aboutsummaryrefslogtreecommitdiffstats
path: root/tests/wpt/web-platform-tests/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tests/wpt/web-platform-tests/tools')
-rw-r--r--tests/wpt/web-platform-tests/tools/ci/ci_wpt.sh2
-rw-r--r--tests/wpt/web-platform-tests/tools/ci/lib.sh4
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/lint.py58
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/a-ref.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/a-ref.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/a-ref.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/a-ref.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/support/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/support/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/support/tools/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/support/tools/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/tools/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/tools/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/a-ref.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/a-ref.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/support/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/support/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/tools/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/tools/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/selectors/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/selectors/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/support/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/support/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/support/tools/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/support/tools/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/tools/a.html (renamed from tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/tools/a.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/work-in-progress/foo/support/a.html1
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/test_file_lints.py55
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/test_lint.py60
-rw-r--r--tests/wpt/web-platform-tests/tools/lint/tests/test_path_lints.py6
-rw-r--r--tests/wpt/web-platform-tests/tools/localpaths.py7
-rw-r--r--tests/wpt/web-platform-tests/tools/manifest/download.py13
-rw-r--r--tests/wpt/web-platform-tests/tools/manifest/sourcefile.py6
-rw-r--r--tests/wpt/web-platform-tests/tools/manifest/tests/test_sourcefile.py3
-rw-r--r--tests/wpt/web-platform-tests/tools/py/.hgtags67
-rw-r--r--tests/wpt/web-platform-tests/tools/py/CHANGELOG1089
-rw-r--r--tests/wpt/web-platform-tests/tools/py/MANIFEST.in9
-rw-r--r--tests/wpt/web-platform-tests/tools/py/README.txt21
-rw-r--r--tests/wpt/web-platform-tests/tools/py/conftest.py71
-rw-r--r--tests/wpt/web-platform-tests/tools/py/doc/index.txt43
-rw-r--r--tests/wpt/web-platform-tests/tools/py/doc/install.txt88
-rw-r--r--tests/wpt/web-platform-tests/tools/py/doc/misc.txt93
-rw-r--r--tests/wpt/web-platform-tests/tools/py/doc/path.txt260
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/__init__.py150
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_apipkg.py181
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_code/_assertionnew.py339
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_code/_assertionold.py555
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_code/assertion.py94
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_code/code.py787
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_code/source.py419
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_error.py88
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_iniconfig.py162
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_io/terminalwriter.py348
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_log/log.py186
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_log/warning.py76
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_path/common.py403
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_path/local.py911
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_path/svnurl.py380
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_path/svnwc.py1240
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_std.py18
-rw-r--r--tests/wpt/web-platform-tests/tools/py/py/_xmlgen.py253
-rw-r--r--tests/wpt/web-platform-tests/tools/py/setup.cfg5
-rw-r--r--tests/wpt/web-platform-tests/tools/py/setup.py38
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/code/test_assertion.py308
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/code/test_excinfo.py909
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/code/test_source.py651
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/io_/test_capture.py501
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/io_/test_saferepr.py78
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/io_/test_terminalwriter.py271
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/log/test_log.py190
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/log/test_warning.py76
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/path/common.py470
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/path/conftest.py80
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/path/test_cacheutil.py84
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/path/test_local.py860
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/path/test_svnauth.py454
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/path/test_svnwc.py549
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/process/test_cmdexec.py39
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/process/test_forkedfunc.py177
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/process/test_killproc.py16
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/root/test_error.py37
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/root/test_py_imports.py68
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/root/test_xmlgen.py145
-rw-r--r--tests/wpt/web-platform-tests/tools/py/testing/test_iniconfig.py299
-rw-r--r--tests/wpt/web-platform-tests/tools/py/tox.ini39
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest.ini2
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/.coveragerc7
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/.github/ISSUE_TEMPLATE.md8
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/.github/PULL_REQUEST_TEMPLATE.md8
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/.gitignore34
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/.travis.yml40
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/AUTHORS87
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/CHANGELOG.rst2586
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/CONTRIBUTING.rst253
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/HOWTORELEASE.rst92
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/ISSUES.txt365
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/LICENSE21
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/MANIFEST.in34
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/README.rst102
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/__init__.py2
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/_argcomplete.py101
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/__init__.py12
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/_py2traceback.py81
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/code.py795
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/source.py421
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/_pluggy.py11
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/__init__.py176
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/reinterpret.py407
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/rewrite.py885
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/util.py332
-rwxr-xr-xtests/wpt/web-platform-tests/tools/pytest/_pytest/cacheprovider.py245
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/capture.py472
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/config.py1192
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/doctest.py290
-rwxr-xr-xtests/wpt/web-platform-tests/tools/pytest/_pytest/genscript.py132
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/helpconfig.py139
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/hookspec.py295
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/impl254
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/junitxml.py387
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/main.py744
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/mark.py311
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/monkeypatch.py254
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/nose.py71
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/pastebin.py92
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/pdb.py109
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/pytester.py1110
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/python.py2302
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/recwarn.py221
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/resultlog.py104
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/runner.py515
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/skipping.py354
-rwxr-xr-xtests/wpt/web-platform-tests/tools/pytest/_pytest/standalonetemplate.py89
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/terminal.py593
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/tmpdir.py123
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/unittest.py205
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/README.md13
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/DESCRIPTION.rst10
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/METADATA39
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/RECORD8
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/metadata.json1
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/pbr.json1
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/top_level.txt1
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy.py777
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/appveyor.yml28
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/Makefile164
-rwxr-xr-xtests/wpt/web-platform-tests/tools/pytest/doc/en/_getdoctarget.py16
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/globaltoc.html18
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/layout.html34
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/links.html16
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/adopt.rst78
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/index.rst48
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.2.rst73
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.3.rst40
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.1.rst41
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.4.rst39
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.0.rst134
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.2.rst57
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.3.rst62
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.5.rst97
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.4.0.rst225
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.5.0.rst175
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.5.2.rst64
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.3.rst52
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.7.0.rst101
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.7.1.rst58
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.9.0.rst159
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.9.1.rst65
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/sprint2016.rst105
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/assert.rst289
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/bash-completion.rst28
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/builtin.rst134
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/cache.rst278
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/capture.rst118
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/conf.py326
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/contact.rst51
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/contents.rst39
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/customize.rst228
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/doctest.rst105
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/failure_demo.py238
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/test_setup_flow_example.py42
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/attic.rst79
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/conftest.py18
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/index.rst34
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/layout1/setup.cfg4
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/markers.rst592
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/multipython.py52
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython.rst91
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython/conftest.py40
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/parametrize.rst475
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/pythoncollection.py11
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/pythoncollection.rst192
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/reportingdemo.rst598
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/simple.rst751
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/example/special.rst72
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/faq.rst165
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/feedback.rst8
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/fixture.rst987
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/funcarg_compare.rst217
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/genapi.py41
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/getting-started.rst237
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/goodpractices.rst278
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/index.rst61
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/license.rst32
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/links.inc21
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/mark.rst40
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/monkeypatch.rst82
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/nose.rst55
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/overview.rst13
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/parametrize.rst219
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/plugins.rst159
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/projects.rst85
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/recwarn.rst130
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/setup.rst10
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/skipping.rst373
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/status.rst5
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/talks.rst116
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/test/attic.rst117
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/test/mission.rst13
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/cov.rst230
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/coverage.rst51
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/figleaf.rst44
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/genscript.rst28
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/helpconfig.rst38
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/links.rst47
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/nose.rst56
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/terminal.rst40
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/xdist.rst172
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/tmpdir.rst111
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/unittest.rst190
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/usage.rst275
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/writing_plugins.rst575
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/xdist.rst197
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/xunit_setup.rst90
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/doc/en/yieldfixture.rst100
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/extra/get_issues.py74
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/plugin-test.sh20
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/pytest.py28
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/requirements-docs.txt3
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/runtox.py8
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/setup.cfg13
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/setup.py122
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/acceptance_test.py695
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/code/test_code.py174
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/code/test_excinfo.py911
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/code/test_source.py659
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/install_cx_freeze.py64
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/runtests_script.py9
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/runtests_setup.py15
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/tests/test_trivial.py6
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/tox_run.py15
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/python/collect.py1200
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/python/fixture.py2693
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/python/integration.py369
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/python/metafunc.py1094
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/python/raises.py78
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_argcomplete.py90
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_assertinterpret.py274
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_assertion.py628
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_assertrewrite.py716
-rwxr-xr-xtests/wpt/web-platform-tests/tools/pytest/testing/test_cache.py386
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_capture.py1068
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_collection.py641
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_config.py570
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_conftest.py409
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_doctest.py715
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_genscript.py51
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_helpconfig.py69
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_junitxml.py816
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_mark.py672
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_monkeypatch.py330
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_nose.py394
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_parseopt.py287
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_pastebin.py115
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_pdb.py313
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_pluginmanager.py340
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_pytester.py122
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_recwarn.py227
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_resultlog.py236
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_runner.py634
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_runner_xunit.py252
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_session.py244
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_skipping.py917
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_terminal.py880
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_tmpdir.py183
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/testing/test_unittest.py737
-rw-r--r--tests/wpt/web-platform-tests/tools/pytest/tox.ini160
-rw-r--r--tests/wpt/web-platform-tests/tools/serve/serve.py87
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/.gitignore9
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/.travis.yml56
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/AUTHORS.rst11
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/CHANGELOG.rst326
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/CODE_OF_CONDUCT.rst55
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/CONTRIBUTING.rst220
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/LICENSE21
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/MANIFEST.in19
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/PULL_REQUEST_TEMPLATE.md12
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/README.rst129
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/.gitignore (renamed from tests/wpt/web-platform-tests/tools/py/testing/log/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/261.change.rst4
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/284.change.rst2
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/285.change.rst2
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/286.change.rst2
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/287.change.rst2
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/291.change.rst1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/292.change.rst1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/295.change.rst4
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/296.change.rst4
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/conftest.py28
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/dev-requirements.txt6
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs-requirements.txt3
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/Makefile177
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/_static/attrs_logo.pngbin0 -> 7639 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/_static/attrs_logo.svg1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/api.rst428
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/backward-compatibility.rst19
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/changelog.rst1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/conf.py155
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/contributing.rst5
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/docutils.conf3
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/examples.rst705
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/extending.rst112
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/how-does-it-work.rst79
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/index.rst80
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/license.rst8
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/overview.rst87
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/docs/why.rst251
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/pyproject.toml26
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/setup.cfg26
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/setup.py95
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/__init__.py55
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_compat.py139
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_config.py23
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_funcs.py212
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_make.py1395
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/converters.py24
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/exceptions.py48
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/filters.py52
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/validators.py166
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/__init__.py (renamed from tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_annotations.py156
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_config.py43
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_converters.py36
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_dark_magic.py382
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_dunders.py502
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_filters.py94
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_funcs.py527
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_init_subclass.py44
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_make.py866
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_slots.py423
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_validators.py266
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tests/utils.py237
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/attrs/tox.ini83
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/.travis.yml17
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/CHANGELOG19
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/LICENSE13
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/MANIFEST.in7
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/Makefile39
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/README.rst83
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/Makefile153
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/_templates/page.html9
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/conf.py251
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/index.rst315
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/__init__.py818
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/odict.py261
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/version.py1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/requirements/development.txt6
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/requirements/production.txt (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/setup.cfg2
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/setup.py55
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/__init__.py0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_formatannotation.py27
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_funcsigs.py93
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_inspect.py1019
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/.gitignore58
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/.travis.yml48
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/CHANGELOG.rst152
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/LICENSE22
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/MANIFEST.in7
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/README.rst80
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/appveyor.yml34
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/_static/img/plug.pngbin0 -> 9350 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/api_reference.rst14
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/conf.py71
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/examples/firstexample.py44
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/index.rst705
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/pluggy/__init__.py684
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/pluggy/callers.py201
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/setup.cfg8
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/setup.py51
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/benchmark.py59
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/conftest.py30
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_details.py103
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_helpers.py68
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_hookrelay.py210
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_method_ordering.py322
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_multicall.py194
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_pluginmanager.py374
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_tracer.py89
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pluggy/tox.ini44
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/.gitattributes1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/.gitignore12
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/.hgignore (renamed from tests/wpt/web-platform-tests/tools/py/.hgignore)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/.hgtags68
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/.travis.yml27
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/AUTHORS (renamed from tests/wpt/web-platform-tests/tools/py/AUTHORS)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/CHANGELOG1149
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/LICENSE (renamed from tests/wpt/web-platform-tests/tools/py/LICENSE)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/MANIFEST.in10
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/README.rst34
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/appveyor.yml26
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/bench/localpath.py (renamed from tests/wpt/web-platform-tests/tools/py/bench/localpath.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/conftest.py60
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/Makefile (renamed from tests/wpt/web-platform-tests/tools/py/doc/Makefile)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/_templates/layout.html (renamed from tests/wpt/web-platform-tests/tools/py/doc/_templates/layout.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-0.9.0.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-0.9.0.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-0.9.2.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-0.9.2.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.0.0.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.0.0.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.0.1.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.0.1.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.0.2.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.0.2.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.1.0.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.1.0.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.1.1.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.1.1.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.2.0.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.2.0.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.2.1.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.2.1.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.0.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.0.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.1.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.1.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.2.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.2.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.3.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.3.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.4.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.4.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.4.0.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.4.0.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.4.1.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.4.1.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/releases.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/announce/releases.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/changelog.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/changelog.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/code.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/code.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/conf.py (renamed from tests/wpt/web-platform-tests/tools/py/doc/conf.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/download.html (renamed from tests/wpt/web-platform-tests/tools/py/doc/download.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/example/genhtml.py (renamed from tests/wpt/web-platform-tests/tools/py/doc/example/genhtml.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/example/genhtmlcss.py (renamed from tests/wpt/web-platform-tests/tools/py/doc/example/genhtmlcss.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/example/genxml.py (renamed from tests/wpt/web-platform-tests/tools/py/doc/example/genxml.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/faq.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/faq.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/img/pylib.png (renamed from tests/wpt/web-platform-tests/tools/py/doc/img/pylib.png)bin8276 -> 8276 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/index.txt39
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/install.txt88
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/io.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/io.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/links.inc (renamed from tests/wpt/web-platform-tests/tools/py/doc/links.inc)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/log.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/log.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/misc.txt93
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/path.txt258
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/style.css (renamed from tests/wpt/web-platform-tests/tools/py/doc/style.css)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/doc/xml.txt (renamed from tests/wpt/web-platform-tests/tools/py/doc/xml.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/__init__.py154
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/__metainfo.py (renamed from tests/wpt/web-platform-tests/tools/py/py/__metainfo.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_builtin.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_builtin.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_code/__init__.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_code/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_code/_assertionnew.py322
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_code/_assertionold.py556
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_code/_py2traceback.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_code/_py2traceback.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_code/assertion.py90
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_code/code.py796
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_code/source.py410
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_error.py91
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_io/__init__.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_io/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_io/capture.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_io/capture.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_io/saferepr.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_io/saferepr.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_io/terminalwriter.py384
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_log/__init__.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_log/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_log/log.py206
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_log/warning.py79
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_path/__init__.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_path/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_path/cacheutil.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_path/cacheutil.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_path/common.py453
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_path/local.py992
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_path/svnurl.py380
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_path/svnwc.py1240
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_process/__init__.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_process/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_process/cmdexec.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_process/cmdexec.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_process/forkedfunc.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_process/forkedfunc.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_process/killproc.py (renamed from tests/wpt/web-platform-tests/tools/py/py/_process/killproc.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_std.py26
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/__init__.py0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/DESCRIPTION.rst87
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/INSTALLER1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/METADATA109
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/RECORD9
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/WHEEL (renamed from tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/WHEEL)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/metadata.json1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/top_level.txt1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg.py205
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/DESCRIPTION.rst53
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/INSTALLER1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/METADATA78
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/RECORD9
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/WHEEL5
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/metadata.json1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/top_level.txt1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig.py165
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/_xmlgen.py255
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/py/test.py (renamed from tests/wpt/web-platform-tests/tools/py/py/test.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/setup.cfg8
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/setup.py53
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/tasks/__init__.py12
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/tasks/vendoring.py23
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_assertion.py305
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_code.py (renamed from tests/wpt/web-platform-tests/tools/py/testing/code/test_code.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_excinfo.py956
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_source.py648
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/conftest.py (renamed from tests/wpt/web-platform-tests/tools/py/testing/conftest.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/__init__.py (renamed from tests/wpt/web-platform-tests/tools/py/testing/io_/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_capture.py501
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_saferepr.py75
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_terminalwriter.py292
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/log/__init__.py0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/log/test_log.py191
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/log/test_warning.py86
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/path/common.py492
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/path/conftest.py80
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/path/repotest.dump (renamed from tests/wpt/web-platform-tests/tools/py/testing/path/repotest.dump)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/path/svntestbase.py (renamed from tests/wpt/web-platform-tests/tools/py/testing/path/svntestbase.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_cacheutil.py89
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_local.py977
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_svnauth.py460
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_svnurl.py (renamed from tests/wpt/web-platform-tests/tools/py/testing/path/test_svnurl.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_svnwc.py557
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/process/__init__.py (renamed from tests/wpt/web-platform-tests/tools/py/testing/process/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_cmdexec.py41
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_forkedfunc.py173
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_killproc.py18
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/root/__init__.py (renamed from tests/wpt/web-platform-tests/tools/py/testing/root/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_builtin.py (renamed from tests/wpt/web-platform-tests/tools/py/testing/root/test_builtin.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_error.py68
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_py_imports.py71
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_std.py (renamed from tests/wpt/web-platform-tests/tools/py/testing/root/test_std.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_xmlgen.py146
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/py/tox.ini33
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/.gitattributes (renamed from tests/wpt/web-platform-tests/tools/pytest/.gitattributes)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/.github/ISSUE_TEMPLATE.md8
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/.github/PULL_REQUEST_TEMPLATE.md15
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/.gitignore39
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/.travis.yml56
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/AUTHORS189
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/CHANGELOG.rst4194
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/CONTRIBUTING.rst278
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/HOWTORELEASE.rst65
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/LICENSE21
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/README.rst109
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/__init__.py8
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_argcomplete.py103
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/__init__.py10
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/_py2traceback.py85
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/code.py906
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/source.py415
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/__init__.py146
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/rewrite.py948
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/truncate.py102
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/util.py308
-rwxr-xr-xtests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/cacheprovider.py260
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/capture.py683
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/compat.py322
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/config.py1400
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/debugging.py123
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/deprecated.py52
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/doctest.py369
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/fixtures.py1147
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/freeze_support.py43
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/helpconfig.py184
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/hookspec.py423
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/junitxml.py453
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/logging.py337
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/main.py821
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/mark.py496
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/monkeypatch.py258
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/nodes.py37
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/nose.py72
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/outcomes.py147
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/pastebin.py100
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/pytester.py1187
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/python.py1175
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/python_api.py619
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/recwarn.py236
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/resultlog.py113
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/runner.py506
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/setuponly.py74
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/setupplan.py25
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/skipping.py397
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/terminal.py703
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/tmpdir.py126
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/unittest.py237
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/warnings.py96
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/appveyor.yml44
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/bench/bench.py (renamed from tests/wpt/web-platform-tests/tools/pytest/bench/bench.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/bench/bench_argcomplete.py (renamed from tests/wpt/web-platform-tests/tools/pytest/bench/bench_argcomplete.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/bench/empty.py (renamed from tests/wpt/web-platform-tests/tools/pytest/bench/empty.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/bench/manyparam.py (renamed from tests/wpt/web-platform-tests/tools/pytest/bench/manyparam.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/bench/skip.py (renamed from tests/wpt/web-platform-tests/tools/pytest/bench/skip.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2920.bugfix1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2949.trivial1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2956.bugfix1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2957.bugfix1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2963.doc1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2971.bugfix1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2984.bugfix1
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/_template.rst40
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/Makefile150
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/globaltoc.html19
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/layout.html20
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/links.html11
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/sidebarintro.html (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/sidebarintro.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/.gitignore (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/.gitignore)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/LICENSE (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/LICENSE)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/README (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/README)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/layout.html (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/layout.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/relations.html (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/relations.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/static/flasky.css_t (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/static/flasky.css_t)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/theme.conf (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/theme.conf)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask_theme_support.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask_theme_support.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/adopt.rst82
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/index.rst68
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.0.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.0.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.1.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.1.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.2.rst73
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.3.rst40
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.0.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.0.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.1.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.1.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.2.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.2.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.3.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.3.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.0.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.0.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.1.rst41
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.2.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.2.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.4.rst39
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.0.rst134
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.1.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.1.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.2.rst57
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.3.rst62
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.4.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.4.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.5.rst97
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.4.0.rst225
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.4.1.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.4.1.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.4.2.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.4.2.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.5.0.rst175
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.5.1.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.5.1.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.5.2.rst64
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.0.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.0.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.1.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.1.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.2.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.2.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.3.rst52
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.7.0.rst101
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.7.1.rst58
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.7.2.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.7.2.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.2.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.2.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.3.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.3.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.4.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.4.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.5.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.5.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.6.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.6.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.7.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.7.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.0.rst159
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.1.rst67
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.2.rst78
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.0.rst82
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.1.rst26
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.2.rst24
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.3.rst27
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.4.rst29
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.5.rst27
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.6.rst33
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.7.rst33
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.0.rst61
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.1.rst23
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.2.rst23
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.3.rst23
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.0.rst48
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.1.rst22
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.2.rst28
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.3.rst23
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.4.rst36
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.5.rst18
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.3.0.rst50
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/sprint2016.rst64
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/assert.rst299
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/backwards-compatibility.rst105
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/bash-completion.rst28
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/builtin.rst161
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/cache.rst268
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/capture.rst148
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/changelog.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/changelog.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/check_sphinx.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/check_sphinx.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/conf.py323
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/conftest.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/conftest.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/contact.rst50
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/contents.rst65
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/contributing.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/contributing.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/customize.rst333
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/development_guide.rst108
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/doctest.rst165
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/failure_demo.py238
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/global_testmodule_config/conftest.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/global_testmodule_config/conftest.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/global_testmodule_config/test_hello.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/global_testmodule_config/test_hello.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/test_failures.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/test_failures.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/test_setup_flow_example.py42
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/attic.rst79
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/conftest.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/conftest.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/conftest.py18
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub1/__init__.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub1/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub1/test_quick.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub1/test_quick.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub2/__init__.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub2/__init__.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub2/test_two.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub2/test_two.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/index.rst34
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/markers.rst642
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/multipython.py52
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython.rst91
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython/__init__.py0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython/conftest.py40
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython/test_simple.yml (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython/test_simple.yml)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/parametrize.rst536
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/py2py3/conftest.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/py2py3/conftest.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/py2py3/test_py2.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/py2py3/test_py2.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/py2py3/test_py3.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/py2py3/test_py3.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/pythoncollection.py11
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/pythoncollection.rst239
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/reportingdemo.rst604
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/simple.rst847
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/special.rst72
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/xfail_demo.py (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/example/xfail_demo.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/existingtestsuite.rst34
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/faq.rst156
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/fixture.rst1088
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/funcarg_compare.rst218
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/funcargs.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/funcargs.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/genapi.py41
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/getting-started.rst198
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/goodpractices.rst299
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/historical-notes.rst177
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/cramer2.png (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/img/cramer2.png)bin25291 -> 25291 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/freiburg2.jpg (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/img/freiburg2.jpg)bin104057 -> 104057 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/gaynor3.png (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/img/gaynor3.png)bin23032 -> 23032 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/keleshev.png (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/img/keleshev.png)bin23246 -> 23246 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pullrequest.png (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pullrequest.png)bin17035 -> 17035 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pylib.png (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pylib.png)bin8276 -> 8276 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pytest1.png (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pytest1.png)bin6010 -> 6010 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pytest1favi.ico (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pytest1favi.ico)bin3742 -> 3742 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/theuni.png (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/img/theuni.png)bin31476 -> 31476 bytes
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/index.rst90
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/license.rst32
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/links.inc21
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/logging.rst192
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/mark.rst41
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/monkeypatch.rst75
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/naming20.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/naming20.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/nose.rst75
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/parametrize.rst217
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/plugins.rst154
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/projects.rst85
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/proposals/parametrize_with_fixtures.rst160
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/pytest.ini (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/pytest.ini)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/pythonpath.rst76
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/recwarn.rst3
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/requirements.txt3
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/skipping.rst378
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/talks.rst105
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/attic.rst117
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/config.html (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/test/config.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/dist.html (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/test/dist.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/extend.html (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/test/extend.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/index.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/test/index.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/mission.rst13
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/cov.rst230
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/coverage.rst51
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/django.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/django.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/figleaf.rst44
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/helpconfig.rst36
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/index.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/index.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/links.rst45
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/nose.rst56
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/oejskit.rst (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/oejskit.rst)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/terminal.rst38
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/xdist.rst172
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/test.html (renamed from tests/wpt/web-platform-tests/tools/pytest/doc/en/test/test.html)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/tmpdir.rst111
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/unittest.rst245
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/usage.rst392
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/warnings.rst299
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/writing_plugins.rst739
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/xunit_setup.rst99
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/yieldfixture.rst18
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/extra/get_issues.py84
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/extra/setup-py.test/setup.py (renamed from tests/wpt/web-platform-tests/tools/pytest/extra/setup-py.test/setup.py)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/pyproject.toml35
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/pytest.py77
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/call-tox.bat8
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/check-rst.py11
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/install-pypy.bat6
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/setup.cfg20
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/setup.py114
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/__init__.py12
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/generate.py162
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/release.minor.rst27
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/release.patch.rst17
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/requirements.txt5
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/acceptance_test.py850
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_code.py196
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_excinfo.py1245
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_source.py680
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_source_multiline_block.py26
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/deprecated_test.py127
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/.gitignore3
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/create_executable.py12
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/runtests_script.py9
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/tests/test_doctest.txt (renamed from tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/tests/test_doctest.txt)0
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/tests/test_trivial.py7
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/tox_run.py12
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/logging/test_fixture.py70
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/logging/test_reporting.py398
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/approx.py392
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/collect.py1402
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/fixture.py3171
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/integration.py384
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/metafunc.py1561
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/raises.py134
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/setup_only.py243
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/setup_plan.py19
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/show_fixtures_per_test.py158
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/test_deprecations.py22
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_argcomplete.py97
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_assertion.py1066
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_assertrewrite.py996
-rwxr-xr-xtests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_cache.py605
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_capture.py1274
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_collection.py857
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_compat.py101
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_config.py862
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_conftest.py478
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_doctest.py1003
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_entry_points.py14
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_helpconfig.py77
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_junitxml.py1062
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_mark.py878
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_modimport.py25
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_monkeypatch.py329
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_nodes.py18
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_nose.py409
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_parseopt.py309
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pastebin.py117
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pdb.py406
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pluginmanager.py361
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pytester.py149
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_recwarn.py310
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_resultlog.py228
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_runner.py813
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_runner_xunit.py319
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_session.py255
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_skipping.py1067
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_terminal.py1039
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_tmpdir.py188
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_unittest.py830
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_warnings.py258
-rw-r--r--tests/wpt/web-platform-tests/tools/third_party/pytest/tox.ini220
-rw-r--r--tests/wpt/web-platform-tests/tools/tox.ini2
-rw-r--r--tests/wpt/web-platform-tests/tools/webdriver/webdriver/client.py15
-rw-r--r--tests/wpt/web-platform-tests/tools/webdriver/webdriver/error.py20
-rw-r--r--tests/wpt/web-platform-tests/tools/webdriver/webdriver/transport.py1
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/OWNERS1
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/docs/conf.py2
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/firefox.py5
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/ie.py2
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/servo.py2
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/environment.py12
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/base.py12
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py54
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py59
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/stability.py42
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testdriver-vendor.js1
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testloader.py6
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testrunner.py29
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/update/sync.py1
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/update/update.py5
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py5
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptlogging.py17
-rw-r--r--tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptrunner.py10
-rw-r--r--tests/wpt/web-platform-tests/tools/wptserve/docs/pipes.rst4
-rw-r--r--tests/wpt/web-platform-tests/tools/wptserve/tests/functional/test_pipes.py6
-rw-r--r--tests/wpt/web-platform-tests/tools/wptserve/wptserve/pipes.py7
-rw-r--r--tests/wpt/web-platform-tests/tools/wptserve/wptserve/server.py3
872 files changed, 96380 insertions, 67520 deletions
diff --git a/tests/wpt/web-platform-tests/tools/ci/ci_wpt.sh b/tests/wpt/web-platform-tests/tools/ci/ci_wpt.sh
index 2b634137605..2e572b2a65b 100644
--- a/tests/wpt/web-platform-tests/tools/ci/ci_wpt.sh
+++ b/tests/wpt/web-platform-tests/tools/ci/ci_wpt.sh
@@ -10,7 +10,7 @@ source tools/ci/lib.sh
main() {
git fetch --unshallow https://github.com/w3c/web-platform-tests.git +refs/heads/*:refs/remotes/origin/*
hosts_fixup
- install_chrome unstable
+ install_chrome dev
pip install -U tox codecov
cd tools/wpt
tox
diff --git a/tests/wpt/web-platform-tests/tools/ci/lib.sh b/tests/wpt/web-platform-tests/tools/ci/lib.sh
index e80499a7d0a..db49ec6b449 100644
--- a/tests/wpt/web-platform-tests/tools/ci/lib.sh
+++ b/tests/wpt/web-platform-tests/tools/ci/lib.sh
@@ -23,6 +23,10 @@ hosts_fixup() {
install_chrome() {
channel=$1
+ # The package name for Google Chrome Dev uses "unstable", not "dev".
+ if [[ $channel == "dev" ]]; then
+ channel="unstable"
+ fi
deb_archive=google-chrome-${channel}_current_amd64.deb
wget https://dl.google.com/linux/direct/$deb_archive
diff --git a/tests/wpt/web-platform-tests/tools/lint/lint.py b/tests/wpt/web-platform-tests/tools/lint/lint.py
index 271b9f1441b..3228524353b 100644
--- a/tests/wpt/web-platform-tests/tools/lint/lint.py
+++ b/tests/wpt/web-platform-tests/tools/lint/lint.py
@@ -56,7 +56,7 @@ For example, to make the lint tool ignore all '%s'
errors in the %s file,
you could add the following line to the lint.whitelist file.
-%s:%s"""
+%s: %s"""
def all_filesystem_paths(repo_root):
path_filter = PathFilter(repo_root, extras=[".git/*"])
@@ -108,13 +108,13 @@ def _all_files_equal(paths):
return True
-def check_path_length(repo_root, path, css_mode):
+def check_path_length(repo_root, path):
if len(path) + 1 > 150:
return [("PATH LENGTH", "/%s longer than maximum path length (%d > 150)" % (path, len(path) + 1), path, None)]
return []
-def check_worker_collision(repo_root, path, css_mode):
+def check_worker_collision(repo_root, path):
endings = [(".any.html", ".any.js"),
(".any.worker.html", ".any.js"),
(".worker.html", ".worker.js")]
@@ -127,7 +127,7 @@ def check_worker_collision(repo_root, path, css_mode):
return []
-def check_ahem_copy(repo_root, path, css_mode):
+def check_ahem_copy(repo_root, path):
lpath = path.lower()
if "ahem" in lpath and lpath.endswith(".ttf"):
return [("AHEM COPY", "Don't add extra copies of Ahem, use /fonts/Ahem.ttf", path, None)]
@@ -139,7 +139,7 @@ w3c_tr_re = re.compile(r"https?\:\/\/www\.w3c?\.org\/TR\/([^/?#]+)")
w3c_dev_re = re.compile(r"https?\:\/\/dev\.w3c?\.org\/[^/?#]+\/([^/?#]+)")
-def check_css_globally_unique(repo_root, paths, css_mode):
+def check_css_globally_unique(repo_root, paths):
"""
Checks that CSS filenames are sufficiently unique
@@ -154,7 +154,6 @@ def check_css_globally_unique(repo_root, paths, css_mode):
:param repo_root: the repository root
:param paths: list of all paths
- :param css_mode: whether we're in CSS testsuite mode
:returns: a list of errors found in ``paths``
"""
@@ -166,11 +165,9 @@ def check_css_globally_unique(repo_root, paths, css_mode):
if os.name == "nt":
path = path.replace("\\", "/")
- if not css_mode:
- if not path.startswith("css/"):
- continue
+ if not path.startswith("css/"):
+ continue
- # we're within css or in css_mode after all that
source_file = SourceFile(repo_root, path, "/")
if source_file.name_is_non_test:
# If we're name_is_non_test for a reason apart from support, ignore it.
@@ -332,6 +329,11 @@ class W3CTestOrgRegexp(Regexp):
error = "W3C-TEST.ORG"
description = "External w3c-test.org domain used"
+class WebPlatformTestRegexp(Regexp):
+ pattern = b"web\-platform\.test"
+ error = "WEB-PLATFORM.TEST"
+ description = "Internal web-platform.test domain used"
+
class Webidl2Regexp(Regexp):
pattern = b"webidl2\.js"
error = "WEBIDL2.JS"
@@ -374,6 +376,7 @@ regexps = [item() for item in
CRRegexp,
SetTimeoutRegexp,
W3CTestOrgRegexp,
+ WebPlatformTestRegexp,
Webidl2Regexp,
ConsoleRegexp,
GenerateTestsRegexp,
@@ -381,7 +384,7 @@ regexps = [item() for item in
LayoutTestsRegexp,
SpecialPowersRegexp]]
-def check_regexp_line(repo_root, path, f, css_mode):
+def check_regexp_line(repo_root, path, f):
errors = []
applicable_regexps = [regexp for regexp in regexps if regexp.applies(path)]
@@ -393,12 +396,12 @@ def check_regexp_line(repo_root, path, f, css_mode):
return errors
-def check_parsed(repo_root, path, f, css_mode):
+def check_parsed(repo_root, path, f):
source_file = SourceFile(repo_root, path, "/", contents=f.read())
errors = []
- if css_mode or path.startswith("css/"):
+ if path.startswith("css/"):
if (source_file.type == "support" and
not source_file.name_is_non_test and
not source_file.name_is_reference):
@@ -562,7 +565,7 @@ class OpenModeCheck(ASTCheck):
ast_checkers = [item() for item in [OpenModeCheck]]
-def check_python_ast(repo_root, path, f, css_mode):
+def check_python_ast(repo_root, path, f):
if not path.endswith(".py"):
return []
@@ -582,7 +585,7 @@ broken_js_metadata = re.compile(b"//\s*META:")
broken_python_metadata = re.compile(b"#\s*META:")
-def check_script_metadata(repo_root, path, f, css_mode):
+def check_script_metadata(repo_root, path, f):
if path.endswith((".worker.js", ".any.js")):
meta_re = js_meta_re
broken_metadata = broken_js_metadata
@@ -621,52 +624,49 @@ def check_script_metadata(repo_root, path, f, css_mode):
return errors
-def check_path(repo_root, path, css_mode):
+def check_path(repo_root, path):
"""
Runs lints that check the file path.
:param repo_root: the repository root
:param path: the path of the file within the repository
- :param css_mode: whether we're in CSS testsuite mode
:returns: a list of errors found in ``path``
"""
errors = []
for path_fn in path_lints:
- errors.extend(path_fn(repo_root, path, css_mode))
+ errors.extend(path_fn(repo_root, path))
return errors
-def check_all_paths(repo_root, paths, css_mode):
+def check_all_paths(repo_root, paths):
"""
Runs lints that check all paths globally.
:param repo_root: the repository root
:param paths: a list of all the paths within the repository
- :param css_mode: whether we're in CSS testsuite mode
:returns: a list of errors found in ``f``
"""
errors = []
for paths_fn in all_paths_lints:
- errors.extend(paths_fn(repo_root, paths, css_mode))
+ errors.extend(paths_fn(repo_root, paths))
return errors
-def check_file_contents(repo_root, path, f, css_mode):
+def check_file_contents(repo_root, path, f):
"""
Runs lints that check the file contents.
:param repo_root: the repository root
:param path: the path of the file within the repository
:param f: a file-like object with the file contents
- :param css_mode: whether we're in CSS testsuite mode
:returns: a list of errors found in ``f``
"""
errors = []
for file_fn in file_lints:
- errors.extend(file_fn(repo_root, path, f, css_mode))
+ errors.extend(file_fn(repo_root, path, f))
f.seek(0)
return errors
@@ -773,10 +773,10 @@ def main(**kwargs):
paths = lint_paths(kwargs, repo_root)
- return lint(repo_root, paths, output_format, kwargs.get("css_mode", False))
+ return lint(repo_root, paths, output_format)
-def lint(repo_root, paths, output_format, css_mode):
+def lint(repo_root, paths, output_format):
error_count = defaultdict(int)
last = None
@@ -817,15 +817,15 @@ def lint(repo_root, paths, output_format, css_mode):
paths.remove(path)
continue
- errors = check_path(repo_root, path, css_mode)
+ errors = check_path(repo_root, path)
last = process_errors(errors) or last
if not os.path.isdir(abs_path):
with open(abs_path, 'rb') as f:
- errors = check_file_contents(repo_root, path, f, css_mode)
+ errors = check_file_contents(repo_root, path, f)
last = process_errors(errors) or last
- errors = check_all_paths(repo_root, paths, css_mode)
+ errors = check_all_paths(repo_root, paths)
last = process_errors(errors) or last
if output_format in ("normal", "markdown"):
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/a-ref.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/a-ref.html
index d00491fd7e5..d00491fd7e5 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/a-ref.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/a-ref.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/a.html
index 73c5d0bc373..73c5d0bc373 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/a-ref.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/a-ref.html
index d00491fd7e5..d00491fd7e5 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/a-ref.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/a-ref.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/a.html
index 73c5d0bc373..73c5d0bc373 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/support/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/support/a.html
index d00491fd7e5..d00491fd7e5 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/support/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/support/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/support/tools/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/support/tools/a.html
index 0cfbf08886f..0cfbf08886f 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/support/tools/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/support/tools/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/tools/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/tools/a.html
index d00491fd7e5..d00491fd7e5 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/match/tools/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/match/tools/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/a-ref.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/a-ref.html
index 0cfbf08886f..0cfbf08886f 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/a-ref.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/a-ref.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/a.html
index 4b0ce383a25..4b0ce383a25 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/support/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/support/a.html
index 0cfbf08886f..0cfbf08886f 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/support/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/support/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/tools/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/tools/a.html
index 0cfbf08886f..0cfbf08886f 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/not-match/tools/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/not-match/tools/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/selectors/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/selectors/a.html
index 0d63c6bfedd..0d63c6bfedd 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/selectors/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/selectors/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/support/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/support/a.html
index d00491fd7e5..d00491fd7e5 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/support/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/support/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/support/tools/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/support/tools/a.html
index 0cfbf08886f..0cfbf08886f 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/support/tools/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/support/tools/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/tools/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/tools/a.html
index d00491fd7e5..d00491fd7e5 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css-unique/tools/a.html
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/css-unique/tools/a.html
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/work-in-progress/foo/support/a.html b/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/work-in-progress/foo/support/a.html
deleted file mode 100644
index d00491fd7e5..00000000000
--- a/tests/wpt/web-platform-tests/tools/lint/tests/dummy/css/work-in-progress/foo/support/a.html
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/test_file_lints.py b/tests/wpt/web-platform-tests/tools/lint/tests/test_file_lints.py
index 1406de2e980..f4fb221b7a5 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/test_file_lints.py
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/test_file_lints.py
@@ -26,7 +26,7 @@ INTERESTING_FILE_NAMES = {
def check_with_files(input_bytes):
return {
- filename: (check_file_contents("", filename, six.BytesIO(input_bytes), False), kind)
+ filename: (check_file_contents("", filename, six.BytesIO(input_bytes)), kind)
for (filename, kind) in
(
(os.path.join("html", filename), kind)
@@ -100,6 +100,19 @@ def test_w3c_test_org():
expected.append(("PARSE-FAILED", "Unable to parse file", filename, None))
assert errors == expected
+def test_web_platform_test():
+ error_map = check_with_files(b"import('http://web-platform.test/')")
+
+ for (filename, (errors, kind)) in error_map.items():
+ check_errors(errors)
+
+ expected = [("WEB-PLATFORM.TEST", "Internal web-platform.test domain used", filename, 1)]
+ if kind == "python":
+ expected.append(("PARSE-FAILED", "Unable to parse file", filename, 1))
+ elif kind == "web-strict":
+ expected.append(("PARSE-FAILED", "Unable to parse file", filename, None))
+ assert errors == expected
+
def test_webidl2_js():
error_map = check_with_files(b"<script src=/resources/webidl2.js>")
@@ -440,7 +453,7 @@ def fifth():
def test_open_mode():
for method in ["open", "file"]:
code = open_mode_code.format(method).encode("utf-8")
- errors = check_file_contents("", "test.py", six.BytesIO(code), False)
+ errors = check_file_contents("", "test.py", six.BytesIO(code))
check_errors(errors)
message = ("File opened without providing an explicit mode (note: " +
@@ -453,15 +466,13 @@ def test_open_mode():
@pytest.mark.parametrize(
- "filename,css_mode,expect_error",
+ "filename,expect_error",
[
- ("foo/bar.html", False, False),
- ("foo/bar.html", True, True),
- ("css/bar.html", False, True),
- ("css/bar.html", True, True),
+ ("foo/bar.html", False),
+ ("css/bar.html", True),
])
-def test_css_support_file(filename, css_mode, expect_error):
- errors = check_file_contents("", filename, six.BytesIO(b""), css_mode)
+def test_css_support_file(filename, expect_error):
+ errors = check_file_contents("", filename, six.BytesIO(b""))
check_errors(errors)
if expect_error:
@@ -475,24 +486,6 @@ def test_css_support_file(filename, css_mode, expect_error):
assert errors == []
-def test_css_missing_file_css_mode():
- code = b"""\
-<html xmlns="http://www.w3.org/1999/xhtml">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-</html>
-"""
- errors = check_file_contents("", "foo/bar.html", six.BytesIO(code), True)
- check_errors(errors)
-
- assert errors == [
- ('MISSING-LINK',
- 'Testcase file must have a link to a spec',
- "foo/bar.html",
- None),
- ]
-
-
def test_css_missing_file_in_css():
code = b"""\
<html xmlns="http://www.w3.org/1999/xhtml">
@@ -500,7 +493,7 @@ def test_css_missing_file_in_css():
<script src="/resources/testharnessreport.js"></script>
</html>
"""
- errors = check_file_contents("", "css/foo/bar.html", six.BytesIO(code), False)
+ errors = check_file_contents("", "css/foo/bar.html", six.BytesIO(code))
check_errors(errors)
assert errors == [
@@ -512,7 +505,7 @@ def test_css_missing_file_in_css():
def test_css_missing_file_manual():
- errors = check_file_contents("", "css/foo/bar-manual.html", six.BytesIO(b""), False)
+ errors = check_file_contents("", "css/foo/bar-manual.html", six.BytesIO(b""))
check_errors(errors)
assert errors == [
@@ -544,7 +537,7 @@ def test_css_missing_file_manual():
(b"""// META: timeout=bar\n""", (1, "UNKNOWN-TIMEOUT-METADATA")),
])
def test_script_metadata(filename, input, error):
- errors = check_file_contents("", filename, six.BytesIO(input), False)
+ errors = check_file_contents("", filename, six.BytesIO(input))
check_errors(errors)
if error is not None:
@@ -583,7 +576,7 @@ def test_script_metadata(filename, input, error):
])
def test_python_metadata(input, error):
filename = "test.py"
- errors = check_file_contents("", filename, six.BytesIO(input), False)
+ errors = check_file_contents("", filename, six.BytesIO(input))
check_errors(errors)
if error is not None:
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/test_lint.py b/tests/wpt/web-platform-tests/tools/lint/tests/test_lint.py
index 82f432482c9..e3e530f172e 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/test_lint.py
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/test_lint.py
@@ -108,7 +108,7 @@ CR AT EOL, INDENT TABS: html/test2.js: 42
def test_lint_no_files(caplog):
- rv = lint(_dummy_repo, [], "normal", False)
+ rv = lint(_dummy_repo, [], "normal")
assert rv == 0
assert caplog.text == ""
@@ -116,7 +116,7 @@ def test_lint_no_files(caplog):
def test_lint_ignored_file(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["broken_ignored.html"], "normal", False)
+ rv = lint(_dummy_repo, ["broken_ignored.html"], "normal")
assert rv == 0
assert not mocked_check_path.called
assert not mocked_check_file_contents.called
@@ -128,7 +128,7 @@ def test_lint_not_existing_file(caplog):
with _mock_lint("check_file_contents") as mocked_check_file_contents:
# really long path-linted filename
name = "a" * 256 + ".html"
- rv = lint(_dummy_repo, [name], "normal", False)
+ rv = lint(_dummy_repo, [name], "normal")
assert rv == 0
assert not mocked_check_path.called
assert not mocked_check_file_contents.called
@@ -138,7 +138,7 @@ def test_lint_not_existing_file(caplog):
def test_lint_passing(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["okay.html"], "normal", False)
+ rv = lint(_dummy_repo, ["okay.html"], "normal")
assert rv == 0
assert mocked_check_path.call_count == 1
assert mocked_check_file_contents.call_count == 1
@@ -148,7 +148,7 @@ def test_lint_passing(caplog):
def test_lint_failing(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["broken.html"], "normal", False)
+ rv = lint(_dummy_repo, ["broken.html"], "normal")
assert rv == 1
assert mocked_check_path.call_count == 1
assert mocked_check_file_contents.call_count == 1
@@ -159,7 +159,7 @@ def test_lint_failing(caplog):
def test_ref_existent_relative(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["ref/existent_relative.html"], "normal", False)
+ rv = lint(_dummy_repo, ["ref/existent_relative.html"], "normal")
assert rv == 0
assert mocked_check_path.call_count == 1
assert mocked_check_file_contents.call_count == 1
@@ -169,7 +169,7 @@ def test_ref_existent_relative(caplog):
def test_ref_existent_root_relative(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["ref/existent_root_relative.html"], "normal", False)
+ rv = lint(_dummy_repo, ["ref/existent_root_relative.html"], "normal")
assert rv == 0
assert mocked_check_path.call_count == 1
assert mocked_check_file_contents.call_count == 1
@@ -179,7 +179,7 @@ def test_ref_existent_root_relative(caplog):
def test_ref_non_existent_relative(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["ref/non_existent_relative.html"], "normal", False)
+ rv = lint(_dummy_repo, ["ref/non_existent_relative.html"], "normal")
assert rv == 1
assert mocked_check_path.call_count == 1
assert mocked_check_file_contents.call_count == 1
@@ -191,7 +191,7 @@ def test_ref_non_existent_relative(caplog):
def test_ref_non_existent_root_relative(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["ref/non_existent_root_relative.html"], "normal", False)
+ rv = lint(_dummy_repo, ["ref/non_existent_root_relative.html"], "normal")
assert rv == 1
assert mocked_check_path.call_count == 1
assert mocked_check_file_contents.call_count == 1
@@ -204,7 +204,7 @@ def test_ref_non_existent_root_relative(caplog):
def test_ref_absolute_url(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["ref/absolute.html"], "normal", False)
+ rv = lint(_dummy_repo, ["ref/absolute.html"], "normal")
assert rv == 1
assert mocked_check_path.call_count == 1
assert mocked_check_file_contents.call_count == 1
@@ -216,7 +216,7 @@ def test_ref_absolute_url(caplog):
def test_ref_same_file_empty(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["ref/same_file_empty.html"], "normal", False)
+ rv = lint(_dummy_repo, ["ref/same_file_empty.html"], "normal")
assert rv == 1
assert mocked_check_path.call_count == 1
assert mocked_check_file_contents.call_count == 1
@@ -227,7 +227,7 @@ def test_ref_same_file_empty(caplog):
def test_ref_same_file_path(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["ref/same_file_path.html"], "normal", False)
+ rv = lint(_dummy_repo, ["ref/same_file_path.html"], "normal")
assert rv == 1
assert mocked_check_path.call_count == 1
assert mocked_check_file_contents.call_count == 1
@@ -238,7 +238,7 @@ def test_ref_same_file_path(caplog):
def test_lint_passing_and_failing(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["broken.html", "okay.html"], "normal", False)
+ rv = lint(_dummy_repo, ["broken.html", "okay.html"], "normal")
assert rv == 1
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -250,7 +250,7 @@ def test_lint_passing_and_failing(caplog):
def test_check_css_globally_unique_identical_test(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/match/a.html", "css-unique/a.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/match/a.html", "css/css-unique/a.html"], "normal")
assert rv == 0
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -260,7 +260,7 @@ def test_check_css_globally_unique_identical_test(caplog):
def test_check_css_globally_unique_different_test(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/not-match/a.html", "css-unique/a.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/not-match/a.html", "css/css-unique/a.html"], "normal")
assert rv == 2
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -270,7 +270,7 @@ def test_check_css_globally_unique_different_test(caplog):
def test_check_css_globally_unique_different_spec_test(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/selectors/a.html", "css-unique/a.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/selectors/a.html", "css/css-unique/a.html"], "normal")
assert rv == 0
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -280,7 +280,7 @@ def test_check_css_globally_unique_different_spec_test(caplog):
def test_check_css_globally_unique_support_ignored(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/support/a.html", "css-unique/support/tools/a.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/support/a.html", "css/css-unique/support/tools/a.html"], "normal")
assert rv == 0
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -290,7 +290,7 @@ def test_check_css_globally_unique_support_ignored(caplog):
def test_check_css_globally_unique_support_identical(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/support/a.html", "css-unique/match/support/a.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/support/a.html", "css/css-unique/match/support/a.html"], "normal")
assert rv == 0
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -300,7 +300,7 @@ def test_check_css_globally_unique_support_identical(caplog):
def test_check_css_globally_unique_support_different(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/not-match/support/a.html", "css-unique/support/a.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/not-match/support/a.html", "css/css-unique/support/a.html"], "normal")
assert rv == 2
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -310,7 +310,7 @@ def test_check_css_globally_unique_support_different(caplog):
def test_check_css_globally_unique_test_support(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/support/a.html", "css-unique/a.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/support/a.html", "css/css-unique/a.html"], "normal")
assert rv == 0
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -320,7 +320,7 @@ def test_check_css_globally_unique_test_support(caplog):
def test_check_css_globally_unique_ref_identical(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/a-ref.html", "css-unique/match/a-ref.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/a-ref.html", "css/css-unique/match/a-ref.html"], "normal")
assert rv == 0
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -330,7 +330,7 @@ def test_check_css_globally_unique_ref_identical(caplog):
def test_check_css_globally_unique_ref_different(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/not-match/a-ref.html", "css-unique/a-ref.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/not-match/a-ref.html", "css/css-unique/a-ref.html"], "normal")
assert rv == 2
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -340,7 +340,7 @@ def test_check_css_globally_unique_ref_different(caplog):
def test_check_css_globally_unique_test_ref(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/a-ref.html", "css-unique/a.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/a-ref.html", "css/css-unique/a.html"], "normal")
assert rv == 0
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -350,7 +350,7 @@ def test_check_css_globally_unique_test_ref(caplog):
def test_check_css_globally_unique_ignored(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/tools/a.html", "css-unique/not-match/tools/a.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/tools/a.html", "css/css-unique/not-match/tools/a.html"], "normal")
assert rv == 0
assert mocked_check_path.call_count == 2
assert mocked_check_file_contents.call_count == 2
@@ -360,10 +360,10 @@ def test_check_css_globally_unique_ignored(caplog):
def test_check_css_globally_unique_ignored_dir(caplog):
with _mock_lint("check_path") as mocked_check_path:
with _mock_lint("check_file_contents") as mocked_check_file_contents:
- rv = lint(_dummy_repo, ["css-unique/support/a.html", "css/work-in-progress/foo/support/a.html"], "normal", True)
+ rv = lint(_dummy_repo, ["css/css-unique/support/a.html"], "normal")
assert rv == 0
- assert mocked_check_path.call_count == 2
- assert mocked_check_file_contents.call_count == 2
+ assert mocked_check_path.call_count == 1
+ assert mocked_check_file_contents.call_count == 1
assert caplog.text == ""
@@ -390,7 +390,7 @@ def test_main_with_args():
sys.argv = ['./lint', 'a', 'b', 'c']
with _mock_lint('lint', return_value=True) as m:
lint_mod.main(**vars(create_parser().parse_args()))
- m.assert_called_once_with(repo_root, ['a', 'b', 'c'], "normal", False)
+ m.assert_called_once_with(repo_root, ['a', 'b', 'c'], "normal")
finally:
sys.argv = orig_argv
@@ -402,7 +402,7 @@ def test_main_no_args():
with _mock_lint('lint', return_value=True) as m:
with _mock_lint('changed_files', return_value=['foo', 'bar']) as m2:
lint_mod.main(**vars(create_parser().parse_args()))
- m.assert_called_once_with(repo_root, ['foo', 'bar'], "normal", False)
+ m.assert_called_once_with(repo_root, ['foo', 'bar'], "normal")
finally:
sys.argv = orig_argv
@@ -414,6 +414,6 @@ def test_main_all():
with _mock_lint('lint', return_value=True) as m:
with _mock_lint('all_filesystem_paths', return_value=['foo', 'bar']) as m2:
lint_mod.main(**vars(create_parser().parse_args()))
- m.assert_called_once_with(repo_root, ['foo', 'bar'], "normal", False)
+ m.assert_called_once_with(repo_root, ['foo', 'bar'], "normal")
finally:
sys.argv = orig_argv
diff --git a/tests/wpt/web-platform-tests/tools/lint/tests/test_path_lints.py b/tests/wpt/web-platform-tests/tools/lint/tests/test_path_lints.py
index 7af47062b66..b5818289eac 100644
--- a/tests/wpt/web-platform-tests/tools/lint/tests/test_path_lints.py
+++ b/tests/wpt/web-platform-tests/tools/lint/tests/test_path_lints.py
@@ -11,7 +11,7 @@ def test_allowed_path_length():
for idx in range(5):
filename = basename + idx * "a"
- errors = check_path("/foo/", filename, False)
+ errors = check_path("/foo/", filename)
check_errors(errors)
assert errors == []
@@ -23,7 +23,7 @@ def test_forbidden_path_length():
filename = basename + idx * "a"
message = "/%s longer than maximum path length (%s > 150)" % (filename, 146 + idx)
- errors = check_path("/foo/", filename, False)
+ errors = check_path("/foo/", filename)
check_errors(errors)
assert errors == [("PATH LENGTH", message, filename, None)]
@@ -36,6 +36,6 @@ def test_forbidden_path_endings(path_ending, generated):
message = ("path ends with %s which collides with generated tests from %s files" %
(path_ending, generated))
- errors = check_path("/foo/", path, False)
+ errors = check_path("/foo/", path)
check_errors(errors)
assert errors == [("WORKER COLLISION", message, path, None)]
diff --git a/tests/wpt/web-platform-tests/tools/localpaths.py b/tests/wpt/web-platform-tests/tools/localpaths.py
index 9d59cf815c6..c522efd4326 100644
--- a/tests/wpt/web-platform-tests/tools/localpaths.py
+++ b/tests/wpt/web-platform-tests/tools/localpaths.py
@@ -9,7 +9,10 @@ sys.path.insert(0, os.path.join(here, "six"))
sys.path.insert(0, os.path.join(here, "html5lib"))
sys.path.insert(0, os.path.join(here, "wptserve"))
sys.path.insert(0, os.path.join(here, "pywebsocket", "src"))
-sys.path.insert(0, os.path.join(here, "py"))
-sys.path.insert(0, os.path.join(here, "pytest"))
+sys.path.insert(0, os.path.join(here, "third_party", "attrs", "src"))
+sys.path.insert(0, os.path.join(here, "third_party", "funcsigs"))
+sys.path.insert(0, os.path.join(here, "third_party", "pluggy"))
+sys.path.insert(0, os.path.join(here, "third_party", "py"))
+sys.path.insert(0, os.path.join(here, "third_party", "pytest"))
sys.path.insert(0, os.path.join(here, "webdriver"))
sys.path.insert(0, os.path.join(here, "wptrunner"))
diff --git a/tests/wpt/web-platform-tests/tools/manifest/download.py b/tests/wpt/web-platform-tests/tools/manifest/download.py
index 42998a2a2db..611ada0ecd3 100644
--- a/tests/wpt/web-platform-tests/tools/manifest/download.py
+++ b/tests/wpt/web-platform-tests/tools/manifest/download.py
@@ -1,14 +1,17 @@
+from __future__ import absolute_import
+
import argparse
import gzip
import json
import io
-import log
import os
from datetime import datetime, timedelta
-import urllib2
+from six.moves.urllib.request import urlopen
+
+from .vcs import Git
-from vcs import Git
+from . import log
here = os.path.dirname(__file__)
@@ -37,7 +40,7 @@ def git_commits(repo_root):
def github_url(commits):
try:
- resp = urllib2.urlopen("https://api.github.com/repos/w3c/web-platform-tests/releases")
+ resp = urlopen("https://api.github.com/repos/w3c/web-platform-tests/releases")
except Exception:
return None
@@ -76,7 +79,7 @@ def download_manifest(manifest_path, commits_func, url_func, force=False):
logger.info("Downloading manifest from %s" % url)
try:
- resp = urllib2.urlopen(url)
+ resp = urlopen(url)
except Exception:
logger.warning("Downloading pregenerated manifest failed")
return False
diff --git a/tests/wpt/web-platform-tests/tools/manifest/sourcefile.py b/tests/wpt/web-platform-tests/tools/manifest/sourcefile.py
index 1d0637bdee1..093fc2bd470 100644
--- a/tests/wpt/web-platform-tests/tools/manifest/sourcefile.py
+++ b/tests/wpt/web-platform-tests/tools/manifest/sourcefile.py
@@ -54,8 +54,7 @@ class SourceFile(object):
"xhtml":lambda x:ElementTree.parse(x, XMLParser.XMLParser()),
"svg":lambda x:ElementTree.parse(x, XMLParser.XMLParser())}
- root_dir_non_test = set(["common",
- "work-in-progress"])
+ root_dir_non_test = set(["common"])
dir_non_test = set(["resources",
"support",
@@ -63,8 +62,7 @@ class SourceFile(object):
dir_path_non_test = {("css21", "archive"),
("css", "CSS2", "archive"),
- ("css", "common"),
- ("css", "work-in-progress")}
+ ("css", "common")}
def __init__(self, tests_root, rel_path, url_base, contents=None):
"""Object representing a file in a source tree.
diff --git a/tests/wpt/web-platform-tests/tools/manifest/tests/test_sourcefile.py b/tests/wpt/web-platform-tests/tools/manifest/tests/test_sourcefile.py
index 9c3866715dc..57863bf73c4 100644
--- a/tests/wpt/web-platform-tests/tools/manifest/tests/test_sourcefile.py
+++ b/tests/wpt/web-platform-tests/tools/manifest/tests/test_sourcefile.py
@@ -27,7 +27,6 @@ def items(s):
"common/test.html",
"support/test.html",
"css21/archive/test.html",
- "work-in-progress/test.html",
"conformance-checkers/test.html",
"conformance-checkers/README.md",
"conformance-checkers/html/Makefile",
@@ -38,7 +37,6 @@ def items(s):
"foo/test-support.html",
"css/common/test.html",
"css/CSS2/archive/test.html",
- "css/work-in-progress/test.html",
])
def test_name_is_non_test(rel_path):
s = create(rel_path)
@@ -54,7 +52,6 @@ def test_name_is_non_test(rel_path):
"foo/conformance-checkers/test.html",
"foo/_certs/test.html",
"foo/css21/archive/test.html",
- "foo/work-in-progress/test.html",
"foo/CSS2/archive/test.html",
"css/css21/archive/test.html",
])
diff --git a/tests/wpt/web-platform-tests/tools/py/.hgtags b/tests/wpt/web-platform-tests/tools/py/.hgtags
deleted file mode 100644
index d45ebb5a237..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/.hgtags
+++ /dev/null
@@ -1,67 +0,0 @@
-52c6d9e78777a5a34e813123997dfc614a1a4767 1.0.0b3
-1c7aaa8c61f3b0945921a9acc7beb184201aed4b 1.0.0b4
-1c7aaa8c61f3b0945921a9acc7beb184201aed4b 1.0.0b4
-0000000000000000000000000000000000000000 1.0.0b4
-0000000000000000000000000000000000000000 1.0.0b4
-8cd6eb91eba313b012d6e568f37d844dc0751f2e 1.0.0b4
-8cd6eb91eba313b012d6e568f37d844dc0751f2e 1.0.0b4
-0000000000000000000000000000000000000000 1.0.0b4
-2cc0507f117ffe721dff7ee026648cfce00ec92f 1.0.0b6
-86f1e1b6e49bf5882a809f11edd1dbb08162cdad 1.0.0b8
-86f1e1b6e49bf5882a809f11edd1dbb08162cdad 1.0.0b8
-c63f35c266cbb26dad6b87b5e115d65685adf448 1.0.0b8
-c63f35c266cbb26dad6b87b5e115d65685adf448 1.0.0b8
-0eaa0fdf2ba0163cf534dc2eff4ba2e5fc66c261 1.0.0b8
-e2a60653cb490aeed81bbbd83c070b99401c211c 1.0.0b9
-5ea0cdf7854c3d4278d36eda94a2b68483a0e211 1.0.0
-5ea0cdf7854c3d4278d36eda94a2b68483a0e211 1.0.0
-7acde360d94b6a2690ce3d03ff39301da84c0a2b 1.0.0
-6bd221981ac99103002c1cb94fede400d23a96a1 1.0.1
-4816e8b80602a3fd3a0a120333ad85fbe7d8bab4 1.0.2
-60c44bdbf093285dc69d5462d4dbb4acad325ca6 1.1.0
-319187fcda66714c5eb1353492babeec3d3c826f 1.1.1
-4fc5212f7626a56b9eb6437b5c673f56dd7eb942 1.2.0
-c143a8c8840a1c68570890c8ac6165bbf92fd3c6 1.2.1
-eafd3c256e8732dfb0a4d49d051b5b4339858926 1.3.0
-d5eacf390af74553227122b85e20345d47b2f9e6 1.3.1
-d5eacf390af74553227122b85e20345d47b2f9e6 1.3.1
-8b8e7c25a13cf863f01b2dd955978285ae9daf6a 1.3.1
-3bff44b188a7ec1af328d977b9d39b6757bb38df 1.3.2
-c59d3fa8681a5b5966b8375b16fccd64a3a8dbeb 1.3.3
-79ef6377705184c55633d456832eea318fedcf61 1.3.4
-79ef6377705184c55633d456832eea318fedcf61 1.3.4
-90fffd35373e9f125af233f78b19416f0938d841 1.3.4
-5346ab41b059c95a48cbe1e8a7bae96ce6e0da27 1.4.0
-1f3125cba7976538952be268f107c1d0c36c5ce8 1.4.1
-04ab22db4ff737cf31e91d75a0f5d7077f324167 1.4.2
-9950bf9d684a984d511795013421c89c5cf88bef 1.4.3
-d9951e3bdbc765e73835ae13012f6a074d13d8bf 1.4.4
-b827dd156a36753e32c7f3f15ce82d6fe9e356c8 1.4.6
-f15726f9e5a67cc6221c499affa4840e9d591763 1.4.7
-abfabd07a1d328f13c730e8a50d80d2e470afd3b 1.4.9
-7f37ee0aff9be4b839d6759cfee336f60e8393a4 1.4.10
-fe4593263efa10ea7ba014db6e3379e0b82368a2 1.4.11
-f07af25a26786e4825b5170e17ad693245cb3426 1.4.12
-d3730d84ba7eda92fd3469a3f63fd6d8cb22c975 1.4.13
-12c1ae8e7c5345721e9ec9f8e27b1e36c07f74dc 1.4.14
-12c1ae8e7c5345721e9ec9f8e27b1e36c07f74dc 1.4.14
-0000000000000000000000000000000000000000 1.4.14
-0000000000000000000000000000000000000000 1.4.14
-1497e2efd0f8c73a0e3d529debf0c489e4cd6cab 1.4.14
-e065014c1ce8ad110a381e9baaaa5d647ba7ac6b 1.4.15
-e9e5b38f53dc35b35aa1f9ee9a9be9bbd2d2c3b1 1.4.16
-c603503945f52b78522d96a423605cbc953236d3 1.4.17
-c59201105a29801cc858eb9160b7a19791b91a35 1.4.18
-284cc172e294d48edc840012e1451c32c3963d92 1.4.19
-a3e0626aa0c5aecf271367dc77e476ab216ea3c8 1.4.20
-5e48016c4a3af8e7358a1267d33d021e71765bed 1.4.21
-01ae2cfcc61c4fcb3aa5031349adb5b467c31018 1.4.23
-5ffd982f4dff60b588f309cd9bdc61036547282a 1.4.24
-dc9ffbcaf1f7d72e96be3f68c11deebb7e7193c5 1.4.25
-6de1a44bf75de7af4fcae947c235e9072bbdbb9a 1.4.26
-7d650ba2657890a2253c8c4a83f170febebd90fa 1.4.27
-7d650ba2657890a2253c8c4a83f170febebd90fa 1.4.27
-1810003dec63dd1b506a23849861fffa5bc3ba13 1.4.27
-ba08706f08ddea1b77a426f00dfe2bdc244345e8 1.4.28
-4e8054ada63f3327bcf759ae7cd36c7c8652bc9b 1.4.29
-366ab346610c6de8aaa7617e24011794b40236c6 1.4.30
diff --git a/tests/wpt/web-platform-tests/tools/py/CHANGELOG b/tests/wpt/web-platform-tests/tools/py/CHANGELOG
deleted file mode 100644
index 712fc4c5389..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/CHANGELOG
+++ /dev/null
@@ -1,1089 +0,0 @@
-1.4.31
-==================================================
-
-- fix local().copy(dest, mode=True) to also work
- with unicode.
-
-- pass better error message with svn EEXIST paths
-
-1.4.30
-==================================================
-
-- fix issue68 an assert with a multiline list comprehension
- was not reported correctly. Thanks Henrik Heibuerger.
-
-
-1.4.29
-==================================================
-
-- fix issue55: revert a change to the statement finding algorithm
- which is used by pytest for generating tracebacks.
- Thanks Daniel Hahler for initial analysis.
-
-- fix pytest issue254 for when traceback rendering can't
- find valid source code. Thanks Ionel Cristian Maries.
-
-
-1.4.28
-==================================================
-
-- fix issue64 -- dirpath regression when "abs=True" is passed.
- Thanks Gilles Dartiguelongue.
-
-1.4.27
-==================================================
-
-- fix issue59: point to new repo site
-
-- allow a new ensuresyspath="append" mode for py.path.local.pyimport()
- so that a neccessary import path is appended instead of prepended to
- sys.path
-
-- strike undocumented, untested argument to py.path.local.pypkgpath
-
-- speed up py.path.local.dirpath by a factor of 10
-
-1.4.26
-==================================================
-
-- avoid calling normpath twice in py.path.local
-
-- py.builtin._reraise properly reraises under Python3 now.
-
-- fix issue53 - remove module index, thanks jenisys.
-
-- allow posix path separators when "fnmatch" is called.
- Thanks Christian Long for the complete PR.
-
-1.4.25
-==================================================
-
-- fix issue52: vaguely fix py25 compat of py.path.local (it's not
- officially supported), also fix docs
-
-- fix pytest issue 589: when checking if we have a recursion error
- check for the specific "maximum recursion depth" text of the exception.
-
-1.4.24
-==================================================
-
-- Fix retrieving source when an else: line has an other statement on
- the same line.
-
-- add localpath read_text/write_text/read_bytes/write_bytes methods
- as shortcuts and clearer bytes/text interfaces for read/write.
- Adapted from a PR from Paul Moore.
-
-
-1.4.23
-==================================================
-
-- use newer apipkg version which makes attribute access on
- alias modules resolve to None rather than an ImportError.
- This helps with code that uses inspect.getframeinfo()
- on py34 which causes a complete walk on sys.modules
- thus triggering the alias module to resolve and blowing
- up with ImportError. The negative side is that something
- like "py.test.X" will now result in None instead of "importerror: pytest"
- if pytest is not installed. But you shouldn't import "py.test"
- anyway anymore.
-
-- adapt one svn test to only check for any exception instead
- of specific ones because different svn versions cause different
- errors and we don't care.
-
-
-1.4.22
-==================================================
-
-- refactor class-level registry on ForkedFunc child start/finish
- event to become instance based (i.e. passed into the constructor)
-
-1.4.21
-==================================================
-
-- ForkedFunc now has class-level register_on_start/on_exit()
- methods to allow adding information in the boxed process.
- Thanks Marc Schlaich.
-
-- ForkedFunc in the child opens in "auto-flush" mode for
- stdout/stderr so that when a subprocess dies you can see
- its output even if it didn't flush itself.
-
-- refactor traceback generation in light of pytest issue 364
- (shortening tracebacks). you can now set a new traceback style
- on a per-entry basis such that a caller can force entries to be
- isplayed as short or long entries.
-
-- win32: py.path.local.sysfind(name) will preferrably return files with
- extensions so that if "X" and "X.bat" or "X.exe" is on the PATH,
- one of the latter two will be returned.
-
-1.4.20
-==================================================
-
-- ignore unicode decode errors in xmlescape. Thanks Anatoly Bubenkoff.
-
-- on python2 modify traceback.format_exception_only to match python3
- behaviour, namely trying to print unicode for Exception instances
-
-- use a safer way for serializing exception reports (helps to fix
- pytest issue413)
-
-Changes between 1.4.18 and 1.4.19
-==================================================
-
-- merge in apipkg fixes
-
-- some micro-optimizations in py/_code/code.py for speeding
- up pytest runs. Thanks Alex Gaynor for initiative.
-
-- check PY_COLORS=1 or PY_COLORS=0 to force coloring/not-coloring
- for py.io.TerminalWriter() independently from capabilities
- of the output file. Thanks Marc Abramowitz for the PR.
-
-- some fixes to unicode handling in assertion handling.
- Thanks for the PR to Floris Bruynooghe. (This helps
- to fix pytest issue 319).
-
-- depend on setuptools presence, remove distribute_setup
-
-Changes between 1.4.17 and 1.4.18
-==================================================
-
-- introduce path.ensure_dir() as a synonym for ensure(..., dir=1)
-
-- some unicode/python3 related fixes wrt to path manipulations
- (if you start passing unicode particular in py2 you might
- still get problems, though)
-
-Changes between 1.4.16 and 1.4.17
-==================================================
-
-- make py.io.TerminalWriter() prefer colorama if it is available
- and avoid empty lines when separator-lines are printed by
- being defensive and reducing the working terminalwidth by 1
-
-- introduce optional "expanduser" argument to py.path.local
- to that local("~", expanduser=True) gives the home
- directory of "user".
-
-Changes between 1.4.15 and 1.4.16
-==================================================
-
-- fix issue35 - define __gt__ ordering between a local path
- and strings
-
-- fix issue36 - make chdir() work even if os.getcwd() fails.
-
-- add path.exists/isdir/isfile/islink shortcuts
-
-- introduce local path.as_cwd() context manager.
-
-- introduce p.write(ensure=1) and p.open(ensure=1)
- where ensure triggers creation of neccessary parent
- dirs.
-
-
-Changes between 1.4.14 and 1.4.15
-==================================================
-
-- majorly speed up some common calling patterns with
- LocalPath.listdir()/join/check/stat functions considerably.
-
-- fix an edge case with fnmatch where a glob style pattern appeared
- in an absolute path.
-
-Changes between 1.4.13 and 1.4.14
-==================================================
-
-- fix dupfile to work with files that don't
- carry a mode. Thanks Jason R. Coombs.
-
-Changes between 1.4.12 and 1.4.13
-==================================================
-
-- fix getting statementrange/compiling a file ending
- in a comment line without newline (on python2.5)
-- for local paths you can pass "mode=True" to a copy()
- in order to copy permission bits (underlying mechanism
- is using shutil.copymode)
-- add paths arguments to py.path.local.sysfind to restrict
- search to the diretories in the path.
-- add isdir/isfile/islink to path.stat() objects allowing to perform
- multiple checks without calling out multiple times
-- drop py.path.local.__new__ in favour of a simpler __init__
-- iniconfig: allow "name:value" settings in config files, no space after
- "name" required
-- fix issue 27 - NameError in unlikely untested case of saferepr
-
-
-Changes between 1.4.11 and 1.4.12
-==================================================
-
-- fix python2.4 support - for pre-AST interpreters re-introduce
- old way to find statements in exceptions (closes pytest issue 209)
-- add tox.ini to distribution
-- fix issue23 - print *,** args information in tracebacks,
- thanks Manuel Jacob
-
-
-Changes between 1.4.10 and 1.4.11
-==================================================
-
-- use _ast to determine statement ranges when printing tracebacks -
- avoiding multi-second delays on some large test modules
-- fix an internal test to not use class-denoted pytest_funcarg__
-- fix a doc link to bug tracker
-- try to make terminal.write() printing more robust against
- unicodeencode/decode problems, amend according test
-- introduce py.builtin.text and py.builtin.bytes
- to point to respective str/unicode (py2) and bytes/str (py3) types
-- fix error handling on win32/py33 for ENODIR
-
-Changes between 1.4.9 and 1.4.10
-==================================================
-
-- terminalwriter: default to encode to UTF8 if no encoding is defined
- on the output stream
-- issue22: improve heuristic for finding the statementrange in exceptions
-
-Changes between 1.4.8 and 1.4.9
-==================================================
-
-- fix bug of path.visit() which would not recognize glob-style patterns
- for the "rec" recursion argument
-- changed iniconfig parsing to better conform, now the chars ";"
- and "#" only mark a comment at the stripped start of a line
-- include recent apipkg-1.2
-- change internal terminalwriter.line/reline logic to more nicely
- support file spinners
-
-Changes between 1.4.7 and 1.4.8
-==================================================
-
-- fix issue 13 - correct handling of the tag name object in xmlgen
-- fix issue 14 - support raw attribute values in xmlgen
-- fix windows terminalwriter printing/re-line problem
-- update distribute_setup.py to 0.6.27
-
-Changes between 1.4.6 and 1.4.7
-==================================================
-
-- fix issue11 - own test failure with python3.3 / Thanks Benjamin Peterson
-- help fix pytest issue 102
-
-Changes between 1.4.5 and 1.4.6
-==================================================
-
-- help to fix pytest issue99: unify output of
- ExceptionInfo.getrepr(style="native") with ...(style="long")
-- fix issue7: source.getstatementrange() now raises proper error
- if no valid statement can be found
-- fix issue8: fix code and tests of svnurl/svnwc to work on subversion 1.7 -
- note that path.status(updates=1) will not properly work svn-17's status
- --xml output is broken.
-- make source.getstatementrange() more resilent about non-python code frames
- (as seen from jnja2)
-- make trackeback recursion detection more resilent
- about the eval magic of a decorator library
-- iniconfig: add support for ; as comment starter
-- properly handle lists in xmlgen on python3
-- normalize py.code.getfslineno(obj) to always return a (string, int) tuple
- defaulting to ("", -1) respectively if no source code can be found for obj.
-
-Changes between 1.4.4 and 1.4.5
-==================================================
-
-- improve some unicode handling in terminalwriter and capturing
- (used by pytest)
-
-Changes between 1.4.3 and 1.4.4
-==================================================
-
-- a few fixes and assertion related refinements for pytest-2.1
-- guard py.code.Code and getfslineno against bogus input
- and make py.code.Code objects for object instance
- by looking up their __call__ function.
-- make exception presentation robust against invalid current cwd
-
-Changes between 1.4.2 and 1.4.3
-==================================================
-
-- fix terminal coloring issue for skipped tests (thanks Amaury)
-- fix issue4 - large calls to ansi_print (thanks Amaury)
-
-Changes between 1.4.1 and 1.4.2
-==================================================
-
-- fix (pytest) issue23 - tmpdir argument now works on Python3.2 and WindowsXP
- (which apparently starts to offer os.symlink now)
-
-- better error message for syntax errors from compiled code
-
-- small fix to better deal with (un-)colored terminal output on windows
-
-Changes between 1.4.0 and 1.4.1
-==================================================
-
-- fix issue1 - py.error.* classes to be pickleable
-
-- fix issue2 - on windows32 use PATHEXT as the list of potential
- extensions to find find binaries with py.path.local.sysfind(commandname)
-
-- fix (pytest-) issue10 and refine assertion reinterpretation
- to avoid breaking if the __nonzero__ of an object fails
-
-- fix (pytest-) issue17 where python3 does not like "import *"
- leading to misrepresentation of import-errors in test modules
-
-- fix py.error.* attribute pypy access issue
-
-- allow path.samefile(arg) to succeed when arg is a relative filename
-
-- fix (pytest-) issue20 path.samefile(relpath) works as expected now
-
-- fix (pytest-) issue8 len(long_list) now shows the lenght of the list
-
-Changes between 1.3.4 and 1.4.0
-==================================================
-
-- py.test was moved to a separate "pytest" package. What remains is
- a stub hook which will proxy ``import py.test`` to ``pytest``.
-- all command line tools ("py.cleanup/lookup/countloc/..." moved
- to "pycmd" package)
-- removed the old and deprecated "py.magic" namespace
-- use apipkg-1.1 and make py.apipkg.initpkg|ApiModule available
-- add py.iniconfig module for brain-dead easy ini-config file parsing
-- introduce py.builtin.any()
-- path objects have a .dirname attribute now (equivalent to
- os.path.dirname(path))
-- path.visit() accepts breadthfirst (bf) and sort options
-- remove deprecated py.compat namespace
-
-Changes between 1.3.3 and 1.3.4
-==================================================
-
-- fix issue111: improve install documentation for windows
-- fix issue119: fix custom collectability of __init__.py as a module
-- fix issue116: --doctestmodules work with __init__.py files as well
-- fix issue115: unify internal exception passthrough/catching/GeneratorExit
-- fix issue118: new --tb=native for presenting cpython-standard exceptions
-
-Changes between 1.3.2 and 1.3.3
-==================================================
-
-- fix issue113: assertion representation problem with triple-quoted strings
- (and possibly other cases)
-- make conftest loading detect that a conftest file with the same
- content was already loaded, avoids surprises in nested directory structures
- which can be produced e.g. by Hudson. It probably removes the need to use
- --confcutdir in most cases.
-- fix terminal coloring for win32
- (thanks Michael Foord for reporting)
-- fix weirdness: make terminal width detection work on stdout instead of stdin
- (thanks Armin Ronacher for reporting)
-- remove trailing whitespace in all py/text distribution files
-
-Changes between 1.3.1 and 1.3.2
-==================================================
-
-New features
-++++++++++++++++++
-
-- fix issue103: introduce py.test.raises as context manager, examples::
-
- with py.test.raises(ZeroDivisionError):
- x = 0
- 1 / x
-
- with py.test.raises(RuntimeError) as excinfo:
- call_something()
-
- # you may do extra checks on excinfo.value|type|traceback here
-
- (thanks Ronny Pfannschmidt)
-
-- Funcarg factories can now dynamically apply a marker to a
- test invocation. This is for example useful if a factory
- provides parameters to a test which are expected-to-fail::
-
- def pytest_funcarg__arg(request):
- request.applymarker(py.test.mark.xfail(reason="flaky config"))
- ...
-
- def test_function(arg):
- ...
-
-- improved error reporting on collection and import errors. This makes
- use of a more general mechanism, namely that for custom test item/collect
- nodes ``node.repr_failure(excinfo)`` is now uniformly called so that you can
- override it to return a string error representation of your choice
- which is going to be reported as a (red) string.
-
-- introduce '--junitprefix=STR' option to prepend a prefix
- to all reports in the junitxml file.
-
-Bug fixes / Maintenance
-++++++++++++++++++++++++++
-
-- make tests and the ``pytest_recwarn`` plugin in particular fully compatible
- to Python2.7 (if you use the ``recwarn`` funcarg warnings will be enabled so that
- you can properly check for their existence in a cross-python manner).
-- refine --pdb: ignore xfailed tests, unify its TB-reporting and
- don't display failures again at the end.
-- fix assertion interpretation with the ** operator (thanks Benjamin Peterson)
-- fix issue105 assignment on the same line as a failing assertion (thanks Benjamin Peterson)
-- fix issue104 proper escaping for test names in junitxml plugin (thanks anonymous)
-- fix issue57 -f|--looponfail to work with xpassing tests (thanks Ronny)
-- fix issue92 collectonly reporter and --pastebin (thanks Benjamin Peterson)
-- fix py.code.compile(source) to generate unique filenames
-- fix assertion re-interp problems on PyPy, by defering code
- compilation to the (overridable) Frame.eval class. (thanks Amaury Forgeot)
-- fix py.path.local.pyimport() to work with directories
-- streamline py.path.local.mkdtemp implementation and usage
-- don't print empty lines when showing junitxml-filename
-- add optional boolean ignore_errors parameter to py.path.local.remove
-- fix terminal writing on win32/python2.4
-- py.process.cmdexec() now tries harder to return properly encoded unicode objects
- on all python versions
-- install plain py.test/py.which scripts also for Jython, this helps to
- get canonical script paths in virtualenv situations
-- make path.bestrelpath(path) return ".", note that when calling
- X.bestrelpath the assumption is that X is a directory.
-- make initial conftest discovery ignore "--" prefixed arguments
-- fix resultlog plugin when used in an multicpu/multihost xdist situation
- (thanks Jakub Gustak)
-- perform distributed testing related reporting in the xdist-plugin
- rather than having dist-related code in the generic py.test
- distribution
-- fix homedir detection on Windows
-- ship distribute_setup.py version 0.6.13
-
-Changes between 1.3.0 and 1.3.1
-==================================================
-
-New features
-++++++++++++++++++
-
-- issue91: introduce new py.test.xfail(reason) helper
- to imperatively mark a test as expected to fail. Can
- be used from within setup and test functions. This is
- useful especially for parametrized tests when certain
- configurations are expected-to-fail. In this case the
- declarative approach with the @py.test.mark.xfail cannot
- be used as it would mark all configurations as xfail.
-
-- issue102: introduce new --maxfail=NUM option to stop
- test runs after NUM failures. This is a generalization
- of the '-x' or '--exitfirst' option which is now equivalent
- to '--maxfail=1'. Both '-x' and '--maxfail' will
- now also print a line near the end indicating the Interruption.
-
-- issue89: allow py.test.mark decorators to be used on classes
- (class decorators were introduced with python2.6) and
- also allow to have multiple markers applied at class/module level
- by specifying a list.
-
-- improve and refine letter reporting in the progress bar:
- . pass
- f failed test
- s skipped tests (reminder: use for dependency/platform mismatch only)
- x xfailed test (test that was expected to fail)
- X xpassed test (test that was expected to fail but passed)
-
- You can use any combination of 'fsxX' with the '-r' extended
- reporting option. The xfail/xpass results will show up as
- skipped tests in the junitxml output - which also fixes
- issue99.
-
-- make py.test.cmdline.main() return the exitstatus instead of raising
- SystemExit and also allow it to be called multiple times. This of
- course requires that your application and tests are properly teared
- down and don't have global state.
-
-Fixes / Maintenance
-++++++++++++++++++++++
-
-- improved traceback presentation:
- - improved and unified reporting for "--tb=short" option
- - Errors during test module imports are much shorter, (using --tb=short style)
- - raises shows shorter more relevant tracebacks
- - --fulltrace now more systematically makes traces longer / inhibits cutting
-
-- improve support for raises and other dynamically compiled code by
- manipulating python's linecache.cache instead of the previous
- rather hacky way of creating custom code objects. This makes
- it seemlessly work on Jython and PyPy where it previously didn't.
-
-- fix issue96: make capturing more resilient against Control-C
- interruptions (involved somewhat substantial refactoring
- to the underlying capturing functionality to avoid race
- conditions).
-
-- fix chaining of conditional skipif/xfail decorators - so it works now
- as expected to use multiple @py.test.mark.skipif(condition) decorators,
- including specific reporting which of the conditions lead to skipping.
-
-- fix issue95: late-import zlib so that it's not required
- for general py.test startup.
-
-- fix issue94: make reporting more robust against bogus source code
- (and internally be more careful when presenting unexpected byte sequences)
-
-
-Changes between 1.2.1 and 1.3.0
-==================================================
-
-- deprecate --report option in favour of a new shorter and easier to
- remember -r option: it takes a string argument consisting of any
- combination of 'xfsX' characters. They relate to the single chars
- you see during the dotted progress printing and will print an extra line
- per test at the end of the test run. This extra line indicates the exact
- position or test ID that you directly paste to the py.test cmdline in order
- to re-run a particular test.
-
-- allow external plugins to register new hooks via the new
- pytest_addhooks(pluginmanager) hook. The new release of
- the pytest-xdist plugin for distributed and looponfailing
- testing requires this feature.
-
-- add a new pytest_ignore_collect(path, config) hook to allow projects and
- plugins to define exclusion behaviour for their directory structure -
- for example you may define in a conftest.py this method::
-
- def pytest_ignore_collect(path):
- return path.check(link=1)
-
- to prevent even a collection try of any tests in symlinked dirs.
-
-- new pytest_pycollect_makemodule(path, parent) hook for
- allowing customization of the Module collection object for a
- matching test module.
-
-- extend and refine xfail mechanism:
- ``@py.test.mark.xfail(run=False)`` do not run the decorated test
- ``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries
- specifiying ``--runxfail`` on command line virtually ignores xfail markers
-
-- expose (previously internal) commonly useful methods:
- py.io.get_terminal_with() -> return terminal width
- py.io.ansi_print(...) -> print colored/bold text on linux/win32
- py.io.saferepr(obj) -> return limited representation string
-
-- expose test outcome related exceptions as py.test.skip.Exception,
- py.test.raises.Exception etc., useful mostly for plugins
- doing special outcome interpretation/tweaking
-
-- (issue85) fix junitxml plugin to handle tests with non-ascii output
-
-- fix/refine python3 compatibility (thanks Benjamin Peterson)
-
-- fixes for making the jython/win32 combination work, note however:
- jython2.5.1/win32 does not provide a command line launcher, see
- http://bugs.jython.org/issue1491 . See pylib install documentation
- for how to work around.
-
-- fixes for handling of unicode exception values and unprintable objects
-
-- (issue87) fix unboundlocal error in assertionold code
-
-- (issue86) improve documentation for looponfailing
-
-- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method
-
-- ship distribute_setup.py version 0.6.10
-
-- added links to the new capturelog and coverage plugins
-
-
-Changes between 1.2.1 and 1.2.0
-=====================================
-
-- refined usage and options for "py.cleanup"::
-
- py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
- py.cleanup -e .swp -e .cache # also remove files with these extensions
- py.cleanup -s # remove "build" and "dist" directory next to setup.py files
- py.cleanup -d # also remove empty directories
- py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
- py.cleanup -n # dry run, only show what would be removed
-
-- add a new option "py.test --funcargs" which shows available funcargs
- and their help strings (docstrings on their respective factory function)
- for a given test path
-
-- display a short and concise traceback if a funcarg lookup fails
-
-- early-load "conftest.py" files in non-dot first-level sub directories.
- allows to conveniently keep and access test-related options in a ``test``
- subdir and still add command line options.
-
-- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
-
-- fix issue78: always call python-level teardown functions even if the
- according setup failed. This includes refinements for calling setup_module/class functions
- which will now only be called once instead of the previous behaviour where they'd be called
- multiple times if they raise an exception (including a Skipped exception). Any exception
- will be re-corded and associated with all tests in the according module/class scope.
-
-- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
-
-- fix pdb debugging to be in the correct frame on raises-related errors
-
-- update apipkg.py to fix an issue where recursive imports might
- unnecessarily break importing
-
-- fix plugin links
-
-Changes between 1.2 and 1.1.1
-=====================================
-
-- moved dist/looponfailing from py.test core into a new
- separately released pytest-xdist plugin.
-
-- new junitxml plugin: --junitxml=path will generate a junit style xml file
- which is processable e.g. by the Hudson CI system.
-
-- new option: --genscript=path will generate a standalone py.test script
- which will not need any libraries installed. thanks to Ralf Schmitt.
-
-- new option: --ignore will prevent specified path from collection.
- Can be specified multiple times.
-
-- new option: --confcutdir=dir will make py.test only consider conftest
- files that are relative to the specified dir.
-
-- new funcarg: "pytestconfig" is the pytest config object for access
- to command line args and can now be easily used in a test.
-
-- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to
- disambiguate between Python3, python2.X, Jython and PyPy installed versions.
-
-- new "pytestconfig" funcarg allows access to test config object
-
-- new "pytest_report_header" hook can return additional lines
- to be displayed at the header of a test run.
-
-- (experimental) allow "py.test path::name1::name2::..." for pointing
- to a test within a test collection directly. This might eventually
- evolve as a full substitute to "-k" specifications.
-
-- streamlined plugin loading: order is now as documented in
- customize.html: setuptools, ENV, commandline, conftest.
- also setuptools entry point names are turned to canonical namees ("pytest_*")
-
-- automatically skip tests that need 'capfd' but have no os.dup
-
-- allow pytest_generate_tests to be defined in classes as well
-
-- deprecate usage of 'disabled' attribute in favour of pytestmark
-- deprecate definition of Directory, Module, Class and Function nodes
- in conftest.py files. Use pytest collect hooks instead.
-
-- collection/item node specific runtest/collect hooks are only called exactly
- on matching conftest.py files, i.e. ones which are exactly below
- the filesystem path of an item
-
-- change: the first pytest_collect_directory hook to return something
- will now prevent further hooks to be called.
-
-- change: figleaf plugin now requires --figleaf to run. Also
- change its long command line options to be a bit shorter (see py.test -h).
-
-- change: pytest doctest plugin is now enabled by default and has a
- new option --doctest-glob to set a pattern for file matches.
-
-- change: remove internal py._* helper vars, only keep py._pydir
-
-- robustify capturing to survive if custom pytest_runtest_setup
- code failed and prevented the capturing setup code from running.
-
-- make py.test.* helpers provided by default plugins visible early -
- works transparently both for pydoc and for interactive sessions
- which will regularly see e.g. py.test.mark and py.test.importorskip.
-
-- simplify internal plugin manager machinery
-- simplify internal collection tree by introducing a RootCollector node
-
-- fix assert reinterpreation that sees a call containing "keyword=..."
-
-- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
- hooks on slaves during dist-testing, report module/session teardown
- hooks correctly.
-
-- fix issue65: properly handle dist-testing if no
- execnet/py lib installed remotely.
-
-- skip some install-tests if no execnet is available
-
-- fix docs, fix internal bin/ script generation
-
-
-Changes between 1.1.1 and 1.1.0
-=====================================
-
-- introduce automatic plugin registration via 'pytest11'
- entrypoints via setuptools' pkg_resources.iter_entry_points
-
-- fix py.test dist-testing to work with execnet >= 1.0.0b4
-
-- re-introduce py.test.cmdline.main() for better backward compatibility
-
-- svn paths: fix a bug with path.check(versioned=True) for svn paths,
- allow '%' in svn paths, make svnwc.update() default to interactive mode
- like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
-
-- refine distributed tarball to contain test and no pyc files
-
-- try harder to have deprecation warnings for py.compat.* accesses
- report a correct location
-
-Changes between 1.1.0 and 1.0.2
-=====================================
-
-* adjust and improve docs
-
-* remove py.rest tool and internal namespace - it was
- never really advertised and can still be used with
- the old release if needed. If there is interest
- it could be revived into its own tool i guess.
-
-* fix issue48 and issue59: raise an Error if the module
- from an imported test file does not seem to come from
- the filepath - avoids "same-name" confusion that has
- been reported repeatedly
-
-* merged Ronny's nose-compatibility hacks: now
- nose-style setup_module() and setup() functions are
- supported
-
-* introduce generalized py.test.mark function marking
-
-* reshuffle / refine command line grouping
-
-* deprecate parser.addgroup in favour of getgroup which creates option group
-
-* add --report command line option that allows to control showing of skipped/xfailed sections
-
-* generalized skipping: a new way to mark python functions with skipif or xfail
- at function, class and modules level based on platform or sys-module attributes.
-
-* extend py.test.mark decorator to allow for positional args
-
-* introduce and test "py.cleanup -d" to remove empty directories
-
-* fix issue #59 - robustify unittest test collection
-
-* make bpython/help interaction work by adding an __all__ attribute
- to ApiModule, cleanup initpkg
-
-* use MIT license for pylib, add some contributors
-
-* remove py.execnet code and substitute all usages with 'execnet' proper
-
-* fix issue50 - cached_setup now caches more to expectations
- for test functions with multiple arguments.
-
-* merge Jarko's fixes, issue #45 and #46
-
-* add the ability to specify a path for py.lookup to search in
-
-* fix a funcarg cached_setup bug probably only occuring
- in distributed testing and "module" scope with teardown.
-
-* many fixes and changes for making the code base python3 compatible,
- many thanks to Benjamin Peterson for helping with this.
-
-* consolidate builtins implementation to be compatible with >=2.3,
- add helpers to ease keeping 2 and 3k compatible code
-
-* deprecate py.compat.doctest|subprocess|textwrap|optparse
-
-* deprecate py.magic.autopath, remove py/magic directory
-
-* move pytest assertion handling to py/code and a pytest_assertion
- plugin, add "--no-assert" option, deprecate py.magic namespaces
- in favour of (less) py.code ones.
-
-* consolidate and cleanup py/code classes and files
-
-* cleanup py/misc, move tests to bin-for-dist
-
-* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
-
-* consolidate py.log implementation, remove old approach.
-
-* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
- text/unicode and byte-streams (uses underlying standard lib io.*
- if available)
-
-* make py.unittest_convert helper script available which converts "unittest.py"
- style files into the simpler assert/direct-test-classes py.test/nosetests
- style. The script was written by Laura Creighton.
-
-* simplified internal localpath implementation
-
-Changes between 1.0.1 and 1.0.2
-=====================================
-
-* fixing packaging issues, triggered by fedora redhat packaging,
- also added doc, examples and contrib dirs to the tarball.
-
-* added a documentation link to the new django plugin.
-
-Changes between 1.0.0 and 1.0.1
-=====================================
-
-* added a 'pytest_nose' plugin which handles nose.SkipTest,
- nose-style function/method/generator setup/teardown and
- tries to report functions correctly.
-
-* capturing of unicode writes or encoded strings to sys.stdout/err
- work better, also terminalwriting was adapted and somewhat
- unified between windows and linux.
-
-* improved documentation layout and content a lot
-
-* added a "--help-config" option to show conftest.py / ENV-var names for
- all longopt cmdline options, and some special conftest.py variables.
- renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
-
-* fix issue #27: better reporting on non-collectable items given on commandline
- (e.g. pyc files)
-
-* fix issue #33: added --version flag (thanks Benjamin Peterson)
-
-* fix issue #32: adding support for "incomplete" paths to wcpath.status()
-
-* "Test" prefixed classes are *not* collected by default anymore if they
- have an __init__ method
-
-* monkeypatch setenv() now accepts a "prepend" parameter
-
-* improved reporting of collection error tracebacks
-
-* simplified multicall mechanism and plugin architecture,
- renamed some internal methods and argnames
-
-Changes between 1.0.0b9 and 1.0.0
-=====================================
-
-* more terse reporting try to show filesystem path relatively to current dir
-* improve xfail output a bit
-
-Changes between 1.0.0b8 and 1.0.0b9
-=====================================
-
-* cleanly handle and report final teardown of test setup
-
-* fix svn-1.6 compat issue with py.path.svnwc().versioned()
- (thanks Wouter Vanden Hove)
-
-* setup/teardown or collection problems now show as ERRORs
- or with big "E"'s in the progress lines. they are reported
- and counted separately.
-
-* dist-testing: properly handle test items that get locally
- collected but cannot be collected on the remote side - often
- due to platform/dependency reasons
-
-* simplified py.test.mark API - see keyword plugin documentation
-
-* integrate better with logging: capturing now by default captures
- test functions and their immediate setup/teardown in a single stream
-
-* capsys and capfd funcargs now have a readouterr() and a close() method
- (underlyingly py.io.StdCapture/FD objects are used which grew a
- readouterr() method as well to return snapshots of captured out/err)
-
-* make assert-reinterpretation work better with comparisons not
- returning bools (reported with numpy from thanks maciej fijalkowski)
-
-* reworked per-test output capturing into the pytest_iocapture.py plugin
- and thus removed capturing code from config object
-
-* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
-
-
-Changes between 1.0.0b7 and 1.0.0b8
-=====================================
-
-* pytest_unittest-plugin is now enabled by default
-
-* introduced pytest_keyboardinterrupt hook and
- refined pytest_sessionfinish hooked, added tests.
-
-* workaround a buggy logging module interaction ("closing already closed
- files"). Thanks to Sridhar Ratnakumar for triggering.
-
-* if plugins use "py.test.importorskip" for importing
- a dependency only a warning will be issued instead
- of exiting the testing process.
-
-* many improvements to docs:
- - refined funcargs doc , use the term "factory" instead of "provider"
- - added a new talk/tutorial doc page
- - better download page
- - better plugin docstrings
- - added new plugins page and automatic doc generation script
-
-* fixed teardown problem related to partially failing funcarg setups
- (thanks MrTopf for reporting), "pytest_runtest_teardown" is now
- always invoked even if the "pytest_runtest_setup" failed.
-
-* tweaked doctest output for docstrings in py modules,
- thanks Radomir.
-
-Changes between 1.0.0b3 and 1.0.0b7
-=============================================
-
-* renamed py.test.xfail back to py.test.mark.xfail to avoid
- two ways to decorate for xfail
-
-* re-added py.test.mark decorator for setting keywords on functions
- (it was actually documented so removing it was not nice)
-
-* remove scope-argument from request.addfinalizer() because
- request.cached_setup has the scope arg. TOOWTDI.
-
-* perform setup finalization before reporting failures
-
-* apply modified patches from Andreas Kloeckner to allow
- test functions to have no func_code (#22) and to make
- "-k" and function keywords work (#20)
-
-* apply patch from Daniel Peolzleithner (issue #23)
-
-* resolve issue #18, multiprocessing.Manager() and
- redirection clash
-
-* make __name__ == "__channelexec__" for remote_exec code
-
-Changes between 1.0.0b1 and 1.0.0b3
-=============================================
-
-* plugin classes are removed: one now defines
- hooks directly in conftest.py or global pytest_*.py
- files.
-
-* added new pytest_namespace(config) hook that allows
- to inject helpers directly to the py.test.* namespace.
-
-* documented and refined many hooks
-
-* added new style of generative tests via
- pytest_generate_tests hook that integrates
- well with function arguments.
-
-
-Changes between 0.9.2 and 1.0.0b1
-=============================================
-
-* introduced new "funcarg" setup method,
- see doc/test/funcarg.txt
-
-* introduced plugin architecuture and many
- new py.test plugins, see
- doc/test/plugins.txt
-
-* teardown_method is now guaranteed to get
- called after a test method has run.
-
-* new method: py.test.importorskip(mod,minversion)
- will either import or call py.test.skip()
-
-* completely revised internal py.test architecture
-
-* new py.process.ForkedFunc object allowing to
- fork execution of a function to a sub process
- and getting a result back.
-
-XXX lots of things missing here XXX
-
-Changes between 0.9.1 and 0.9.2
-===============================
-
-* refined installation and metadata, created new setup.py,
- now based on setuptools/ez_setup (thanks to Ralf Schmitt
- for his support).
-
-* improved the way of making py.* scripts available in
- windows environments, they are now added to the
- Scripts directory as ".cmd" files.
-
-* py.path.svnwc.status() now is more complete and
- uses xml output from the 'svn' command if available
- (Guido Wesdorp)
-
-* fix for py.path.svn* to work with svn 1.5
- (Chris Lamb)
-
-* fix path.relto(otherpath) method on windows to
- use normcase for checking if a path is relative.
-
-* py.test's traceback is better parseable from editors
- (follows the filenames:LINENO: MSG convention)
- (thanks to Osmo Salomaa)
-
-* fix to javascript-generation, "py.test --runbrowser"
- should work more reliably now
-
-* removed previously accidentally added
- py.test.broken and py.test.notimplemented helpers.
-
-* there now is a py.__version__ attribute
-
-Changes between 0.9.0 and 0.9.1
-===============================
-
-This is a fairly complete list of changes between 0.9 and 0.9.1, which can
-serve as a reference for developers.
-
-* allowing + signs in py.path.svn urls [39106]
-* fixed support for Failed exceptions without excinfo in py.test [39340]
-* added support for killing processes for Windows (as well as platforms that
- support os.kill) in py.misc.killproc [39655]
-* added setup/teardown for generative tests to py.test [40702]
-* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739]
-* fixed problem with calling .remove() on wcpaths of non-versioned files in
- py.path [44248]
-* fixed some import and inheritance issues in py.test [41480, 44648, 44655]
-* fail to run greenlet tests when pypy is available, but without stackless
- [45294]
-* small fixes in rsession tests [45295]
-* fixed issue with 2.5 type representations in py.test [45483, 45484]
-* made that internal reporting issues displaying is done atomically in py.test
- [45518]
-* made that non-existing files are igored by the py.lookup script [45519]
-* improved exception name creation in py.test [45535]
-* made that less threads are used in execnet [merge in 45539]
-* removed lock required for atomical reporting issue displaying in py.test
- [45545]
-* removed globals from execnet [45541, 45547]
-* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit
- get called in 2.5 (py.execnet) [45548]
-* fixed bug in joining threads in py.execnet's servemain [45549]
-* refactored py.test.rsession tests to not rely on exact output format anymore
- [45646]
-* using repr() on test outcome [45647]
-* added 'Reason' classes for py.test.skip() [45648, 45649]
-* killed some unnecessary sanity check in py.test.collect [45655]
-* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only
- usable by Administrators [45901]
-* added support for locking and non-recursive commits to py.path.svnwc [45994]
-* locking files in py.execnet to prevent CPython from segfaulting [46010]
-* added export() method to py.path.svnurl
-* fixed -d -x in py.test [47277]
-* fixed argument concatenation problem in py.path.svnwc [49423]
-* restore py.test behaviour that it exits with code 1 when there are failures
- [49974]
-* don't fail on html files that don't have an accompanying .txt file [50606]
-* fixed 'utestconvert.py < input' [50645]
-* small fix for code indentation in py.code.source [50755]
-* fix _docgen.py documentation building [51285]
-* improved checks for source representation of code blocks in py.test [51292]
-* added support for passing authentication to py.path.svn* objects [52000,
- 52001]
-* removed sorted() call for py.apigen tests in favour of [].sort() to support
- Python 2.3 [52481]
diff --git a/tests/wpt/web-platform-tests/tools/py/MANIFEST.in b/tests/wpt/web-platform-tests/tools/py/MANIFEST.in
deleted file mode 100644
index 31fb010b487..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/MANIFEST.in
+++ /dev/null
@@ -1,9 +0,0 @@
-include CHANGELOG
-include AUTHORS
-include README.txt
-include setup.py
-include LICENSE
-include conftest.py
-include tox.ini
-graft doc
-graft testing
diff --git a/tests/wpt/web-platform-tests/tools/py/README.txt b/tests/wpt/web-platform-tests/tools/py/README.txt
deleted file mode 100644
index e327e937373..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/README.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-.. image:: https://drone.io/bitbucket.org/pytest-dev/py/status.png
- :target: https://drone.io/bitbucket.org/pytest-dev/py/latest
-.. image:: https://pypip.in/v/py/badge.png
- :target: https://pypi.python.org/pypi/py
-
-The py lib is a Python development support library featuring
-the following tools and modules:
-
-* py.path: uniform local and svn path objects
-* py.apipkg: explicit API control and lazy-importing
-* py.iniconfig: easy parsing of .ini files
-* py.code: dynamic code generation and introspection
-
-NOTE: prior to the 1.4 release this distribution used to
-contain py.test which is now its own package, see http://pytest.org
-
-For questions and more information please visit http://pylib.readthedocs.org
-
-Bugs and issues: http://bitbucket.org/pytest-dev/py/issues/
-
-Authors: Holger Krekel and others, 2004-2015
diff --git a/tests/wpt/web-platform-tests/tools/py/conftest.py b/tests/wpt/web-platform-tests/tools/py/conftest.py
deleted file mode 100644
index 11c2d442504..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/conftest.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import py
-import sys
-
-pytest_plugins = 'doctest pytester'.split()
-
-collect_ignore = ['build', 'doc/_build']
-
-
-import os, py
-pid = os.getpid()
-
-def pytest_addoption(parser):
- group = parser.getgroup("pylib", "py lib testing options")
- group.addoption('--runslowtests',
- action="store_true", dest="runslowtests", default=False,
- help=("run slow tests"))
-
-def pytest_funcarg__sshhost(request):
- val = request.config.getvalue("sshhost")
- if val:
- return val
- py.test.skip("need --sshhost option")
-def pytest_generate_tests(metafunc):
- multi = getattr(metafunc.function, 'multi', None)
- if multi is not None:
- assert len(multi.kwargs) == 1
- for name, l in multi.kwargs.items():
- for val in l:
- metafunc.addcall(funcargs={name: val})
- elif 'anypython' in metafunc.funcargnames:
- for name in ('python2.4', 'python2.5', 'python2.6',
- 'python2.7', 'python3.1', 'pypy-c', 'jython'):
- metafunc.addcall(id=name, param=name)
-
-# XXX copied from execnet's conftest.py - needs to be merged
-winpymap = {
- 'python2.7': r'C:\Python27\python.exe',
- 'python2.6': r'C:\Python26\python.exe',
- 'python2.5': r'C:\Python25\python.exe',
- 'python2.4': r'C:\Python24\python.exe',
- 'python3.1': r'C:\Python31\python.exe',
-}
-
-def getexecutable(name, cache={}):
- try:
- return cache[name]
- except KeyError:
- executable = py.path.local.sysfind(name)
- if executable:
- if name == "jython":
- import subprocess
- popen = subprocess.Popen([str(executable), "--version"],
- universal_newlines=True, stderr=subprocess.PIPE)
- out, err = popen.communicate()
- if not err or "2.5" not in err:
- executable = None
- cache[name] = executable
- return executable
-
-def pytest_funcarg__anypython(request):
- name = request.param
- executable = getexecutable(name)
- if executable is None:
- if sys.platform == "win32":
- executable = winpymap.get(name, None)
- if executable:
- executable = py.path.local(executable)
- if executable.check():
- return executable
- py.test.skip("no %s found" % (name,))
- return executable
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/index.txt b/tests/wpt/web-platform-tests/tools/py/doc/index.txt
deleted file mode 100644
index 7eb5c63905e..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/doc/index.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-.. py documentation master file, created by
- sphinx-quickstart on Thu Oct 21 08:30:10 2010.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
-Welcome to py's documentation!
-=================================
-
-see :ref:`CHANGELOG <changelog>` for latest changes.
-
-.. note::
-
- Since version 1.4, the testing tool "py.test" is part of its own `pytest distribution`_.
-
-.. _`pytest distribution`: http://pytest.org
-
-Contents:
-
-.. toctree::
-
- install
- path
- code
- io
- log
- xml
- misc
-
- :maxdepth: 2
-
-.. toctree::
- :hidden:
-
- announce/release-2.0.0
- changelog
- announce/*
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`search`
-
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/install.txt b/tests/wpt/web-platform-tests/tools/py/doc/install.txt
deleted file mode 100644
index d0e981def45..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/doc/install.txt
+++ /dev/null
@@ -1,88 +0,0 @@
-
-.. _`py`:
-.. _`index page`: http://pypi.python.org/pypi/py/
-
-installation info in a nutshell
-===================================================
-
-**PyPI name**: py_
-
-**Pythons**: CPython 2.6, 2.7, 3.3, 3.4, PyPy-2.3
-
-**Operating systems**: Linux, Windows, OSX, Unix
-
-**Requirements**: setuptools_ or Distribute_
-
-**Installers**: ``easy_install`` and ``pip``
-
-**hg repository**: https://bitbucket.org/hpk42/py
-
-easy install or pip ``py``
------------------------------
-
-Both `Distribute`_ and setuptools_ provide the ``easy_install``
-installation tool with which you can type into a command line window::
-
- easy_install -U py
-
-to install the latest release of the py lib. The ``-U`` switch
-will trigger an upgrade if you already have an older version installed.
-
-.. note::
-
- As of version 1.4 py does not contain py.test anymore - you
- need to install the new `pytest`_ distribution.
-
-.. _pytest: http://pytest.org
-
-Working from version control or a tarball
------------------------------------------------
-
-To follow development or start experiments, checkout the
-complete code and documentation source with mercurial_::
-
- hg clone https://bitbucket.org/hpk42/py
-
-Development takes place on the 'trunk' branch.
-
-You can also go to the python package index and
-download and unpack a TAR file::
-
- http://pypi.python.org/pypi/py/
-
-activating a checkout with setuptools
---------------------------------------------
-
-With a working `Distribute`_ or setuptools_ installation you can type::
-
- python setup.py develop
-
-in order to work inline with the tools and the lib of your checkout.
-
-.. _`no-setuptools`:
-
-.. _`directly use a checkout`:
-
-.. _`setuptools`: http://pypi.python.org/pypi/setuptools
-
-
-Mailing list and issue tracker
---------------------------------------
-
-- `py-dev developers list`_ and `commit mailing list`_.
-
-- #pylib on irc.freenode.net IRC channel for random questions.
-
-- `bitbucket issue tracker`_ use this bitbucket issue tracker to report
- bugs or request features.
-
-.. _`bitbucket issue tracker`: http://bitbucket.org/hpk42/py/issues/
-
-.. _codespeak: http://codespeak.net/
-.. _`py-dev`:
-.. _`development mailing list`:
-.. _`py-dev developers list`: http://codespeak.net/mailman/listinfo/py-dev
-.. _`py-svn`:
-.. _`commit mailing list`: http://codespeak.net/mailman/listinfo/py-svn
-
-.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/misc.txt b/tests/wpt/web-platform-tests/tools/py/doc/misc.txt
deleted file mode 100644
index 8c3c0b3f7a3..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/doc/misc.txt
+++ /dev/null
@@ -1,93 +0,0 @@
-====================================
-Miscellaneous features of the py lib
-====================================
-
-Mapping the standard python library into py
-===========================================
-
-The ``py.std`` object allows lazy access to
-standard library modules. For example, to get to the print-exception
-functionality of the standard library you can write::
-
- py.std.traceback.print_exc()
-
-without having to do anything else than the usual ``import py``
-at the beginning. You can access any other top-level standard
-library module this way. This means that you will only trigger
-imports of modules that are actually needed. Note that no attempt
-is made to import submodules.
-
-Support for interaction with system utilities/binaries
-======================================================
-
-Currently, the py lib offers two ways to interact with
-system executables. ``py.process.cmdexec()`` invokes
-the shell in order to execute a string. The other
-one, ``py.path.local``'s 'sysexec()' method lets you
-directly execute a binary.
-
-Both approaches will raise an exception in case of a return-
-code other than 0 and otherwise return the stdout-output
-of the child process.
-
-The shell based approach
-------------------------
-
-You can execute a command via your system shell
-by doing something like::
-
- out = py.process.cmdexec('ls -v')
-
-However, the ``cmdexec`` approach has a few shortcomings:
-
-- it relies on the underlying system shell
-- it neccessitates shell-escaping for expressing arguments
-- it does not easily allow to "fix" the binary you want to run.
-- it only allows to execute executables from the local
- filesystem
-
-.. _sysexec:
-
-local paths have ``sysexec``
-----------------------------
-
-In order to synchronously execute an executable file you
-can use ``sysexec``::
-
- binsvn.sysexec('ls', 'http://codespeak.net/svn')
-
-where ``binsvn`` is a path that points to the ``svn`` commandline
-binary. Note that this function does not offer any shell-escaping
-so you have to pass in already separated arguments.
-
-finding an executable local path
---------------------------------
-
-Finding an executable is quite different on multiple platforms.
-Currently, the ``PATH`` environment variable based search on
-unix platforms is supported::
-
- py.path.local.sysfind('svn')
-
-which returns the first path whose ``basename`` matches ``svn``.
-In principle, `sysfind` deploys platform specific algorithms
-to perform the search. On Windows, for example, it may look
-at the registry (XXX).
-
-To make the story complete, we allow to pass in a second ``checker``
-argument that is called for each found executable. For example, if
-you have multiple binaries available you may want to select the
-right version::
-
- def mysvn(p):
- """ check that the given svn binary has version 1.1. """
- line = p.execute('--version'').readlines()[0]
- if line.find('version 1.1'):
- return p
- binsvn = py.path.local.sysfind('svn', checker=mysvn)
-
-
-Cross-Python Version compatibility helpers
-=============================================
-
-The ``py.builtin`` namespace provides a number of helpers that help to write python code compatible across Python interpreters, mainly Python2 and Python3. Type ``help(py.builtin)`` on a Python prompt for a the selection of builtins.
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/path.txt b/tests/wpt/web-platform-tests/tools/py/doc/path.txt
deleted file mode 100644
index 837c1d19272..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/doc/path.txt
+++ /dev/null
@@ -1,260 +0,0 @@
-=======
-py.path
-=======
-
-The 'py' lib provides a uniform high-level api to deal with filesystems
-and filesystem-like interfaces: ``py.path``. It aims to offer a central
-object to fs-like object trees (reading from and writing to files, adding
-files/directories, examining the types and structure, etc.), and out-of-the-box
-provides a number of implementations of this API.
-
-py.path.local - local file system path
-===============================================
-
-.. _`local`:
-
-basic interactive example
--------------------------------------
-
-The first and most obvious of the implementations is a wrapper around a local
-filesystem. It's just a bit nicer in usage than the regular Python APIs, and
-of course all the functionality is bundled together rather than spread over a
-number of modules.
-
-Example usage, here we use the ``py.test.ensuretemp()`` function to create
-a ``py.path.local`` object for us (which wraps a directory):
-
-.. sourcecode:: pycon
-
- >>> import py
- >>> temppath = py.test.ensuretemp('py.path_documentation')
- >>> foopath = temppath.join('foo') # get child 'foo' (lazily)
- >>> foopath.check() # check if child 'foo' exists
- False
- >>> foopath.write('bar') # write some data to it
- >>> foopath.check()
- True
- >>> foopath.read()
- 'bar'
- >>> foofile = foopath.open() # return a 'real' file object
- >>> foofile.read(1)
- 'b'
-
-reference documentation
----------------------------------
-
-.. autoclass:: py._path.local.LocalPath
- :members:
- :inherited-members:
-
-``py.path.svnurl`` and ``py.path.svnwc``
-==================================================
-
-Two other ``py.path`` implementations that the py lib provides wrap the
-popular `Subversion`_ revision control system: the first (called 'svnurl')
-by interfacing with a remote server, the second by wrapping a local checkout.
-Both allow you to access relatively advanced features such as metadata and
-versioning, and both in a way more user-friendly manner than existing other
-solutions.
-
-Some example usage of ``py.path.svnurl``:
-
-.. sourcecode:: pycon
-
- .. >>> import py
- .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
- >>> url = py.path.svnurl('http://codespeak.net/svn/py')
- >>> info = url.info()
- >>> info.kind
- 'dir'
- >>> firstentry = url.log()[-1]
- >>> import time
- >>> time.strftime('%Y-%m-%d', time.gmtime(firstentry.date))
- '2004-10-02'
-
-Example usage of ``py.path.svnwc``:
-
-.. sourcecode:: pycon
-
- .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
- >>> temp = py.test.ensuretemp('py.path_documentation')
- >>> wc = py.path.svnwc(temp.join('svnwc'))
- >>> wc.checkout('http://codespeak.net/svn/py/dist/py/path/local')
- >>> wc.join('local.py').check()
- True
-
-.. _`Subversion`: http://subversion.tigris.org/
-
-svn path related API reference
------------------------------------------
-
-.. autoclass:: py._path.svnwc.SvnWCCommandPath
- :members:
- :inherited-members:
-
-.. autoclass:: py._path.svnurl.SvnCommandPath
- :members:
- :inherited-members:
-
-.. autoclass:: py._path.svnwc.SvnAuth
- :members:
- :inherited-members:
-
-Common vs. specific API, Examples
-========================================
-
-All Path objects support a common set of operations, suitable
-for many use cases and allowing to transparently switch the
-path object within an application (e.g. from "local" to "svnwc").
-The common set includes functions such as `path.read()` to read all data
-from a file, `path.write()` to write data, `path.listdir()` to get a list
-of directory entries, `path.check()` to check if a node exists
-and is of a particular type, `path.join()` to get
-to a (grand)child, `path.visit()` to recursively walk through a node's
-children, etc. Only things that are not common on 'normal' filesystems (yet),
-such as handling metadata (e.g. the Subversion "properties") require
-using specific APIs.
-
-A quick 'cookbook' of small examples that will be useful 'in real life',
-which also presents parts of the 'common' API, and shows some non-common
-methods:
-
-Searching `.txt` files
---------------------------------
-
-Search for a particular string inside all files with a .txt extension in a
-specific directory.
-
-.. sourcecode:: pycon
-
- >>> dirpath = temppath.ensure('testdir', dir=True)
- >>> dirpath.join('textfile1.txt').write('foo bar baz')
- >>> dirpath.join('textfile2.txt').write('frob bar spam eggs')
- >>> subdir = dirpath.ensure('subdir', dir=True)
- >>> subdir.join('textfile1.txt').write('foo baz')
- >>> subdir.join('textfile2.txt').write('spam eggs spam foo bar spam')
- >>> results = []
- >>> for fpath in dirpath.visit('*.txt'):
- ... if 'bar' in fpath.read():
- ... results.append(fpath.basename)
- >>> results.sort()
- >>> results
- ['textfile1.txt', 'textfile2.txt', 'textfile2.txt']
-
-Working with Paths
-----------------------------
-
-This example shows the ``py.path`` features to deal with
-filesystem paths Note that the filesystem is never touched,
-all operations are performed on a string level (so the paths
-don't have to exist, either):
-
-.. sourcecode:: pycon
-
- >>> p1 = py.path.local('/foo/bar')
- >>> p2 = p1.join('baz/qux')
- >>> p2 == py.path.local('/foo/bar/baz/qux')
- True
- >>> sep = py.path.local.sep
- >>> p2.relto(p1).replace(sep, '/') # os-specific path sep in the string
- 'baz/qux'
- >>> p2.bestrelpath(p1).replace(sep, '/')
- '../..'
- >>> p2.join(p2.bestrelpath(p1)) == p1
- True
- >>> p3 = p1 / 'baz/qux' # the / operator allows joining, too
- >>> p2 == p3
- True
- >>> p4 = p1 + ".py"
- >>> p4.basename == "bar.py"
- True
- >>> p4.ext == ".py"
- True
- >>> p4.purebasename == "bar"
- True
-
-This should be possible on every implementation of ``py.path``, so
-regardless of whether the implementation wraps a UNIX filesystem, a Windows
-one, or a database or object tree, these functions should be available (each
-with their own notion of path seperators and dealing with conversions, etc.).
-
-Checking path types
--------------------------------
-
-Now we will show a bit about the powerful 'check()' method on paths, which
-allows you to check whether a file exists, what type it is, etc.:
-
-.. sourcecode:: pycon
-
- >>> file1 = temppath.join('file1')
- >>> file1.check() # does it exist?
- False
- >>> file1 = file1.ensure(file=True) # 'touch' the file
- >>> file1.check()
- True
- >>> file1.check(dir=True) # is it a dir?
- False
- >>> file1.check(file=True) # or a file?
- True
- >>> file1.check(ext='.txt') # check the extension
- False
- >>> textfile = temppath.ensure('text.txt', file=True)
- >>> textfile.check(ext='.txt')
- True
- >>> file1.check(basename='file1') # we can use all the path's properties here
- True
-
-Setting svn-properties
---------------------------------
-
-As an example of 'uncommon' methods, we'll show how to read and write
-properties in an ``py.path.svnwc`` instance:
-
-.. sourcecode:: pycon
-
- .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
- >>> wc.propget('foo')
- ''
- >>> wc.propset('foo', 'bar')
- >>> wc.propget('foo')
- 'bar'
- >>> len(wc.status().prop_modified) # our own props
- 1
- >>> msg = wc.revert() # roll back our changes
- >>> len(wc.status().prop_modified)
- 0
-
-SVN authentication
-----------------------------
-
-Some uncommon functionality can also be provided as extensions, such as SVN
-authentication:
-
-.. sourcecode:: pycon
-
- .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
- >>> auth = py.path.SvnAuth('anonymous', 'user', cache_auth=False,
- ... interactive=False)
- >>> wc.auth = auth
- >>> wc.update() # this should work
- >>> path = wc.ensure('thisshouldnotexist.txt')
- >>> try:
- ... path.commit('testing')
- ... except py.process.cmdexec.Error, e:
- ... pass
- >>> 'authorization failed' in str(e)
- True
-
-Known problems / limitations
-===================================
-
-* The SVN path objects require the "svn" command line,
- there is currently no support for python bindings.
- Parsing the svn output can lead to problems, particularly
- regarding if you have a non-english "locales" setting.
-
-* While the path objects basically work on windows,
- there is no attention yet on making unicode paths
- work or deal with the famous "8.3" filename issues.
-
-
diff --git a/tests/wpt/web-platform-tests/tools/py/py/__init__.py b/tests/wpt/web-platform-tests/tools/py/py/__init__.py
deleted file mode 100644
index bdb9aa2181f..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/__init__.py
+++ /dev/null
@@ -1,150 +0,0 @@
-"""
-py.test and pylib: rapid testing and development utils
-
-this module uses apipkg.py for lazy-loading sub modules
-and classes. The initpkg-dictionary below specifies
-name->value mappings where value can be another namespace
-dictionary or an import path.
-
-(c) Holger Krekel and others, 2004-2014
-"""
-__version__ = '1.4.31'
-
-from py import _apipkg
-
-# so that py.error.* instances are picklable
-import sys
-sys.modules['py.error'] = _apipkg.AliasModule("py.error", "py._error", 'error')
-
-_apipkg.initpkg(__name__, attr={'_apipkg': _apipkg}, exportdefs={
- # access to all standard lib modules
- 'std': '._std:std',
- # access to all posix errno's as classes
- 'error': '._error:error',
-
- '_pydir' : '.__metainfo:pydir',
- 'version': 'py:__version__', # backward compatibility
-
- # pytest-2.0 has a flat namespace, we use alias modules
- # to keep old references compatible
- 'test' : 'pytest',
- 'test.collect' : 'pytest',
- 'test.cmdline' : 'pytest',
-
- # hook into the top-level standard library
- 'process' : {
- '__doc__' : '._process:__doc__',
- 'cmdexec' : '._process.cmdexec:cmdexec',
- 'kill' : '._process.killproc:kill',
- 'ForkedFunc' : '._process.forkedfunc:ForkedFunc',
- },
-
- 'apipkg' : {
- 'initpkg' : '._apipkg:initpkg',
- 'ApiModule' : '._apipkg:ApiModule',
- },
-
- 'iniconfig' : {
- 'IniConfig' : '._iniconfig:IniConfig',
- 'ParseError' : '._iniconfig:ParseError',
- },
-
- 'path' : {
- '__doc__' : '._path:__doc__',
- 'svnwc' : '._path.svnwc:SvnWCCommandPath',
- 'svnurl' : '._path.svnurl:SvnCommandPath',
- 'local' : '._path.local:LocalPath',
- 'SvnAuth' : '._path.svnwc:SvnAuth',
- },
-
- # python inspection/code-generation API
- 'code' : {
- '__doc__' : '._code:__doc__',
- 'compile' : '._code.source:compile_',
- 'Source' : '._code.source:Source',
- 'Code' : '._code.code:Code',
- 'Frame' : '._code.code:Frame',
- 'ExceptionInfo' : '._code.code:ExceptionInfo',
- 'Traceback' : '._code.code:Traceback',
- 'getfslineno' : '._code.source:getfslineno',
- 'getrawcode' : '._code.code:getrawcode',
- 'patch_builtins' : '._code.code:patch_builtins',
- 'unpatch_builtins' : '._code.code:unpatch_builtins',
- '_AssertionError' : '._code.assertion:AssertionError',
- '_reinterpret_old' : '._code.assertion:reinterpret_old',
- '_reinterpret' : '._code.assertion:reinterpret',
- '_reprcompare' : '._code.assertion:_reprcompare',
- '_format_explanation' : '._code.assertion:_format_explanation',
- },
-
- # backports and additions of builtins
- 'builtin' : {
- '__doc__' : '._builtin:__doc__',
- 'enumerate' : '._builtin:enumerate',
- 'reversed' : '._builtin:reversed',
- 'sorted' : '._builtin:sorted',
- 'any' : '._builtin:any',
- 'all' : '._builtin:all',
- 'set' : '._builtin:set',
- 'frozenset' : '._builtin:frozenset',
- 'BaseException' : '._builtin:BaseException',
- 'GeneratorExit' : '._builtin:GeneratorExit',
- '_sysex' : '._builtin:_sysex',
- 'print_' : '._builtin:print_',
- '_reraise' : '._builtin:_reraise',
- '_tryimport' : '._builtin:_tryimport',
- 'exec_' : '._builtin:exec_',
- '_basestring' : '._builtin:_basestring',
- '_totext' : '._builtin:_totext',
- '_isbytes' : '._builtin:_isbytes',
- '_istext' : '._builtin:_istext',
- '_getimself' : '._builtin:_getimself',
- '_getfuncdict' : '._builtin:_getfuncdict',
- '_getcode' : '._builtin:_getcode',
- 'builtins' : '._builtin:builtins',
- 'execfile' : '._builtin:execfile',
- 'callable' : '._builtin:callable',
- 'bytes' : '._builtin:bytes',
- 'text' : '._builtin:text',
- },
-
- # input-output helping
- 'io' : {
- '__doc__' : '._io:__doc__',
- 'dupfile' : '._io.capture:dupfile',
- 'TextIO' : '._io.capture:TextIO',
- 'BytesIO' : '._io.capture:BytesIO',
- 'FDCapture' : '._io.capture:FDCapture',
- 'StdCapture' : '._io.capture:StdCapture',
- 'StdCaptureFD' : '._io.capture:StdCaptureFD',
- 'TerminalWriter' : '._io.terminalwriter:TerminalWriter',
- 'ansi_print' : '._io.terminalwriter:ansi_print',
- 'get_terminal_width' : '._io.terminalwriter:get_terminal_width',
- 'saferepr' : '._io.saferepr:saferepr',
- },
-
- # small and mean xml/html generation
- 'xml' : {
- '__doc__' : '._xmlgen:__doc__',
- 'html' : '._xmlgen:html',
- 'Tag' : '._xmlgen:Tag',
- 'raw' : '._xmlgen:raw',
- 'Namespace' : '._xmlgen:Namespace',
- 'escape' : '._xmlgen:escape',
- },
-
- 'log' : {
- # logging API ('producers' and 'consumers' connected via keywords)
- '__doc__' : '._log:__doc__',
- '_apiwarn' : '._log.warning:_apiwarn',
- 'Producer' : '._log.log:Producer',
- 'setconsumer' : '._log.log:setconsumer',
- '_setstate' : '._log.log:setstate',
- '_getstate' : '._log.log:getstate',
- 'Path' : '._log.log:Path',
- 'STDOUT' : '._log.log:STDOUT',
- 'STDERR' : '._log.log:STDERR',
- 'Syslog' : '._log.log:Syslog',
- },
-
-})
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_apipkg.py b/tests/wpt/web-platform-tests/tools/py/py/_apipkg.py
deleted file mode 100644
index a73b8f6d0bc..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_apipkg.py
+++ /dev/null
@@ -1,181 +0,0 @@
-"""
-apipkg: control the exported namespace of a python package.
-
-see http://pypi.python.org/pypi/apipkg
-
-(c) holger krekel, 2009 - MIT license
-"""
-import os
-import sys
-from types import ModuleType
-
-__version__ = '1.3.dev'
-
-def _py_abspath(path):
- """
- special version of abspath
- that will leave paths from jython jars alone
- """
- if path.startswith('__pyclasspath__'):
-
- return path
- else:
- return os.path.abspath(path)
-
-def initpkg(pkgname, exportdefs, attr=dict()):
- """ initialize given package from the export definitions. """
- oldmod = sys.modules.get(pkgname)
- d = {}
- f = getattr(oldmod, '__file__', None)
- if f:
- f = _py_abspath(f)
- d['__file__'] = f
- if hasattr(oldmod, '__version__'):
- d['__version__'] = oldmod.__version__
- if hasattr(oldmod, '__loader__'):
- d['__loader__'] = oldmod.__loader__
- if hasattr(oldmod, '__path__'):
- d['__path__'] = [_py_abspath(p) for p in oldmod.__path__]
- if '__doc__' not in exportdefs and getattr(oldmod, '__doc__', None):
- d['__doc__'] = oldmod.__doc__
- d.update(attr)
- if hasattr(oldmod, "__dict__"):
- oldmod.__dict__.update(d)
- mod = ApiModule(pkgname, exportdefs, implprefix=pkgname, attr=d)
- sys.modules[pkgname] = mod
-
-def importobj(modpath, attrname):
- module = __import__(modpath, None, None, ['__doc__'])
- if not attrname:
- return module
-
- retval = module
- names = attrname.split(".")
- for x in names:
- retval = getattr(retval, x)
- return retval
-
-class ApiModule(ModuleType):
- def __docget(self):
- try:
- return self.__doc
- except AttributeError:
- if '__doc__' in self.__map__:
- return self.__makeattr('__doc__')
- def __docset(self, value):
- self.__doc = value
- __doc__ = property(__docget, __docset)
-
- def __init__(self, name, importspec, implprefix=None, attr=None):
- self.__name__ = name
- self.__all__ = [x for x in importspec if x != '__onfirstaccess__']
- self.__map__ = {}
- self.__implprefix__ = implprefix or name
- if attr:
- for name, val in attr.items():
- # print "setting", self.__name__, name, val
- setattr(self, name, val)
- for name, importspec in importspec.items():
- if isinstance(importspec, dict):
- subname = '%s.%s' % (self.__name__, name)
- apimod = ApiModule(subname, importspec, implprefix)
- sys.modules[subname] = apimod
- setattr(self, name, apimod)
- else:
- parts = importspec.split(':')
- modpath = parts.pop(0)
- attrname = parts and parts[0] or ""
- if modpath[0] == '.':
- modpath = implprefix + modpath
-
- if not attrname:
- subname = '%s.%s' % (self.__name__, name)
- apimod = AliasModule(subname, modpath)
- sys.modules[subname] = apimod
- if '.' not in name:
- setattr(self, name, apimod)
- else:
- self.__map__[name] = (modpath, attrname)
-
- def __repr__(self):
- l = []
- if hasattr(self, '__version__'):
- l.append("version=" + repr(self.__version__))
- if hasattr(self, '__file__'):
- l.append('from ' + repr(self.__file__))
- if l:
- return '<ApiModule %r %s>' % (self.__name__, " ".join(l))
- return '<ApiModule %r>' % (self.__name__,)
-
- def __makeattr(self, name):
- """lazily compute value for name or raise AttributeError if unknown."""
- # print "makeattr", self.__name__, name
- target = None
- if '__onfirstaccess__' in self.__map__:
- target = self.__map__.pop('__onfirstaccess__')
- importobj(*target)()
- try:
- modpath, attrname = self.__map__[name]
- except KeyError:
- if target is not None and name != '__onfirstaccess__':
- # retry, onfirstaccess might have set attrs
- return getattr(self, name)
- raise AttributeError(name)
- else:
- result = importobj(modpath, attrname)
- setattr(self, name, result)
- try:
- del self.__map__[name]
- except KeyError:
- pass # in a recursive-import situation a double-del can happen
- return result
-
- __getattr__ = __makeattr
-
- def __dict__(self):
- # force all the content of the module to be loaded when __dict__ is read
- dictdescr = ModuleType.__dict__['__dict__']
- dict = dictdescr.__get__(self)
- if dict is not None:
- hasattr(self, 'some')
- for name in self.__all__:
- try:
- self.__makeattr(name)
- except AttributeError:
- pass
- return dict
- __dict__ = property(__dict__)
-
-
-def AliasModule(modname, modpath, attrname=None):
- mod = []
-
- def getmod():
- if not mod:
- x = importobj(modpath, None)
- if attrname is not None:
- x = getattr(x, attrname)
- mod.append(x)
- return mod[0]
-
- class AliasModule(ModuleType):
-
- def __repr__(self):
- x = modpath
- if attrname:
- x += "." + attrname
- return '<AliasModule %r for %r>' % (modname, x)
-
- def __getattribute__(self, name):
- try:
- return getattr(getmod(), name)
- except ImportError:
- return None
-
- def __setattr__(self, name, value):
- setattr(getmod(), name, value)
-
- def __delattr__(self, name):
- delattr(getmod(), name)
-
- return AliasModule(str(modname))
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_code/_assertionnew.py b/tests/wpt/web-platform-tests/tools/py/py/_code/_assertionnew.py
deleted file mode 100644
index afb1b31ff05..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_code/_assertionnew.py
+++ /dev/null
@@ -1,339 +0,0 @@
-"""
-Find intermediate evalutation results in assert statements through builtin AST.
-This should replace _assertionold.py eventually.
-"""
-
-import sys
-import ast
-
-import py
-from py._code.assertion import _format_explanation, BuiltinAssertionError
-
-
-if sys.platform.startswith("java") and sys.version_info < (2, 5, 2):
- # See http://bugs.jython.org/issue1497
- _exprs = ("BoolOp", "BinOp", "UnaryOp", "Lambda", "IfExp", "Dict",
- "ListComp", "GeneratorExp", "Yield", "Compare", "Call",
- "Repr", "Num", "Str", "Attribute", "Subscript", "Name",
- "List", "Tuple")
- _stmts = ("FunctionDef", "ClassDef", "Return", "Delete", "Assign",
- "AugAssign", "Print", "For", "While", "If", "With", "Raise",
- "TryExcept", "TryFinally", "Assert", "Import", "ImportFrom",
- "Exec", "Global", "Expr", "Pass", "Break", "Continue")
- _expr_nodes = set(getattr(ast, name) for name in _exprs)
- _stmt_nodes = set(getattr(ast, name) for name in _stmts)
- def _is_ast_expr(node):
- return node.__class__ in _expr_nodes
- def _is_ast_stmt(node):
- return node.__class__ in _stmt_nodes
-else:
- def _is_ast_expr(node):
- return isinstance(node, ast.expr)
- def _is_ast_stmt(node):
- return isinstance(node, ast.stmt)
-
-
-class Failure(Exception):
- """Error found while interpreting AST."""
-
- def __init__(self, explanation=""):
- self.cause = sys.exc_info()
- self.explanation = explanation
-
-
-def interpret(source, frame, should_fail=False):
- mod = ast.parse(source)
- visitor = DebugInterpreter(frame)
- try:
- visitor.visit(mod)
- except Failure:
- failure = sys.exc_info()[1]
- return getfailure(failure)
- if should_fail:
- return ("(assertion failed, but when it was re-run for "
- "printing intermediate values, it did not fail. Suggestions: "
- "compute assert expression before the assert or use --no-assert)")
-
-def run(offending_line, frame=None):
- if frame is None:
- frame = py.code.Frame(sys._getframe(1))
- return interpret(offending_line, frame)
-
-def getfailure(failure):
- explanation = _format_explanation(failure.explanation)
- value = failure.cause[1]
- if str(value):
- lines = explanation.splitlines()
- if not lines:
- lines.append("")
- lines[0] += " << %s" % (value,)
- explanation = "\n".join(lines)
- text = "%s: %s" % (failure.cause[0].__name__, explanation)
- if text.startswith("AssertionError: assert "):
- text = text[16:]
- return text
-
-
-operator_map = {
- ast.BitOr : "|",
- ast.BitXor : "^",
- ast.BitAnd : "&",
- ast.LShift : "<<",
- ast.RShift : ">>",
- ast.Add : "+",
- ast.Sub : "-",
- ast.Mult : "*",
- ast.Div : "/",
- ast.FloorDiv : "//",
- ast.Mod : "%",
- ast.Eq : "==",
- ast.NotEq : "!=",
- ast.Lt : "<",
- ast.LtE : "<=",
- ast.Gt : ">",
- ast.GtE : ">=",
- ast.Pow : "**",
- ast.Is : "is",
- ast.IsNot : "is not",
- ast.In : "in",
- ast.NotIn : "not in"
-}
-
-unary_map = {
- ast.Not : "not %s",
- ast.Invert : "~%s",
- ast.USub : "-%s",
- ast.UAdd : "+%s"
-}
-
-
-class DebugInterpreter(ast.NodeVisitor):
- """Interpret AST nodes to gleam useful debugging information. """
-
- def __init__(self, frame):
- self.frame = frame
-
- def generic_visit(self, node):
- # Fallback when we don't have a special implementation.
- if _is_ast_expr(node):
- mod = ast.Expression(node)
- co = self._compile(mod)
- try:
- result = self.frame.eval(co)
- except Exception:
- raise Failure()
- explanation = self.frame.repr(result)
- return explanation, result
- elif _is_ast_stmt(node):
- mod = ast.Module([node])
- co = self._compile(mod, "exec")
- try:
- self.frame.exec_(co)
- except Exception:
- raise Failure()
- return None, None
- else:
- raise AssertionError("can't handle %s" %(node,))
-
- def _compile(self, source, mode="eval"):
- return compile(source, "<assertion interpretation>", mode)
-
- def visit_Expr(self, expr):
- return self.visit(expr.value)
-
- def visit_Module(self, mod):
- for stmt in mod.body:
- self.visit(stmt)
-
- def visit_Name(self, name):
- explanation, result = self.generic_visit(name)
- # See if the name is local.
- source = "%r in locals() is not globals()" % (name.id,)
- co = self._compile(source)
- try:
- local = self.frame.eval(co)
- except Exception:
- # have to assume it isn't
- local = False
- if not local:
- return name.id, result
- return explanation, result
-
- def visit_Compare(self, comp):
- left = comp.left
- left_explanation, left_result = self.visit(left)
- for op, next_op in zip(comp.ops, comp.comparators):
- next_explanation, next_result = self.visit(next_op)
- op_symbol = operator_map[op.__class__]
- explanation = "%s %s %s" % (left_explanation, op_symbol,
- next_explanation)
- source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,)
- co = self._compile(source)
- try:
- result = self.frame.eval(co, __exprinfo_left=left_result,
- __exprinfo_right=next_result)
- except Exception:
- raise Failure(explanation)
- try:
- if not result:
- break
- except KeyboardInterrupt:
- raise
- except:
- break
- left_explanation, left_result = next_explanation, next_result
-
- rcomp = py.code._reprcompare
- if rcomp:
- res = rcomp(op_symbol, left_result, next_result)
- if res:
- explanation = res
- return explanation, result
-
- def visit_BoolOp(self, boolop):
- is_or = isinstance(boolop.op, ast.Or)
- explanations = []
- for operand in boolop.values:
- explanation, result = self.visit(operand)
- explanations.append(explanation)
- if result == is_or:
- break
- name = is_or and " or " or " and "
- explanation = "(" + name.join(explanations) + ")"
- return explanation, result
-
- def visit_UnaryOp(self, unary):
- pattern = unary_map[unary.op.__class__]
- operand_explanation, operand_result = self.visit(unary.operand)
- explanation = pattern % (operand_explanation,)
- co = self._compile(pattern % ("__exprinfo_expr",))
- try:
- result = self.frame.eval(co, __exprinfo_expr=operand_result)
- except Exception:
- raise Failure(explanation)
- return explanation, result
-
- def visit_BinOp(self, binop):
- left_explanation, left_result = self.visit(binop.left)
- right_explanation, right_result = self.visit(binop.right)
- symbol = operator_map[binop.op.__class__]
- explanation = "(%s %s %s)" % (left_explanation, symbol,
- right_explanation)
- source = "__exprinfo_left %s __exprinfo_right" % (symbol,)
- co = self._compile(source)
- try:
- result = self.frame.eval(co, __exprinfo_left=left_result,
- __exprinfo_right=right_result)
- except Exception:
- raise Failure(explanation)
- return explanation, result
-
- def visit_Call(self, call):
- func_explanation, func = self.visit(call.func)
- arg_explanations = []
- ns = {"__exprinfo_func" : func}
- arguments = []
- for arg in call.args:
- arg_explanation, arg_result = self.visit(arg)
- arg_name = "__exprinfo_%s" % (len(ns),)
- ns[arg_name] = arg_result
- arguments.append(arg_name)
- arg_explanations.append(arg_explanation)
- for keyword in call.keywords:
- arg_explanation, arg_result = self.visit(keyword.value)
- arg_name = "__exprinfo_%s" % (len(ns),)
- ns[arg_name] = arg_result
- keyword_source = "%s=%%s" % (keyword.arg)
- arguments.append(keyword_source % (arg_name,))
- arg_explanations.append(keyword_source % (arg_explanation,))
- if call.starargs:
- arg_explanation, arg_result = self.visit(call.starargs)
- arg_name = "__exprinfo_star"
- ns[arg_name] = arg_result
- arguments.append("*%s" % (arg_name,))
- arg_explanations.append("*%s" % (arg_explanation,))
- if call.kwargs:
- arg_explanation, arg_result = self.visit(call.kwargs)
- arg_name = "__exprinfo_kwds"
- ns[arg_name] = arg_result
- arguments.append("**%s" % (arg_name,))
- arg_explanations.append("**%s" % (arg_explanation,))
- args_explained = ", ".join(arg_explanations)
- explanation = "%s(%s)" % (func_explanation, args_explained)
- args = ", ".join(arguments)
- source = "__exprinfo_func(%s)" % (args,)
- co = self._compile(source)
- try:
- result = self.frame.eval(co, **ns)
- except Exception:
- raise Failure(explanation)
- pattern = "%s\n{%s = %s\n}"
- rep = self.frame.repr(result)
- explanation = pattern % (rep, rep, explanation)
- return explanation, result
-
- def _is_builtin_name(self, name):
- pattern = "%r not in globals() and %r not in locals()"
- source = pattern % (name.id, name.id)
- co = self._compile(source)
- try:
- return self.frame.eval(co)
- except Exception:
- return False
-
- def visit_Attribute(self, attr):
- if not isinstance(attr.ctx, ast.Load):
- return self.generic_visit(attr)
- source_explanation, source_result = self.visit(attr.value)
- explanation = "%s.%s" % (source_explanation, attr.attr)
- source = "__exprinfo_expr.%s" % (attr.attr,)
- co = self._compile(source)
- try:
- result = self.frame.eval(co, __exprinfo_expr=source_result)
- except Exception:
- raise Failure(explanation)
- explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result),
- self.frame.repr(result),
- source_explanation, attr.attr)
- # Check if the attr is from an instance.
- source = "%r in getattr(__exprinfo_expr, '__dict__', {})"
- source = source % (attr.attr,)
- co = self._compile(source)
- try:
- from_instance = self.frame.eval(co, __exprinfo_expr=source_result)
- except Exception:
- from_instance = True
- if from_instance:
- rep = self.frame.repr(result)
- pattern = "%s\n{%s = %s\n}"
- explanation = pattern % (rep, rep, explanation)
- return explanation, result
-
- def visit_Assert(self, assrt):
- test_explanation, test_result = self.visit(assrt.test)
- if test_explanation.startswith("False\n{False =") and \
- test_explanation.endswith("\n"):
- test_explanation = test_explanation[15:-2]
- explanation = "assert %s" % (test_explanation,)
- if not test_result:
- try:
- raise BuiltinAssertionError
- except Exception:
- raise Failure(explanation)
- return explanation, test_result
-
- def visit_Assign(self, assign):
- value_explanation, value_result = self.visit(assign.value)
- explanation = "... = %s" % (value_explanation,)
- name = ast.Name("__exprinfo_expr", ast.Load(),
- lineno=assign.value.lineno,
- col_offset=assign.value.col_offset)
- new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno,
- col_offset=assign.col_offset)
- mod = ast.Module([new_assign])
- co = self._compile(mod, "exec")
- try:
- self.frame.exec_(co, __exprinfo_expr=value_result)
- except Exception:
- raise Failure(explanation)
- return explanation, value_result
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_code/_assertionold.py b/tests/wpt/web-platform-tests/tools/py/py/_code/_assertionold.py
deleted file mode 100644
index 4e81fb3ef6e..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_code/_assertionold.py
+++ /dev/null
@@ -1,555 +0,0 @@
-import py
-import sys, inspect
-from compiler import parse, ast, pycodegen
-from py._code.assertion import BuiltinAssertionError, _format_explanation
-
-passthroughex = py.builtin._sysex
-
-class Failure:
- def __init__(self, node):
- self.exc, self.value, self.tb = sys.exc_info()
- self.node = node
-
-class View(object):
- """View base class.
-
- If C is a subclass of View, then C(x) creates a proxy object around
- the object x. The actual class of the proxy is not C in general,
- but a *subclass* of C determined by the rules below. To avoid confusion
- we call view class the class of the proxy (a subclass of C, so of View)
- and object class the class of x.
-
- Attributes and methods not found in the proxy are automatically read on x.
- Other operations like setting attributes are performed on the proxy, as
- determined by its view class. The object x is available from the proxy
- as its __obj__ attribute.
-
- The view class selection is determined by the __view__ tuples and the
- optional __viewkey__ method. By default, the selected view class is the
- most specific subclass of C whose __view__ mentions the class of x.
- If no such subclass is found, the search proceeds with the parent
- object classes. For example, C(True) will first look for a subclass
- of C with __view__ = (..., bool, ...) and only if it doesn't find any
- look for one with __view__ = (..., int, ...), and then ..., object,...
- If everything fails the class C itself is considered to be the default.
-
- Alternatively, the view class selection can be driven by another aspect
- of the object x, instead of the class of x, by overriding __viewkey__.
- See last example at the end of this module.
- """
-
- _viewcache = {}
- __view__ = ()
-
- def __new__(rootclass, obj, *args, **kwds):
- self = object.__new__(rootclass)
- self.__obj__ = obj
- self.__rootclass__ = rootclass
- key = self.__viewkey__()
- try:
- self.__class__ = self._viewcache[key]
- except KeyError:
- self.__class__ = self._selectsubclass(key)
- return self
-
- def __getattr__(self, attr):
- # attributes not found in the normal hierarchy rooted on View
- # are looked up in the object's real class
- return getattr(self.__obj__, attr)
-
- def __viewkey__(self):
- return self.__obj__.__class__
-
- def __matchkey__(self, key, subclasses):
- if inspect.isclass(key):
- keys = inspect.getmro(key)
- else:
- keys = [key]
- for key in keys:
- result = [C for C in subclasses if key in C.__view__]
- if result:
- return result
- return []
-
- def _selectsubclass(self, key):
- subclasses = list(enumsubclasses(self.__rootclass__))
- for C in subclasses:
- if not isinstance(C.__view__, tuple):
- C.__view__ = (C.__view__,)
- choices = self.__matchkey__(key, subclasses)
- if not choices:
- return self.__rootclass__
- elif len(choices) == 1:
- return choices[0]
- else:
- # combine the multiple choices
- return type('?', tuple(choices), {})
-
- def __repr__(self):
- return '%s(%r)' % (self.__rootclass__.__name__, self.__obj__)
-
-
-def enumsubclasses(cls):
- for subcls in cls.__subclasses__():
- for subsubclass in enumsubclasses(subcls):
- yield subsubclass
- yield cls
-
-
-class Interpretable(View):
- """A parse tree node with a few extra methods."""
- explanation = None
-
- def is_builtin(self, frame):
- return False
-
- def eval(self, frame):
- # fall-back for unknown expression nodes
- try:
- expr = ast.Expression(self.__obj__)
- expr.filename = '<eval>'
- self.__obj__.filename = '<eval>'
- co = pycodegen.ExpressionCodeGenerator(expr).getCode()
- result = frame.eval(co)
- except passthroughex:
- raise
- except:
- raise Failure(self)
- self.result = result
- self.explanation = self.explanation or frame.repr(self.result)
-
- def run(self, frame):
- # fall-back for unknown statement nodes
- try:
- expr = ast.Module(None, ast.Stmt([self.__obj__]))
- expr.filename = '<run>'
- co = pycodegen.ModuleCodeGenerator(expr).getCode()
- frame.exec_(co)
- except passthroughex:
- raise
- except:
- raise Failure(self)
-
- def nice_explanation(self):
- return _format_explanation(self.explanation)
-
-
-class Name(Interpretable):
- __view__ = ast.Name
-
- def is_local(self, frame):
- source = '%r in locals() is not globals()' % self.name
- try:
- return frame.is_true(frame.eval(source))
- except passthroughex:
- raise
- except:
- return False
-
- def is_global(self, frame):
- source = '%r in globals()' % self.name
- try:
- return frame.is_true(frame.eval(source))
- except passthroughex:
- raise
- except:
- return False
-
- def is_builtin(self, frame):
- source = '%r not in locals() and %r not in globals()' % (
- self.name, self.name)
- try:
- return frame.is_true(frame.eval(source))
- except passthroughex:
- raise
- except:
- return False
-
- def eval(self, frame):
- super(Name, self).eval(frame)
- if not self.is_local(frame):
- self.explanation = self.name
-
-class Compare(Interpretable):
- __view__ = ast.Compare
-
- def eval(self, frame):
- expr = Interpretable(self.expr)
- expr.eval(frame)
- for operation, expr2 in self.ops:
- if hasattr(self, 'result'):
- # shortcutting in chained expressions
- if not frame.is_true(self.result):
- break
- expr2 = Interpretable(expr2)
- expr2.eval(frame)
- self.explanation = "%s %s %s" % (
- expr.explanation, operation, expr2.explanation)
- source = "__exprinfo_left %s __exprinfo_right" % operation
- try:
- self.result = frame.eval(source,
- __exprinfo_left=expr.result,
- __exprinfo_right=expr2.result)
- except passthroughex:
- raise
- except:
- raise Failure(self)
- expr = expr2
-
-class And(Interpretable):
- __view__ = ast.And
-
- def eval(self, frame):
- explanations = []
- for expr in self.nodes:
- expr = Interpretable(expr)
- expr.eval(frame)
- explanations.append(expr.explanation)
- self.result = expr.result
- if not frame.is_true(expr.result):
- break
- self.explanation = '(' + ' and '.join(explanations) + ')'
-
-class Or(Interpretable):
- __view__ = ast.Or
-
- def eval(self, frame):
- explanations = []
- for expr in self.nodes:
- expr = Interpretable(expr)
- expr.eval(frame)
- explanations.append(expr.explanation)
- self.result = expr.result
- if frame.is_true(expr.result):
- break
- self.explanation = '(' + ' or '.join(explanations) + ')'
-
-
-# == Unary operations ==
-keepalive = []
-for astclass, astpattern in {
- ast.Not : 'not __exprinfo_expr',
- ast.Invert : '(~__exprinfo_expr)',
- }.items():
-
- class UnaryArith(Interpretable):
- __view__ = astclass
-
- def eval(self, frame, astpattern=astpattern):
- expr = Interpretable(self.expr)
- expr.eval(frame)
- self.explanation = astpattern.replace('__exprinfo_expr',
- expr.explanation)
- try:
- self.result = frame.eval(astpattern,
- __exprinfo_expr=expr.result)
- except passthroughex:
- raise
- except:
- raise Failure(self)
-
- keepalive.append(UnaryArith)
-
-# == Binary operations ==
-for astclass, astpattern in {
- ast.Add : '(__exprinfo_left + __exprinfo_right)',
- ast.Sub : '(__exprinfo_left - __exprinfo_right)',
- ast.Mul : '(__exprinfo_left * __exprinfo_right)',
- ast.Div : '(__exprinfo_left / __exprinfo_right)',
- ast.Mod : '(__exprinfo_left % __exprinfo_right)',
- ast.Power : '(__exprinfo_left ** __exprinfo_right)',
- }.items():
-
- class BinaryArith(Interpretable):
- __view__ = astclass
-
- def eval(self, frame, astpattern=astpattern):
- left = Interpretable(self.left)
- left.eval(frame)
- right = Interpretable(self.right)
- right.eval(frame)
- self.explanation = (astpattern
- .replace('__exprinfo_left', left .explanation)
- .replace('__exprinfo_right', right.explanation))
- try:
- self.result = frame.eval(astpattern,
- __exprinfo_left=left.result,
- __exprinfo_right=right.result)
- except passthroughex:
- raise
- except:
- raise Failure(self)
-
- keepalive.append(BinaryArith)
-
-
-class CallFunc(Interpretable):
- __view__ = ast.CallFunc
-
- def is_bool(self, frame):
- source = 'isinstance(__exprinfo_value, bool)'
- try:
- return frame.is_true(frame.eval(source,
- __exprinfo_value=self.result))
- except passthroughex:
- raise
- except:
- return False
-
- def eval(self, frame):
- node = Interpretable(self.node)
- node.eval(frame)
- explanations = []
- vars = {'__exprinfo_fn': node.result}
- source = '__exprinfo_fn('
- for a in self.args:
- if isinstance(a, ast.Keyword):
- keyword = a.name
- a = a.expr
- else:
- keyword = None
- a = Interpretable(a)
- a.eval(frame)
- argname = '__exprinfo_%d' % len(vars)
- vars[argname] = a.result
- if keyword is None:
- source += argname + ','
- explanations.append(a.explanation)
- else:
- source += '%s=%s,' % (keyword, argname)
- explanations.append('%s=%s' % (keyword, a.explanation))
- if self.star_args:
- star_args = Interpretable(self.star_args)
- star_args.eval(frame)
- argname = '__exprinfo_star'
- vars[argname] = star_args.result
- source += '*' + argname + ','
- explanations.append('*' + star_args.explanation)
- if self.dstar_args:
- dstar_args = Interpretable(self.dstar_args)
- dstar_args.eval(frame)
- argname = '__exprinfo_kwds'
- vars[argname] = dstar_args.result
- source += '**' + argname + ','
- explanations.append('**' + dstar_args.explanation)
- self.explanation = "%s(%s)" % (
- node.explanation, ', '.join(explanations))
- if source.endswith(','):
- source = source[:-1]
- source += ')'
- try:
- self.result = frame.eval(source, **vars)
- except passthroughex:
- raise
- except:
- raise Failure(self)
- if not node.is_builtin(frame) or not self.is_bool(frame):
- r = frame.repr(self.result)
- self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
-
-class Getattr(Interpretable):
- __view__ = ast.Getattr
-
- def eval(self, frame):
- expr = Interpretable(self.expr)
- expr.eval(frame)
- source = '__exprinfo_expr.%s' % self.attrname
- try:
- self.result = frame.eval(source, __exprinfo_expr=expr.result)
- except passthroughex:
- raise
- except:
- raise Failure(self)
- self.explanation = '%s.%s' % (expr.explanation, self.attrname)
- # if the attribute comes from the instance, its value is interesting
- source = ('hasattr(__exprinfo_expr, "__dict__") and '
- '%r in __exprinfo_expr.__dict__' % self.attrname)
- try:
- from_instance = frame.is_true(
- frame.eval(source, __exprinfo_expr=expr.result))
- except passthroughex:
- raise
- except:
- from_instance = True
- if from_instance:
- r = frame.repr(self.result)
- self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
-
-# == Re-interpretation of full statements ==
-
-class Assert(Interpretable):
- __view__ = ast.Assert
-
- def run(self, frame):
- test = Interpretable(self.test)
- test.eval(frame)
- # simplify 'assert False where False = ...'
- if (test.explanation.startswith('False\n{False = ') and
- test.explanation.endswith('\n}')):
- test.explanation = test.explanation[15:-2]
- # print the result as 'assert <explanation>'
- self.result = test.result
- self.explanation = 'assert ' + test.explanation
- if not frame.is_true(test.result):
- try:
- raise BuiltinAssertionError
- except passthroughex:
- raise
- except:
- raise Failure(self)
-
-class Assign(Interpretable):
- __view__ = ast.Assign
-
- def run(self, frame):
- expr = Interpretable(self.expr)
- expr.eval(frame)
- self.result = expr.result
- self.explanation = '... = ' + expr.explanation
- # fall-back-run the rest of the assignment
- ass = ast.Assign(self.nodes, ast.Name('__exprinfo_expr'))
- mod = ast.Module(None, ast.Stmt([ass]))
- mod.filename = '<run>'
- co = pycodegen.ModuleCodeGenerator(mod).getCode()
- try:
- frame.exec_(co, __exprinfo_expr=expr.result)
- except passthroughex:
- raise
- except:
- raise Failure(self)
-
-class Discard(Interpretable):
- __view__ = ast.Discard
-
- def run(self, frame):
- expr = Interpretable(self.expr)
- expr.eval(frame)
- self.result = expr.result
- self.explanation = expr.explanation
-
-class Stmt(Interpretable):
- __view__ = ast.Stmt
-
- def run(self, frame):
- for stmt in self.nodes:
- stmt = Interpretable(stmt)
- stmt.run(frame)
-
-
-def report_failure(e):
- explanation = e.node.nice_explanation()
- if explanation:
- explanation = ", in: " + explanation
- else:
- explanation = ""
- sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation))
-
-def check(s, frame=None):
- if frame is None:
- frame = sys._getframe(1)
- frame = py.code.Frame(frame)
- expr = parse(s, 'eval')
- assert isinstance(expr, ast.Expression)
- node = Interpretable(expr.node)
- try:
- node.eval(frame)
- except passthroughex:
- raise
- except Failure:
- e = sys.exc_info()[1]
- report_failure(e)
- else:
- if not frame.is_true(node.result):
- sys.stderr.write("assertion failed: %s\n" % node.nice_explanation())
-
-
-###########################################################
-# API / Entry points
-# #########################################################
-
-def interpret(source, frame, should_fail=False):
- module = Interpretable(parse(source, 'exec').node)
- #print "got module", module
- if isinstance(frame, py.std.types.FrameType):
- frame = py.code.Frame(frame)
- try:
- module.run(frame)
- except Failure:
- e = sys.exc_info()[1]
- return getfailure(e)
- except passthroughex:
- raise
- except:
- import traceback
- traceback.print_exc()
- if should_fail:
- return ("(assertion failed, but when it was re-run for "
- "printing intermediate values, it did not fail. Suggestions: "
- "compute assert expression before the assert or use --nomagic)")
- else:
- return None
-
-def getmsg(excinfo):
- if isinstance(excinfo, tuple):
- excinfo = py.code.ExceptionInfo(excinfo)
- #frame, line = gettbline(tb)
- #frame = py.code.Frame(frame)
- #return interpret(line, frame)
-
- tb = excinfo.traceback[-1]
- source = str(tb.statement).strip()
- x = interpret(source, tb.frame, should_fail=True)
- if not isinstance(x, str):
- raise TypeError("interpret returned non-string %r" % (x,))
- return x
-
-def getfailure(e):
- explanation = e.node.nice_explanation()
- if str(e.value):
- lines = explanation.split('\n')
- lines[0] += " << %s" % (e.value,)
- explanation = '\n'.join(lines)
- text = "%s: %s" % (e.exc.__name__, explanation)
- if text.startswith('AssertionError: assert '):
- text = text[16:]
- return text
-
-def run(s, frame=None):
- if frame is None:
- frame = sys._getframe(1)
- frame = py.code.Frame(frame)
- module = Interpretable(parse(s, 'exec').node)
- try:
- module.run(frame)
- except Failure:
- e = sys.exc_info()[1]
- report_failure(e)
-
-
-if __name__ == '__main__':
- # example:
- def f():
- return 5
- def g():
- return 3
- def h(x):
- return 'never'
- check("f() * g() == 5")
- check("not f()")
- check("not (f() and g() or 0)")
- check("f() == g()")
- i = 4
- check("i == f()")
- check("len(f()) == 0")
- check("isinstance(2+3+4, float)")
-
- run("x = i")
- check("x == 5")
-
- run("assert not f(), 'oops'")
- run("a, b, c = 1, 2")
- run("a, b, c = f()")
-
- check("max([f(),g()]) == 4")
- check("'hello'[g()] == 'h'")
- run("'guk%d' % h(f())")
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_code/assertion.py b/tests/wpt/web-platform-tests/tools/py/py/_code/assertion.py
deleted file mode 100644
index 4ce80c75b1c..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_code/assertion.py
+++ /dev/null
@@ -1,94 +0,0 @@
-import sys
-import py
-
-BuiltinAssertionError = py.builtin.builtins.AssertionError
-
-_reprcompare = None # if set, will be called by assert reinterp for comparison ops
-
-def _format_explanation(explanation):
- """This formats an explanation
-
- Normally all embedded newlines are escaped, however there are
- three exceptions: \n{, \n} and \n~. The first two are intended
- cover nested explanations, see function and attribute explanations
- for examples (.visit_Call(), visit_Attribute()). The last one is
- for when one explanation needs to span multiple lines, e.g. when
- displaying diffs.
- """
- raw_lines = (explanation or '').split('\n')
- # escape newlines not followed by {, } and ~
- lines = [raw_lines[0]]
- for l in raw_lines[1:]:
- if l.startswith('{') or l.startswith('}') or l.startswith('~'):
- lines.append(l)
- else:
- lines[-1] += '\\n' + l
-
- result = lines[:1]
- stack = [0]
- stackcnt = [0]
- for line in lines[1:]:
- if line.startswith('{'):
- if stackcnt[-1]:
- s = 'and '
- else:
- s = 'where '
- stack.append(len(result))
- stackcnt[-1] += 1
- stackcnt.append(0)
- result.append(' +' + ' '*(len(stack)-1) + s + line[1:])
- elif line.startswith('}'):
- assert line.startswith('}')
- stack.pop()
- stackcnt.pop()
- result[stack[-1]] += line[1:]
- else:
- assert line.startswith('~')
- result.append(' '*len(stack) + line[1:])
- assert len(stack) == 1
- return '\n'.join(result)
-
-
-class AssertionError(BuiltinAssertionError):
- def __init__(self, *args):
- BuiltinAssertionError.__init__(self, *args)
- if args:
- try:
- self.msg = str(args[0])
- except py.builtin._sysex:
- raise
- except:
- self.msg = "<[broken __repr__] %s at %0xd>" %(
- args[0].__class__, id(args[0]))
- else:
- f = py.code.Frame(sys._getframe(1))
- try:
- source = f.code.fullsource
- if source is not None:
- try:
- source = source.getstatement(f.lineno, assertion=True)
- except IndexError:
- source = None
- else:
- source = str(source.deindent()).strip()
- except py.error.ENOENT:
- source = None
- # this can also occur during reinterpretation, when the
- # co_filename is set to "<run>".
- if source:
- self.msg = reinterpret(source, f, should_fail=True)
- else:
- self.msg = "<could not determine information>"
- if not self.args:
- self.args = (self.msg,)
-
-if sys.version_info > (3, 0):
- AssertionError.__module__ = "builtins"
- reinterpret_old = "old reinterpretation not available for py3"
-else:
- from py._code._assertionold import interpret as reinterpret_old
-if sys.version_info >= (2, 6) or (sys.platform.startswith("java")):
- from py._code._assertionnew import interpret as reinterpret
-else:
- reinterpret = reinterpret_old
-
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_code/code.py b/tests/wpt/web-platform-tests/tools/py/py/_code/code.py
deleted file mode 100644
index f14c562a296..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_code/code.py
+++ /dev/null
@@ -1,787 +0,0 @@
-import py
-import sys
-from inspect import CO_VARARGS, CO_VARKEYWORDS
-
-builtin_repr = repr
-
-reprlib = py.builtin._tryimport('repr', 'reprlib')
-
-if sys.version_info[0] >= 3:
- from traceback import format_exception_only
-else:
- from py._code._py2traceback import format_exception_only
-
-class Code(object):
- """ wrapper around Python code objects """
- def __init__(self, rawcode):
- if not hasattr(rawcode, "co_filename"):
- rawcode = py.code.getrawcode(rawcode)
- try:
- self.filename = rawcode.co_filename
- self.firstlineno = rawcode.co_firstlineno - 1
- self.name = rawcode.co_name
- except AttributeError:
- raise TypeError("not a code object: %r" %(rawcode,))
- self.raw = rawcode
-
- def __eq__(self, other):
- return self.raw == other.raw
-
- def __ne__(self, other):
- return not self == other
-
- @property
- def path(self):
- """ return a path object pointing to source code (note that it
- might not point to an actually existing file). """
- p = py.path.local(self.raw.co_filename)
- # maybe don't try this checking
- if not p.check():
- # XXX maybe try harder like the weird logic
- # in the standard lib [linecache.updatecache] does?
- p = self.raw.co_filename
- return p
-
- @property
- def fullsource(self):
- """ return a py.code.Source object for the full source file of the code
- """
- from py._code import source
- full, _ = source.findsource(self.raw)
- return full
-
- def source(self):
- """ return a py.code.Source object for the code object's source only
- """
- # return source only for that part of code
- return py.code.Source(self.raw)
-
- def getargs(self, var=False):
- """ return a tuple with the argument names for the code object
-
- if 'var' is set True also return the names of the variable and
- keyword arguments when present
- """
- # handfull shortcut for getting args
- raw = self.raw
- argcount = raw.co_argcount
- if var:
- argcount += raw.co_flags & CO_VARARGS
- argcount += raw.co_flags & CO_VARKEYWORDS
- return raw.co_varnames[:argcount]
-
-class Frame(object):
- """Wrapper around a Python frame holding f_locals and f_globals
- in which expressions can be evaluated."""
-
- def __init__(self, frame):
- self.lineno = frame.f_lineno - 1
- self.f_globals = frame.f_globals
- self.f_locals = frame.f_locals
- self.raw = frame
- self.code = py.code.Code(frame.f_code)
-
- @property
- def statement(self):
- """ statement this frame is at """
- if self.code.fullsource is None:
- return py.code.Source("")
- return self.code.fullsource.getstatement(self.lineno)
-
- def eval(self, code, **vars):
- """ evaluate 'code' in the frame
-
- 'vars' are optional additional local variables
-
- returns the result of the evaluation
- """
- f_locals = self.f_locals.copy()
- f_locals.update(vars)
- return eval(code, self.f_globals, f_locals)
-
- def exec_(self, code, **vars):
- """ exec 'code' in the frame
-
- 'vars' are optiona; additional local variables
- """
- f_locals = self.f_locals.copy()
- f_locals.update(vars)
- py.builtin.exec_(code, self.f_globals, f_locals )
-
- def repr(self, object):
- """ return a 'safe' (non-recursive, one-line) string repr for 'object'
- """
- return py.io.saferepr(object)
-
- def is_true(self, object):
- return object
-
- def getargs(self, var=False):
- """ return a list of tuples (name, value) for all arguments
-
- if 'var' is set True also include the variable and keyword
- arguments when present
- """
- retval = []
- for arg in self.code.getargs(var):
- try:
- retval.append((arg, self.f_locals[arg]))
- except KeyError:
- pass # this can occur when using Psyco
- return retval
-
-class TracebackEntry(object):
- """ a single entry in a traceback """
-
- _repr_style = None
- exprinfo = None
-
- def __init__(self, rawentry):
- self._rawentry = rawentry
- self.lineno = rawentry.tb_lineno - 1
-
- def set_repr_style(self, mode):
- assert mode in ("short", "long")
- self._repr_style = mode
-
- @property
- def frame(self):
- return py.code.Frame(self._rawentry.tb_frame)
-
- @property
- def relline(self):
- return self.lineno - self.frame.code.firstlineno
-
- def __repr__(self):
- return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1)
-
- @property
- def statement(self):
- """ py.code.Source object for the current statement """
- source = self.frame.code.fullsource
- return source.getstatement(self.lineno)
-
- @property
- def path(self):
- """ path to the source code """
- return self.frame.code.path
-
- def getlocals(self):
- return self.frame.f_locals
- locals = property(getlocals, None, None, "locals of underlaying frame")
-
- def reinterpret(self):
- """Reinterpret the failing statement and returns a detailed information
- about what operations are performed."""
- if self.exprinfo is None:
- source = str(self.statement).strip()
- x = py.code._reinterpret(source, self.frame, should_fail=True)
- if not isinstance(x, str):
- raise TypeError("interpret returned non-string %r" % (x,))
- self.exprinfo = x
- return self.exprinfo
-
- def getfirstlinesource(self):
- # on Jython this firstlineno can be -1 apparently
- return max(self.frame.code.firstlineno, 0)
-
- def getsource(self, astcache=None):
- """ return failing source code. """
- # we use the passed in astcache to not reparse asttrees
- # within exception info printing
- from py._code.source import getstatementrange_ast
- source = self.frame.code.fullsource
- if source is None:
- return None
- key = astnode = None
- if astcache is not None:
- key = self.frame.code.path
- if key is not None:
- astnode = astcache.get(key, None)
- start = self.getfirstlinesource()
- try:
- astnode, _, end = getstatementrange_ast(self.lineno, source,
- astnode=astnode)
- except SyntaxError:
- end = self.lineno + 1
- else:
- if key is not None:
- astcache[key] = astnode
- return source[start:end]
-
- source = property(getsource)
-
- def ishidden(self):
- """ return True if the current frame has a var __tracebackhide__
- resolving to True
-
- mostly for internal use
- """
- try:
- return self.frame.f_locals['__tracebackhide__']
- except KeyError:
- try:
- return self.frame.f_globals['__tracebackhide__']
- except KeyError:
- return False
-
- def __str__(self):
- try:
- fn = str(self.path)
- except py.error.Error:
- fn = '???'
- name = self.frame.code.name
- try:
- line = str(self.statement).lstrip()
- except KeyboardInterrupt:
- raise
- except:
- line = "???"
- return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line)
-
- def name(self):
- return self.frame.code.raw.co_name
- name = property(name, None, None, "co_name of underlaying code")
-
-class Traceback(list):
- """ Traceback objects encapsulate and offer higher level
- access to Traceback entries.
- """
- Entry = TracebackEntry
- def __init__(self, tb):
- """ initialize from given python traceback object. """
- if hasattr(tb, 'tb_next'):
- def f(cur):
- while cur is not None:
- yield self.Entry(cur)
- cur = cur.tb_next
- list.__init__(self, f(tb))
- else:
- list.__init__(self, tb)
-
- def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None):
- """ return a Traceback instance wrapping part of this Traceback
-
- by provding any combination of path, lineno and firstlineno, the
- first frame to start the to-be-returned traceback is determined
-
- this allows cutting the first part of a Traceback instance e.g.
- for formatting reasons (removing some uninteresting bits that deal
- with handling of the exception/traceback)
- """
- for x in self:
- code = x.frame.code
- codepath = code.path
- if ((path is None or codepath == path) and
- (excludepath is None or not hasattr(codepath, 'relto') or
- not codepath.relto(excludepath)) and
- (lineno is None or x.lineno == lineno) and
- (firstlineno is None or x.frame.code.firstlineno == firstlineno)):
- return Traceback(x._rawentry)
- return self
-
- def __getitem__(self, key):
- val = super(Traceback, self).__getitem__(key)
- if isinstance(key, type(slice(0))):
- val = self.__class__(val)
- return val
-
- def filter(self, fn=lambda x: not x.ishidden()):
- """ return a Traceback instance with certain items removed
-
- fn is a function that gets a single argument, a TracebackItem
- instance, and should return True when the item should be added
- to the Traceback, False when not
-
- by default this removes all the TracebackItems which are hidden
- (see ishidden() above)
- """
- return Traceback(filter(fn, self))
-
- def getcrashentry(self):
- """ return last non-hidden traceback entry that lead
- to the exception of a traceback.
- """
- for i in range(-1, -len(self)-1, -1):
- entry = self[i]
- if not entry.ishidden():
- return entry
- return self[-1]
-
- def recursionindex(self):
- """ return the index of the frame/TracebackItem where recursion
- originates if appropriate, None if no recursion occurred
- """
- cache = {}
- for i, entry in enumerate(self):
- # id for the code.raw is needed to work around
- # the strange metaprogramming in the decorator lib from pypi
- # which generates code objects that have hash/value equality
- #XXX needs a test
- key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
- #print "checking for recursion at", key
- l = cache.setdefault(key, [])
- if l:
- f = entry.frame
- loc = f.f_locals
- for otherloc in l:
- if f.is_true(f.eval(co_equal,
- __recursioncache_locals_1=loc,
- __recursioncache_locals_2=otherloc)):
- return i
- l.append(entry.frame.f_locals)
- return None
-
-co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2',
- '?', 'eval')
-
-class ExceptionInfo(object):
- """ wraps sys.exc_info() objects and offers
- help for navigating the traceback.
- """
- _striptext = ''
- def __init__(self, tup=None, exprinfo=None):
- if tup is None:
- tup = sys.exc_info()
- if exprinfo is None and isinstance(tup[1], AssertionError):
- exprinfo = getattr(tup[1], 'msg', None)
- if exprinfo is None:
- exprinfo = str(tup[1])
- if exprinfo and exprinfo.startswith('assert '):
- self._striptext = 'AssertionError: '
- self._excinfo = tup
- #: the exception class
- self.type = tup[0]
- #: the exception instance
- self.value = tup[1]
- #: the exception raw traceback
- self.tb = tup[2]
- #: the exception type name
- self.typename = self.type.__name__
- #: the exception traceback (py.code.Traceback instance)
- self.traceback = py.code.Traceback(self.tb)
-
- def __repr__(self):
- return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback))
-
- def exconly(self, tryshort=False):
- """ return the exception as a string
-
- when 'tryshort' resolves to True, and the exception is a
- py.code._AssertionError, only the actual exception part of
- the exception representation is returned (so 'AssertionError: ' is
- removed from the beginning)
- """
- lines = format_exception_only(self.type, self.value)
- text = ''.join(lines)
- text = text.rstrip()
- if tryshort:
- if text.startswith(self._striptext):
- text = text[len(self._striptext):]
- return text
-
- def errisinstance(self, exc):
- """ return True if the exception is an instance of exc """
- return isinstance(self.value, exc)
-
- def _getreprcrash(self):
- exconly = self.exconly(tryshort=True)
- entry = self.traceback.getcrashentry()
- path, lineno = entry.frame.code.raw.co_filename, entry.lineno
- return ReprFileLocation(path, lineno+1, exconly)
-
- def getrepr(self, showlocals=False, style="long",
- abspath=False, tbfilter=True, funcargs=False):
- """ return str()able representation of this exception info.
- showlocals: show locals per traceback entry
- style: long|short|no|native traceback style
- tbfilter: hide entries (where __tracebackhide__ is true)
-
- in case of style==native, tbfilter and showlocals is ignored.
- """
- if style == 'native':
- return ReprExceptionInfo(ReprTracebackNative(
- py.std.traceback.format_exception(
- self.type,
- self.value,
- self.traceback[0]._rawentry,
- )), self._getreprcrash())
-
- fmt = FormattedExcinfo(showlocals=showlocals, style=style,
- abspath=abspath, tbfilter=tbfilter, funcargs=funcargs)
- return fmt.repr_excinfo(self)
-
- def __str__(self):
- entry = self.traceback[-1]
- loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
- return str(loc)
-
- def __unicode__(self):
- entry = self.traceback[-1]
- loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
- return unicode(loc)
-
-
-class FormattedExcinfo(object):
- """ presenting information about failing Functions and Generators. """
- # for traceback entries
- flow_marker = ">"
- fail_marker = "E"
-
- def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False):
- self.showlocals = showlocals
- self.style = style
- self.tbfilter = tbfilter
- self.funcargs = funcargs
- self.abspath = abspath
- self.astcache = {}
-
- def _getindent(self, source):
- # figure out indent for given source
- try:
- s = str(source.getstatement(len(source)-1))
- except KeyboardInterrupt:
- raise
- except:
- try:
- s = str(source[-1])
- except KeyboardInterrupt:
- raise
- except:
- return 0
- return 4 + (len(s) - len(s.lstrip()))
-
- def _getentrysource(self, entry):
- source = entry.getsource(self.astcache)
- if source is not None:
- source = source.deindent()
- return source
-
- def _saferepr(self, obj):
- return py.io.saferepr(obj)
-
- def repr_args(self, entry):
- if self.funcargs:
- args = []
- for argname, argvalue in entry.frame.getargs(var=True):
- args.append((argname, self._saferepr(argvalue)))
- return ReprFuncArgs(args)
-
- def get_source(self, source, line_index=-1, excinfo=None, short=False):
- """ return formatted and marked up source lines. """
- lines = []
- if source is None or line_index >= len(source.lines):
- source = py.code.Source("???")
- line_index = 0
- if line_index < 0:
- line_index += len(source)
- space_prefix = " "
- if short:
- lines.append(space_prefix + source.lines[line_index].strip())
- else:
- for line in source.lines[:line_index]:
- lines.append(space_prefix + line)
- lines.append(self.flow_marker + " " + source.lines[line_index])
- for line in source.lines[line_index+1:]:
- lines.append(space_prefix + line)
- if excinfo is not None:
- indent = 4 if short else self._getindent(source)
- lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
- return lines
-
- def get_exconly(self, excinfo, indent=4, markall=False):
- lines = []
- indent = " " * indent
- # get the real exception information out
- exlines = excinfo.exconly(tryshort=True).split('\n')
- failindent = self.fail_marker + indent[1:]
- for line in exlines:
- lines.append(failindent + line)
- if not markall:
- failindent = indent
- return lines
-
- def repr_locals(self, locals):
- if self.showlocals:
- lines = []
- keys = [loc for loc in locals if loc[0] != "@"]
- keys.sort()
- for name in keys:
- value = locals[name]
- if name == '__builtins__':
- lines.append("__builtins__ = <builtins>")
- else:
- # This formatting could all be handled by the
- # _repr() function, which is only reprlib.Repr in
- # disguise, so is very configurable.
- str_repr = self._saferepr(value)
- #if len(str_repr) < 70 or not isinstance(value,
- # (list, tuple, dict)):
- lines.append("%-10s = %s" %(name, str_repr))
- #else:
- # self._line("%-10s =\\" % (name,))
- # # XXX
- # py.std.pprint.pprint(value, stream=self.excinfowriter)
- return ReprLocals(lines)
-
- def repr_traceback_entry(self, entry, excinfo=None):
- source = self._getentrysource(entry)
- if source is None:
- source = py.code.Source("???")
- line_index = 0
- else:
- # entry.getfirstlinesource() can be -1, should be 0 on jython
- line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
-
- lines = []
- style = entry._repr_style
- if style is None:
- style = self.style
- if style in ("short", "long"):
- short = style == "short"
- reprargs = self.repr_args(entry) if not short else None
- s = self.get_source(source, line_index, excinfo, short=short)
- lines.extend(s)
- if short:
- message = "in %s" %(entry.name)
- else:
- message = excinfo and excinfo.typename or ""
- path = self._makepath(entry.path)
- filelocrepr = ReprFileLocation(path, entry.lineno+1, message)
- localsrepr = None
- if not short:
- localsrepr = self.repr_locals(entry.locals)
- return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style)
- if excinfo:
- lines.extend(self.get_exconly(excinfo, indent=4))
- return ReprEntry(lines, None, None, None, style)
-
- def _makepath(self, path):
- if not self.abspath:
- try:
- np = py.path.local().bestrelpath(path)
- except OSError:
- return path
- if len(np) < len(str(path)):
- path = np
- return path
-
- def repr_traceback(self, excinfo):
- traceback = excinfo.traceback
- if self.tbfilter:
- traceback = traceback.filter()
- recursionindex = None
- if excinfo.errisinstance(RuntimeError):
- if "maximum recursion depth exceeded" in str(excinfo.value):
- recursionindex = traceback.recursionindex()
- last = traceback[-1]
- entries = []
- extraline = None
- for index, entry in enumerate(traceback):
- einfo = (last == entry) and excinfo or None
- reprentry = self.repr_traceback_entry(entry, einfo)
- entries.append(reprentry)
- if index == recursionindex:
- extraline = "!!! Recursion detected (same locals & position)"
- break
- return ReprTraceback(entries, extraline, style=self.style)
-
- def repr_excinfo(self, excinfo):
- reprtraceback = self.repr_traceback(excinfo)
- reprcrash = excinfo._getreprcrash()
- return ReprExceptionInfo(reprtraceback, reprcrash)
-
-class TerminalRepr:
- def __str__(self):
- s = self.__unicode__()
- if sys.version_info[0] < 3:
- s = s.encode('utf-8')
- return s
-
- def __unicode__(self):
- # FYI this is called from pytest-xdist's serialization of exception
- # information.
- io = py.io.TextIO()
- tw = py.io.TerminalWriter(file=io)
- self.toterminal(tw)
- return io.getvalue().strip()
-
- def __repr__(self):
- return "<%s instance at %0x>" %(self.__class__, id(self))
-
-
-class ReprExceptionInfo(TerminalRepr):
- def __init__(self, reprtraceback, reprcrash):
- self.reprtraceback = reprtraceback
- self.reprcrash = reprcrash
- self.sections = []
-
- def addsection(self, name, content, sep="-"):
- self.sections.append((name, content, sep))
-
- def toterminal(self, tw):
- self.reprtraceback.toterminal(tw)
- for name, content, sep in self.sections:
- tw.sep(sep, name)
- tw.line(content)
-
-class ReprTraceback(TerminalRepr):
- entrysep = "_ "
-
- def __init__(self, reprentries, extraline, style):
- self.reprentries = reprentries
- self.extraline = extraline
- self.style = style
-
- def toterminal(self, tw):
- # the entries might have different styles
- last_style = None
- for i, entry in enumerate(self.reprentries):
- if entry.style == "long":
- tw.line("")
- entry.toterminal(tw)
- if i < len(self.reprentries) - 1:
- next_entry = self.reprentries[i+1]
- if entry.style == "long" or \
- entry.style == "short" and next_entry.style == "long":
- tw.sep(self.entrysep)
-
- if self.extraline:
- tw.line(self.extraline)
-
-class ReprTracebackNative(ReprTraceback):
- def __init__(self, tblines):
- self.style = "native"
- self.reprentries = [ReprEntryNative(tblines)]
- self.extraline = None
-
-class ReprEntryNative(TerminalRepr):
- style = "native"
-
- def __init__(self, tblines):
- self.lines = tblines
-
- def toterminal(self, tw):
- tw.write("".join(self.lines))
-
-class ReprEntry(TerminalRepr):
- localssep = "_ "
-
- def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style):
- self.lines = lines
- self.reprfuncargs = reprfuncargs
- self.reprlocals = reprlocals
- self.reprfileloc = filelocrepr
- self.style = style
-
- def toterminal(self, tw):
- if self.style == "short":
- self.reprfileloc.toterminal(tw)
- for line in self.lines:
- red = line.startswith("E ")
- tw.line(line, bold=True, red=red)
- #tw.line("")
- return
- if self.reprfuncargs:
- self.reprfuncargs.toterminal(tw)
- for line in self.lines:
- red = line.startswith("E ")
- tw.line(line, bold=True, red=red)
- if self.reprlocals:
- #tw.sep(self.localssep, "Locals")
- tw.line("")
- self.reprlocals.toterminal(tw)
- if self.reprfileloc:
- if self.lines:
- tw.line("")
- self.reprfileloc.toterminal(tw)
-
- def __str__(self):
- return "%s\n%s\n%s" % ("\n".join(self.lines),
- self.reprlocals,
- self.reprfileloc)
-
-class ReprFileLocation(TerminalRepr):
- def __init__(self, path, lineno, message):
- self.path = str(path)
- self.lineno = lineno
- self.message = message
-
- def toterminal(self, tw):
- # filename and lineno output for each entry,
- # using an output format that most editors unterstand
- msg = self.message
- i = msg.find("\n")
- if i != -1:
- msg = msg[:i]
- tw.line("%s:%s: %s" %(self.path, self.lineno, msg))
-
-class ReprLocals(TerminalRepr):
- def __init__(self, lines):
- self.lines = lines
-
- def toterminal(self, tw):
- for line in self.lines:
- tw.line(line)
-
-class ReprFuncArgs(TerminalRepr):
- def __init__(self, args):
- self.args = args
-
- def toterminal(self, tw):
- if self.args:
- linesofar = ""
- for name, value in self.args:
- ns = "%s = %s" %(name, value)
- if len(ns) + len(linesofar) + 2 > tw.fullwidth:
- if linesofar:
- tw.line(linesofar)
- linesofar = ns
- else:
- if linesofar:
- linesofar += ", " + ns
- else:
- linesofar = ns
- if linesofar:
- tw.line(linesofar)
- tw.line("")
-
-
-
-oldbuiltins = {}
-
-def patch_builtins(assertion=True, compile=True):
- """ put compile and AssertionError builtins to Python's builtins. """
- if assertion:
- from py._code import assertion
- l = oldbuiltins.setdefault('AssertionError', [])
- l.append(py.builtin.builtins.AssertionError)
- py.builtin.builtins.AssertionError = assertion.AssertionError
- if compile:
- l = oldbuiltins.setdefault('compile', [])
- l.append(py.builtin.builtins.compile)
- py.builtin.builtins.compile = py.code.compile
-
-def unpatch_builtins(assertion=True, compile=True):
- """ remove compile and AssertionError builtins from Python builtins. """
- if assertion:
- py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop()
- if compile:
- py.builtin.builtins.compile = oldbuiltins['compile'].pop()
-
-def getrawcode(obj, trycall=True):
- """ return code object for given function. """
- try:
- return obj.__code__
- except AttributeError:
- obj = getattr(obj, 'im_func', obj)
- obj = getattr(obj, 'func_code', obj)
- obj = getattr(obj, 'f_code', obj)
- obj = getattr(obj, '__code__', obj)
- if trycall and not hasattr(obj, 'co_firstlineno'):
- if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj):
- x = getrawcode(obj.__call__, trycall=False)
- if hasattr(x, 'co_firstlineno'):
- return x
- return obj
-
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_code/source.py b/tests/wpt/web-platform-tests/tools/py/py/_code/source.py
deleted file mode 100644
index 3a648e63579..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_code/source.py
+++ /dev/null
@@ -1,419 +0,0 @@
-from __future__ import generators
-
-from bisect import bisect_right
-import sys
-import inspect, tokenize
-import py
-from types import ModuleType
-cpy_compile = compile
-
-try:
- import _ast
- from _ast import PyCF_ONLY_AST as _AST_FLAG
-except ImportError:
- _AST_FLAG = 0
- _ast = None
-
-
-class Source(object):
- """ a immutable object holding a source code fragment,
- possibly deindenting it.
- """
- _compilecounter = 0
- def __init__(self, *parts, **kwargs):
- self.lines = lines = []
- de = kwargs.get('deindent', True)
- rstrip = kwargs.get('rstrip', True)
- for part in parts:
- if not part:
- partlines = []
- if isinstance(part, Source):
- partlines = part.lines
- elif isinstance(part, (tuple, list)):
- partlines = [x.rstrip("\n") for x in part]
- elif isinstance(part, py.builtin._basestring):
- partlines = part.split('\n')
- if rstrip:
- while partlines:
- if partlines[-1].strip():
- break
- partlines.pop()
- else:
- partlines = getsource(part, deindent=de).lines
- if de:
- partlines = deindent(partlines)
- lines.extend(partlines)
-
- def __eq__(self, other):
- try:
- return self.lines == other.lines
- except AttributeError:
- if isinstance(other, str):
- return str(self) == other
- return False
-
- def __getitem__(self, key):
- if isinstance(key, int):
- return self.lines[key]
- else:
- if key.step not in (None, 1):
- raise IndexError("cannot slice a Source with a step")
- return self.__getslice__(key.start, key.stop)
-
- def __len__(self):
- return len(self.lines)
-
- def __getslice__(self, start, end):
- newsource = Source()
- newsource.lines = self.lines[start:end]
- return newsource
-
- def strip(self):
- """ return new source object with trailing
- and leading blank lines removed.
- """
- start, end = 0, len(self)
- while start < end and not self.lines[start].strip():
- start += 1
- while end > start and not self.lines[end-1].strip():
- end -= 1
- source = Source()
- source.lines[:] = self.lines[start:end]
- return source
-
- def putaround(self, before='', after='', indent=' ' * 4):
- """ return a copy of the source object with
- 'before' and 'after' wrapped around it.
- """
- before = Source(before)
- after = Source(after)
- newsource = Source()
- lines = [ (indent + line) for line in self.lines]
- newsource.lines = before.lines + lines + after.lines
- return newsource
-
- def indent(self, indent=' ' * 4):
- """ return a copy of the source object with
- all lines indented by the given indent-string.
- """
- newsource = Source()
- newsource.lines = [(indent+line) for line in self.lines]
- return newsource
-
- def getstatement(self, lineno, assertion=False):
- """ return Source statement which contains the
- given linenumber (counted from 0).
- """
- start, end = self.getstatementrange(lineno, assertion)
- return self[start:end]
-
- def getstatementrange(self, lineno, assertion=False):
- """ return (start, end) tuple which spans the minimal
- statement region which containing the given lineno.
- """
- if not (0 <= lineno < len(self)):
- raise IndexError("lineno out of range")
- ast, start, end = getstatementrange_ast(lineno, self)
- return start, end
-
- def deindent(self, offset=None):
- """ return a new source object deindented by offset.
- If offset is None then guess an indentation offset from
- the first non-blank line. Subsequent lines which have a
- lower indentation offset will be copied verbatim as
- they are assumed to be part of multilines.
- """
- # XXX maybe use the tokenizer to properly handle multiline
- # strings etc.pp?
- newsource = Source()
- newsource.lines[:] = deindent(self.lines, offset)
- return newsource
-
- def isparseable(self, deindent=True):
- """ return True if source is parseable, heuristically
- deindenting it by default.
- """
- try:
- import parser
- except ImportError:
- syntax_checker = lambda x: compile(x, 'asd', 'exec')
- else:
- syntax_checker = parser.suite
-
- if deindent:
- source = str(self.deindent())
- else:
- source = str(self)
- try:
- #compile(source+'\n', "x", "exec")
- syntax_checker(source+'\n')
- except KeyboardInterrupt:
- raise
- except Exception:
- return False
- else:
- return True
-
- def __str__(self):
- return "\n".join(self.lines)
-
- def compile(self, filename=None, mode='exec',
- flag=generators.compiler_flag,
- dont_inherit=0, _genframe=None):
- """ return compiled code object. if filename is None
- invent an artificial filename which displays
- the source/line position of the caller frame.
- """
- if not filename or py.path.local(filename).check(file=0):
- if _genframe is None:
- _genframe = sys._getframe(1) # the caller
- fn,lineno = _genframe.f_code.co_filename, _genframe.f_lineno
- base = "<%d-codegen " % self._compilecounter
- self.__class__._compilecounter += 1
- if not filename:
- filename = base + '%s:%d>' % (fn, lineno)
- else:
- filename = base + '%r %s:%d>' % (filename, fn, lineno)
- source = "\n".join(self.lines) + '\n'
- try:
- co = cpy_compile(source, filename, mode, flag)
- except SyntaxError:
- ex = sys.exc_info()[1]
- # re-represent syntax errors from parsing python strings
- msglines = self.lines[:ex.lineno]
- if ex.offset:
- msglines.append(" "*ex.offset + '^')
- msglines.append("(code was compiled probably from here: %s)" % filename)
- newex = SyntaxError('\n'.join(msglines))
- newex.offset = ex.offset
- newex.lineno = ex.lineno
- newex.text = ex.text
- raise newex
- else:
- if flag & _AST_FLAG:
- return co
- lines = [(x + "\n") for x in self.lines]
- if sys.version_info[0] >= 3:
- # XXX py3's inspect.getsourcefile() checks for a module
- # and a pep302 __loader__ ... we don't have a module
- # at code compile-time so we need to fake it here
- m = ModuleType("_pycodecompile_pseudo_module")
- py.std.inspect.modulesbyfile[filename] = None
- py.std.sys.modules[None] = m
- m.__loader__ = 1
- py.std.linecache.cache[filename] = (1, None, lines, filename)
- return co
-
-#
-# public API shortcut functions
-#
-
-def compile_(source, filename=None, mode='exec', flags=
- generators.compiler_flag, dont_inherit=0):
- """ compile the given source to a raw code object,
- and maintain an internal cache which allows later
- retrieval of the source code for the code object
- and any recursively created code objects.
- """
- if _ast is not None and isinstance(source, _ast.AST):
- # XXX should Source support having AST?
- return cpy_compile(source, filename, mode, flags, dont_inherit)
- _genframe = sys._getframe(1) # the caller
- s = Source(source)
- co = s.compile(filename, mode, flags, _genframe=_genframe)
- return co
-
-
-def getfslineno(obj):
- """ Return source location (path, lineno) for the given object.
- If the source cannot be determined return ("", -1)
- """
- try:
- code = py.code.Code(obj)
- except TypeError:
- try:
- fn = (py.std.inspect.getsourcefile(obj) or
- py.std.inspect.getfile(obj))
- except TypeError:
- return "", -1
-
- fspath = fn and py.path.local(fn) or None
- lineno = -1
- if fspath:
- try:
- _, lineno = findsource(obj)
- except IOError:
- pass
- else:
- fspath = code.path
- lineno = code.firstlineno
- assert isinstance(lineno, int)
- return fspath, lineno
-
-#
-# helper functions
-#
-
-def findsource(obj):
- try:
- sourcelines, lineno = py.std.inspect.findsource(obj)
- except py.builtin._sysex:
- raise
- except:
- return None, -1
- source = Source()
- source.lines = [line.rstrip() for line in sourcelines]
- return source, lineno
-
-def getsource(obj, **kwargs):
- obj = py.code.getrawcode(obj)
- try:
- strsrc = inspect.getsource(obj)
- except IndentationError:
- strsrc = "\"Buggy python version consider upgrading, cannot get source\""
- assert isinstance(strsrc, str)
- return Source(strsrc, **kwargs)
-
-def deindent(lines, offset=None):
- if offset is None:
- for line in lines:
- line = line.expandtabs()
- s = line.lstrip()
- if s:
- offset = len(line)-len(s)
- break
- else:
- offset = 0
- if offset == 0:
- return list(lines)
- newlines = []
- def readline_generator(lines):
- for line in lines:
- yield line + '\n'
- while True:
- yield ''
-
- it = readline_generator(lines)
-
- try:
- for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(lambda: next(it)):
- if sline > len(lines):
- break # End of input reached
- if sline > len(newlines):
- line = lines[sline - 1].expandtabs()
- if line.lstrip() and line[:offset].isspace():
- line = line[offset:] # Deindent
- newlines.append(line)
-
- for i in range(sline, eline):
- # Don't deindent continuing lines of
- # multiline tokens (i.e. multiline strings)
- newlines.append(lines[i])
- except (IndentationError, tokenize.TokenError):
- pass
- # Add any lines we didn't see. E.g. if an exception was raised.
- newlines.extend(lines[len(newlines):])
- return newlines
-
-
-def get_statement_startend2(lineno, node):
- import ast
- # flatten all statements and except handlers into one lineno-list
- # AST's line numbers start indexing at 1
- l = []
- for x in ast.walk(node):
- if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler):
- l.append(x.lineno - 1)
- for name in "finalbody", "orelse":
- val = getattr(x, name, None)
- if val:
- # treat the finally/orelse part as its own statement
- l.append(val[0].lineno - 1 - 1)
- l.sort()
- insert_index = bisect_right(l, lineno)
- start = l[insert_index - 1]
- if insert_index >= len(l):
- end = None
- else:
- end = l[insert_index]
- return start, end
-
-
-def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
- if astnode is None:
- content = str(source)
- if sys.version_info < (2,7):
- content += "\n"
- try:
- astnode = compile(content, "source", "exec", 1024) # 1024 for AST
- except ValueError:
- start, end = getstatementrange_old(lineno, source, assertion)
- return None, start, end
- start, end = get_statement_startend2(lineno, astnode)
- # we need to correct the end:
- # - ast-parsing strips comments
- # - there might be empty lines
- # - we might have lesser indented code blocks at the end
- if end is None:
- end = len(source.lines)
-
- if end > start + 1:
- # make sure we don't span differently indented code blocks
- # by using the BlockFinder helper used which inspect.getsource() uses itself
- block_finder = inspect.BlockFinder()
- # if we start with an indented line, put blockfinder to "started" mode
- block_finder.started = source.lines[start][0].isspace()
- it = ((x + "\n") for x in source.lines[start:end])
- try:
- for tok in tokenize.generate_tokens(lambda: next(it)):
- block_finder.tokeneater(*tok)
- except (inspect.EndOfBlock, IndentationError):
- end = block_finder.last + start
- except Exception:
- pass
-
- # the end might still point to a comment or empty line, correct it
- while end:
- line = source.lines[end - 1].lstrip()
- if line.startswith("#") or not line:
- end -= 1
- else:
- break
- return astnode, start, end
-
-
-def getstatementrange_old(lineno, source, assertion=False):
- """ return (start, end) tuple which spans the minimal
- statement region which containing the given lineno.
- raise an IndexError if no such statementrange can be found.
- """
- # XXX this logic is only used on python2.4 and below
- # 1. find the start of the statement
- from codeop import compile_command
- for start in range(lineno, -1, -1):
- if assertion:
- line = source.lines[start]
- # the following lines are not fully tested, change with care
- if 'super' in line and 'self' in line and '__init__' in line:
- raise IndexError("likely a subclass")
- if "assert" not in line and "raise" not in line:
- continue
- trylines = source.lines[start:lineno+1]
- # quick hack to prepare parsing an indented line with
- # compile_command() (which errors on "return" outside defs)
- trylines.insert(0, 'def xxx():')
- trysource = '\n '.join(trylines)
- # ^ space here
- try:
- compile_command(trysource)
- except (SyntaxError, OverflowError, ValueError):
- continue
-
- # 2. find the end of the statement
- for end in range(lineno+1, len(source)+1):
- trysource = source[start:end]
- if trysource.isparseable():
- return start, end
- raise SyntaxError("no valid source range around line %d " % (lineno,))
-
-
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_error.py b/tests/wpt/web-platform-tests/tools/py/py/_error.py
deleted file mode 100644
index 550fb521a04..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_error.py
+++ /dev/null
@@ -1,88 +0,0 @@
-"""
-create errno-specific classes for IO or os calls.
-
-"""
-import sys, os, errno
-
-class Error(EnvironmentError):
- def __repr__(self):
- return "%s.%s %r: %s " %(self.__class__.__module__,
- self.__class__.__name__,
- self.__class__.__doc__,
- " ".join(map(str, self.args)),
- #repr(self.args)
- )
-
- def __str__(self):
- s = "[%s]: %s" %(self.__class__.__doc__,
- " ".join(map(str, self.args)),
- )
- return s
-
-_winerrnomap = {
- 2: errno.ENOENT,
- 3: errno.ENOENT,
- 17: errno.EEXIST,
- 13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailiable
- 22: errno.ENOTDIR,
- 20: errno.ENOTDIR,
- 267: errno.ENOTDIR,
- 5: errno.EACCES, # anything better?
-}
-
-class ErrorMaker(object):
- """ lazily provides Exception classes for each possible POSIX errno
- (as defined per the 'errno' module). All such instances
- subclass EnvironmentError.
- """
- Error = Error
- _errno2class = {}
-
- def __getattr__(self, name):
- if name[0] == "_":
- raise AttributeError(name)
- eno = getattr(errno, name)
- cls = self._geterrnoclass(eno)
- setattr(self, name, cls)
- return cls
-
- def _geterrnoclass(self, eno):
- try:
- return self._errno2class[eno]
- except KeyError:
- clsname = errno.errorcode.get(eno, "UnknownErrno%d" %(eno,))
- errorcls = type(Error)(clsname, (Error,),
- {'__module__':'py.error',
- '__doc__': os.strerror(eno)})
- self._errno2class[eno] = errorcls
- return errorcls
-
- def checked_call(self, func, *args, **kwargs):
- """ call a function and raise an errno-exception if applicable. """
- __tracebackhide__ = True
- try:
- return func(*args, **kwargs)
- except self.Error:
- raise
- except (OSError, EnvironmentError):
- cls, value, tb = sys.exc_info()
- if not hasattr(value, 'errno'):
- raise
- __tracebackhide__ = False
- errno = value.errno
- try:
- if not isinstance(value, WindowsError):
- raise NameError
- except NameError:
- # we are not on Windows, or we got a proper OSError
- cls = self._geterrnoclass(errno)
- else:
- try:
- cls = self._geterrnoclass(_winerrnomap[errno])
- except KeyError:
- raise value
- raise cls("%s%r" % (func.__name__, args))
- __tracebackhide__ = True
-
-
-error = ErrorMaker()
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_iniconfig.py b/tests/wpt/web-platform-tests/tools/py/py/_iniconfig.py
deleted file mode 100644
index 92b50bd853a..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_iniconfig.py
+++ /dev/null
@@ -1,162 +0,0 @@
-""" brain-dead simple parser for ini-style files.
-(C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed
-"""
-__version__ = "0.2.dev2"
-
-__all__ = ['IniConfig', 'ParseError']
-
-COMMENTCHARS = "#;"
-
-class ParseError(Exception):
- def __init__(self, path, lineno, msg):
- Exception.__init__(self, path, lineno, msg)
- self.path = path
- self.lineno = lineno
- self.msg = msg
-
- def __str__(self):
- return "%s:%s: %s" %(self.path, self.lineno+1, self.msg)
-
-class SectionWrapper(object):
- def __init__(self, config, name):
- self.config = config
- self.name = name
-
- def lineof(self, name):
- return self.config.lineof(self.name, name)
-
- def get(self, key, default=None, convert=str):
- return self.config.get(self.name, key, convert=convert, default=default)
-
- def __getitem__(self, key):
- return self.config.sections[self.name][key]
-
- def __iter__(self):
- section = self.config.sections.get(self.name, [])
- def lineof(key):
- return self.config.lineof(self.name, key)
- for name in sorted(section, key=lineof):
- yield name
-
- def items(self):
- for name in self:
- yield name, self[name]
-
-
-class IniConfig(object):
- def __init__(self, path, data=None):
- self.path = str(path) # convenience
- if data is None:
- f = open(self.path)
- try:
- tokens = self._parse(iter(f))
- finally:
- f.close()
- else:
- tokens = self._parse(data.splitlines(True))
-
- self._sources = {}
- self.sections = {}
-
- for lineno, section, name, value in tokens:
- if section is None:
- self._raise(lineno, 'no section header defined')
- self._sources[section, name] = lineno
- if name is None:
- if section in self.sections:
- self._raise(lineno, 'duplicate section %r'%(section, ))
- self.sections[section] = {}
- else:
- if name in self.sections[section]:
- self._raise(lineno, 'duplicate name %r'%(name, ))
- self.sections[section][name] = value
-
- def _raise(self, lineno, msg):
- raise ParseError(self.path, lineno, msg)
-
- def _parse(self, line_iter):
- result = []
- section = None
- for lineno, line in enumerate(line_iter):
- name, data = self._parseline(line, lineno)
- # new value
- if name is not None and data is not None:
- result.append((lineno, section, name, data))
- # new section
- elif name is not None and data is None:
- if not name:
- self._raise(lineno, 'empty section name')
- section = name
- result.append((lineno, section, None, None))
- # continuation
- elif name is None and data is not None:
- if not result:
- self._raise(lineno, 'unexpected value continuation')
- last = result.pop()
- last_name, last_data = last[-2:]
- if last_name is None:
- self._raise(lineno, 'unexpected value continuation')
-
- if last_data:
- data = '%s\n%s' % (last_data, data)
- result.append(last[:-1] + (data,))
- return result
-
- def _parseline(self, line, lineno):
- # blank lines
- if iscommentline(line):
- line = ""
- else:
- line = line.rstrip()
- if not line:
- return None, None
- # section
- if line[0] == '[':
- realline = line
- for c in COMMENTCHARS:
- line = line.split(c)[0].rstrip()
- if line[-1] == "]":
- return line[1:-1], None
- return None, realline.strip()
- # value
- elif not line[0].isspace():
- try:
- name, value = line.split('=', 1)
- if ":" in name:
- raise ValueError()
- except ValueError:
- try:
- name, value = line.split(":", 1)
- except ValueError:
- self._raise(lineno, 'unexpected line: %r' % line)
- return name.strip(), value.strip()
- # continuation
- else:
- return None, line.strip()
-
- def lineof(self, section, name=None):
- lineno = self._sources.get((section, name))
- if lineno is not None:
- return lineno + 1
-
- def get(self, section, name, default=None, convert=str):
- try:
- return convert(self.sections[section][name])
- except KeyError:
- return default
-
- def __getitem__(self, name):
- if name not in self.sections:
- raise KeyError(name)
- return SectionWrapper(self, name)
-
- def __iter__(self):
- for name in sorted(self.sections, key=self.lineof):
- yield SectionWrapper(self, name)
-
- def __contains__(self, arg):
- return arg in self.sections
-
-def iscommentline(line):
- c = line.lstrip()[:1]
- return c in COMMENTCHARS
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_io/terminalwriter.py b/tests/wpt/web-platform-tests/tools/py/py/_io/terminalwriter.py
deleted file mode 100644
index cef1ff58097..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_io/terminalwriter.py
+++ /dev/null
@@ -1,348 +0,0 @@
-"""
-
-Helper functions for writing to terminals and files.
-
-"""
-
-
-import sys, os
-import py
-py3k = sys.version_info[0] >= 3
-from py.builtin import text, bytes
-
-win32_and_ctypes = False
-colorama = None
-if sys.platform == "win32":
- try:
- import colorama
- except ImportError:
- try:
- import ctypes
- win32_and_ctypes = True
- except ImportError:
- pass
-
-
-def _getdimensions():
- import termios,fcntl,struct
- call = fcntl.ioctl(1,termios.TIOCGWINSZ,"\000"*8)
- height,width = struct.unpack( "hhhh", call ) [:2]
- return height, width
-
-
-def get_terminal_width():
- height = width = 0
- try:
- height, width = _getdimensions()
- except py.builtin._sysex:
- raise
- except:
- # pass to fallback below
- pass
-
- if width == 0:
- # FALLBACK:
- # * some exception happened
- # * or this is emacs terminal which reports (0,0)
- width = int(os.environ.get('COLUMNS', 80))
-
- # XXX the windows getdimensions may be bogus, let's sanify a bit
- if width < 40:
- width = 80
- return width
-
-terminal_width = get_terminal_width()
-
-# XXX unify with _escaped func below
-def ansi_print(text, esc, file=None, newline=True, flush=False):
- if file is None:
- file = sys.stderr
- text = text.rstrip()
- if esc and not isinstance(esc, tuple):
- esc = (esc,)
- if esc and sys.platform != "win32" and file.isatty():
- text = (''.join(['\x1b[%sm' % cod for cod in esc]) +
- text +
- '\x1b[0m') # ANSI color code "reset"
- if newline:
- text += '\n'
-
- if esc and win32_and_ctypes and file.isatty():
- if 1 in esc:
- bold = True
- esc = tuple([x for x in esc if x != 1])
- else:
- bold = False
- esctable = {() : FOREGROUND_WHITE, # normal
- (31,): FOREGROUND_RED, # red
- (32,): FOREGROUND_GREEN, # green
- (33,): FOREGROUND_GREEN|FOREGROUND_RED, # yellow
- (34,): FOREGROUND_BLUE, # blue
- (35,): FOREGROUND_BLUE|FOREGROUND_RED, # purple
- (36,): FOREGROUND_BLUE|FOREGROUND_GREEN, # cyan
- (37,): FOREGROUND_WHITE, # white
- (39,): FOREGROUND_WHITE, # reset
- }
- attr = esctable.get(esc, FOREGROUND_WHITE)
- if bold:
- attr |= FOREGROUND_INTENSITY
- STD_OUTPUT_HANDLE = -11
- STD_ERROR_HANDLE = -12
- if file is sys.stderr:
- handle = GetStdHandle(STD_ERROR_HANDLE)
- else:
- handle = GetStdHandle(STD_OUTPUT_HANDLE)
- oldcolors = GetConsoleInfo(handle).wAttributes
- attr |= (oldcolors & 0x0f0)
- SetConsoleTextAttribute(handle, attr)
- while len(text) > 32768:
- file.write(text[:32768])
- text = text[32768:]
- if text:
- file.write(text)
- SetConsoleTextAttribute(handle, oldcolors)
- else:
- file.write(text)
-
- if flush:
- file.flush()
-
-def should_do_markup(file):
- if os.environ.get('PY_COLORS') == '1':
- return True
- if os.environ.get('PY_COLORS') == '0':
- return False
- return hasattr(file, 'isatty') and file.isatty() \
- and os.environ.get('TERM') != 'dumb' \
- and not (sys.platform.startswith('java') and os._name == 'nt')
-
-class TerminalWriter(object):
- _esctable = dict(black=30, red=31, green=32, yellow=33,
- blue=34, purple=35, cyan=36, white=37,
- Black=40, Red=41, Green=42, Yellow=43,
- Blue=44, Purple=45, Cyan=46, White=47,
- bold=1, light=2, blink=5, invert=7)
-
- # XXX deprecate stringio argument
- def __init__(self, file=None, stringio=False, encoding=None):
- if file is None:
- if stringio:
- self.stringio = file = py.io.TextIO()
- else:
- file = py.std.sys.stdout
- elif py.builtin.callable(file) and not (
- hasattr(file, "write") and hasattr(file, "flush")):
- file = WriteFile(file, encoding=encoding)
- if hasattr(file, "isatty") and file.isatty() and colorama:
- file = colorama.AnsiToWin32(file).stream
- self.encoding = encoding or getattr(file, 'encoding', "utf-8")
- self._file = file
- self.fullwidth = get_terminal_width()
- self.hasmarkup = should_do_markup(file)
- self._lastlen = 0
-
- def _escaped(self, text, esc):
- if esc and self.hasmarkup:
- text = (''.join(['\x1b[%sm' % cod for cod in esc]) +
- text +'\x1b[0m')
- return text
-
- def markup(self, text, **kw):
- esc = []
- for name in kw:
- if name not in self._esctable:
- raise ValueError("unknown markup: %r" %(name,))
- if kw[name]:
- esc.append(self._esctable[name])
- return self._escaped(text, tuple(esc))
-
- def sep(self, sepchar, title=None, fullwidth=None, **kw):
- if fullwidth is None:
- fullwidth = self.fullwidth
- # the goal is to have the line be as long as possible
- # under the condition that len(line) <= fullwidth
- if sys.platform == "win32":
- # if we print in the last column on windows we are on a
- # new line but there is no way to verify/neutralize this
- # (we may not know the exact line width)
- # so let's be defensive to avoid empty lines in the output
- fullwidth -= 1
- if title is not None:
- # we want 2 + 2*len(fill) + len(title) <= fullwidth
- # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth
- # 2*len(sepchar)*N <= fullwidth - len(title) - 2
- # N <= (fullwidth - len(title) - 2) // (2*len(sepchar))
- N = (fullwidth - len(title) - 2) // (2*len(sepchar))
- fill = sepchar * N
- line = "%s %s %s" % (fill, title, fill)
- else:
- # we want len(sepchar)*N <= fullwidth
- # i.e. N <= fullwidth // len(sepchar)
- line = sepchar * (fullwidth // len(sepchar))
- # in some situations there is room for an extra sepchar at the right,
- # in particular if we consider that with a sepchar like "_ " the
- # trailing space is not important at the end of the line
- if len(line) + len(sepchar.rstrip()) <= fullwidth:
- line += sepchar.rstrip()
-
- self.line(line, **kw)
-
- def write(self, msg, **kw):
- if msg:
- if not isinstance(msg, (bytes, text)):
- msg = text(msg)
- if self.hasmarkup and kw:
- markupmsg = self.markup(msg, **kw)
- else:
- markupmsg = msg
- write_out(self._file, markupmsg)
-
- def line(self, s='', **kw):
- self.write(s, **kw)
- self._checkfill(s)
- self.write('\n')
-
- def reline(self, line, **kw):
- if not self.hasmarkup:
- raise ValueError("cannot use rewrite-line without terminal")
- self.write(line, **kw)
- self._checkfill(line)
- self.write('\r')
- self._lastlen = len(line)
-
- def _checkfill(self, line):
- diff2last = self._lastlen - len(line)
- if diff2last > 0:
- self.write(" " * diff2last)
-
-class Win32ConsoleWriter(TerminalWriter):
- def write(self, msg, **kw):
- if msg:
- if not isinstance(msg, (bytes, text)):
- msg = text(msg)
- oldcolors = None
- if self.hasmarkup and kw:
- handle = GetStdHandle(STD_OUTPUT_HANDLE)
- oldcolors = GetConsoleInfo(handle).wAttributes
- default_bg = oldcolors & 0x00F0
- attr = default_bg
- if kw.pop('bold', False):
- attr |= FOREGROUND_INTENSITY
-
- if kw.pop('red', False):
- attr |= FOREGROUND_RED
- elif kw.pop('blue', False):
- attr |= FOREGROUND_BLUE
- elif kw.pop('green', False):
- attr |= FOREGROUND_GREEN
- elif kw.pop('yellow', False):
- attr |= FOREGROUND_GREEN|FOREGROUND_RED
- else:
- attr |= oldcolors & 0x0007
-
- SetConsoleTextAttribute(handle, attr)
- write_out(self._file, msg)
- if oldcolors:
- SetConsoleTextAttribute(handle, oldcolors)
-
-class WriteFile(object):
- def __init__(self, writemethod, encoding=None):
- self.encoding = encoding
- self._writemethod = writemethod
-
- def write(self, data):
- if self.encoding:
- data = data.encode(self.encoding, "replace")
- self._writemethod(data)
-
- def flush(self):
- return
-
-
-if win32_and_ctypes:
- TerminalWriter = Win32ConsoleWriter
- import ctypes
- from ctypes import wintypes
-
- # ctypes access to the Windows console
- STD_OUTPUT_HANDLE = -11
- STD_ERROR_HANDLE = -12
- FOREGROUND_BLACK = 0x0000 # black text
- FOREGROUND_BLUE = 0x0001 # text color contains blue.
- FOREGROUND_GREEN = 0x0002 # text color contains green.
- FOREGROUND_RED = 0x0004 # text color contains red.
- FOREGROUND_WHITE = 0x0007
- FOREGROUND_INTENSITY = 0x0008 # text color is intensified.
- BACKGROUND_BLACK = 0x0000 # background color black
- BACKGROUND_BLUE = 0x0010 # background color contains blue.
- BACKGROUND_GREEN = 0x0020 # background color contains green.
- BACKGROUND_RED = 0x0040 # background color contains red.
- BACKGROUND_WHITE = 0x0070
- BACKGROUND_INTENSITY = 0x0080 # background color is intensified.
-
- SHORT = ctypes.c_short
- class COORD(ctypes.Structure):
- _fields_ = [('X', SHORT),
- ('Y', SHORT)]
- class SMALL_RECT(ctypes.Structure):
- _fields_ = [('Left', SHORT),
- ('Top', SHORT),
- ('Right', SHORT),
- ('Bottom', SHORT)]
- class CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
- _fields_ = [('dwSize', COORD),
- ('dwCursorPosition', COORD),
- ('wAttributes', wintypes.WORD),
- ('srWindow', SMALL_RECT),
- ('dwMaximumWindowSize', COORD)]
-
- _GetStdHandle = ctypes.windll.kernel32.GetStdHandle
- _GetStdHandle.argtypes = [wintypes.DWORD]
- _GetStdHandle.restype = wintypes.HANDLE
- def GetStdHandle(kind):
- return _GetStdHandle(kind)
-
- SetConsoleTextAttribute = ctypes.windll.kernel32.SetConsoleTextAttribute
- SetConsoleTextAttribute.argtypes = [wintypes.HANDLE, wintypes.WORD]
- SetConsoleTextAttribute.restype = wintypes.BOOL
-
- _GetConsoleScreenBufferInfo = \
- ctypes.windll.kernel32.GetConsoleScreenBufferInfo
- _GetConsoleScreenBufferInfo.argtypes = [wintypes.HANDLE,
- ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO)]
- _GetConsoleScreenBufferInfo.restype = wintypes.BOOL
- def GetConsoleInfo(handle):
- info = CONSOLE_SCREEN_BUFFER_INFO()
- _GetConsoleScreenBufferInfo(handle, ctypes.byref(info))
- return info
-
- def _getdimensions():
- handle = GetStdHandle(STD_OUTPUT_HANDLE)
- info = GetConsoleInfo(handle)
- # Substract one from the width, otherwise the cursor wraps
- # and the ending \n causes an empty line to display.
- return info.dwSize.Y, info.dwSize.X - 1
-
-def write_out(fil, msg):
- # XXX sometimes "msg" is of type bytes, sometimes text which
- # complicates the situation. Should we try to enforce unicode?
- try:
- # on py27 and above writing out to sys.stdout with an encoding
- # should usually work for unicode messages (if the encoding is
- # capable of it)
- fil.write(msg)
- except UnicodeEncodeError:
- # on py26 it might not work because stdout expects bytes
- if fil.encoding:
- try:
- fil.write(msg.encode(fil.encoding))
- except UnicodeEncodeError:
- # it might still fail if the encoding is not capable
- pass
- else:
- fil.flush()
- return
- # fallback: escape all unicode characters
- msg = msg.encode("unicode-escape").decode("ascii")
- fil.write(msg)
- fil.flush()
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_log/log.py b/tests/wpt/web-platform-tests/tools/py/py/_log/log.py
deleted file mode 100644
index ce47e8c754a..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_log/log.py
+++ /dev/null
@@ -1,186 +0,0 @@
-"""
-basic logging functionality based on a producer/consumer scheme.
-
-XXX implement this API: (maybe put it into slogger.py?)
-
- log = Logger(
- info=py.log.STDOUT,
- debug=py.log.STDOUT,
- command=None)
- log.info("hello", "world")
- log.command("hello", "world")
-
- log = Logger(info=Logger(something=...),
- debug=py.log.STDOUT,
- command=None)
-"""
-import py, sys
-
-class Message(object):
- def __init__(self, keywords, args):
- self.keywords = keywords
- self.args = args
-
- def content(self):
- return " ".join(map(str, self.args))
-
- def prefix(self):
- return "[%s] " % (":".join(self.keywords))
-
- def __str__(self):
- return self.prefix() + self.content()
-
-
-class Producer(object):
- """ (deprecated) Log producer API which sends messages to be logged
- to a 'consumer' object, which then prints them to stdout,
- stderr, files, etc. Used extensively by PyPy-1.1.
- """
-
- Message = Message # to allow later customization
- keywords2consumer = {}
-
- def __init__(self, keywords, keywordmapper=None, **kw):
- if hasattr(keywords, 'split'):
- keywords = tuple(keywords.split())
- self._keywords = keywords
- if keywordmapper is None:
- keywordmapper = default_keywordmapper
- self._keywordmapper = keywordmapper
-
- def __repr__(self):
- return "<py.log.Producer %s>" % ":".join(self._keywords)
-
- def __getattr__(self, name):
- if '_' in name:
- raise AttributeError(name)
- producer = self.__class__(self._keywords + (name,))
- setattr(self, name, producer)
- return producer
-
- def __call__(self, *args):
- """ write a message to the appropriate consumer(s) """
- func = self._keywordmapper.getconsumer(self._keywords)
- if func is not None:
- func(self.Message(self._keywords, args))
-
-class KeywordMapper:
- def __init__(self):
- self.keywords2consumer = {}
-
- def getstate(self):
- return self.keywords2consumer.copy()
- def setstate(self, state):
- self.keywords2consumer.clear()
- self.keywords2consumer.update(state)
-
- def getconsumer(self, keywords):
- """ return a consumer matching the given keywords.
-
- tries to find the most suitable consumer by walking, starting from
- the back, the list of keywords, the first consumer matching a
- keyword is returned (falling back to py.log.default)
- """
- for i in range(len(keywords), 0, -1):
- try:
- return self.keywords2consumer[keywords[:i]]
- except KeyError:
- continue
- return self.keywords2consumer.get('default', default_consumer)
-
- def setconsumer(self, keywords, consumer):
- """ set a consumer for a set of keywords. """
- # normalize to tuples
- if isinstance(keywords, str):
- keywords = tuple(filter(None, keywords.split()))
- elif hasattr(keywords, '_keywords'):
- keywords = keywords._keywords
- elif not isinstance(keywords, tuple):
- raise TypeError("key %r is not a string or tuple" % (keywords,))
- if consumer is not None and not py.builtin.callable(consumer):
- if not hasattr(consumer, 'write'):
- raise TypeError(
- "%r should be None, callable or file-like" % (consumer,))
- consumer = File(consumer)
- self.keywords2consumer[keywords] = consumer
-
-def default_consumer(msg):
- """ the default consumer, prints the message to stdout (using 'print') """
- sys.stderr.write(str(msg)+"\n")
-
-default_keywordmapper = KeywordMapper()
-
-def setconsumer(keywords, consumer):
- default_keywordmapper.setconsumer(keywords, consumer)
-
-def setstate(state):
- default_keywordmapper.setstate(state)
-def getstate():
- return default_keywordmapper.getstate()
-
-#
-# Consumers
-#
-
-class File(object):
- """ log consumer wrapping a file(-like) object """
- def __init__(self, f):
- assert hasattr(f, 'write')
- #assert isinstance(f, file) or not hasattr(f, 'open')
- self._file = f
-
- def __call__(self, msg):
- """ write a message to the log """
- self._file.write(str(msg) + "\n")
- if hasattr(self._file, 'flush'):
- self._file.flush()
-
-class Path(object):
- """ log consumer that opens and writes to a Path """
- def __init__(self, filename, append=False,
- delayed_create=False, buffering=False):
- self._append = append
- self._filename = str(filename)
- self._buffering = buffering
- if not delayed_create:
- self._openfile()
-
- def _openfile(self):
- mode = self._append and 'a' or 'w'
- f = open(self._filename, mode)
- self._file = f
-
- def __call__(self, msg):
- """ write a message to the log """
- if not hasattr(self, "_file"):
- self._openfile()
- self._file.write(str(msg) + "\n")
- if not self._buffering:
- self._file.flush()
-
-def STDOUT(msg):
- """ consumer that writes to sys.stdout """
- sys.stdout.write(str(msg)+"\n")
-
-def STDERR(msg):
- """ consumer that writes to sys.stderr """
- sys.stderr.write(str(msg)+"\n")
-
-class Syslog:
- """ consumer that writes to the syslog daemon """
-
- def __init__(self, priority = None):
- if priority is None:
- priority = self.LOG_INFO
- self.priority = priority
-
- def __call__(self, msg):
- """ write a message to the log """
- py.std.syslog.syslog(self.priority, str(msg))
-
-for _prio in "EMERG ALERT CRIT ERR WARNING NOTICE INFO DEBUG".split():
- _prio = "LOG_" + _prio
- try:
- setattr(Syslog, _prio, getattr(py.std.syslog, _prio))
- except AttributeError:
- pass
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_log/warning.py b/tests/wpt/web-platform-tests/tools/py/py/_log/warning.py
deleted file mode 100644
index 722e31e910d..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_log/warning.py
+++ /dev/null
@@ -1,76 +0,0 @@
-import py, sys
-
-class DeprecationWarning(DeprecationWarning):
- def __init__(self, msg, path, lineno):
- self.msg = msg
- self.path = path
- self.lineno = lineno
- def __repr__(self):
- return "%s:%d: %s" %(self.path, self.lineno+1, self.msg)
- def __str__(self):
- return self.msg
-
-def _apiwarn(startversion, msg, stacklevel=2, function=None):
- # below is mostly COPIED from python2.4/warnings.py's def warn()
- # Get context information
- if isinstance(stacklevel, str):
- frame = sys._getframe(1)
- level = 1
- found = frame.f_code.co_filename.find(stacklevel) != -1
- while frame:
- co = frame.f_code
- if co.co_filename.find(stacklevel) == -1:
- if found:
- stacklevel = level
- break
- else:
- found = True
- level += 1
- frame = frame.f_back
- else:
- stacklevel = 1
- msg = "%s (since version %s)" %(msg, startversion)
- warn(msg, stacklevel=stacklevel+1, function=function)
-
-def warn(msg, stacklevel=1, function=None):
- if function is not None:
- filename = py.std.inspect.getfile(function)
- lineno = py.code.getrawcode(function).co_firstlineno
- else:
- try:
- caller = sys._getframe(stacklevel)
- except ValueError:
- globals = sys.__dict__
- lineno = 1
- else:
- globals = caller.f_globals
- lineno = caller.f_lineno
- if '__name__' in globals:
- module = globals['__name__']
- else:
- module = "<string>"
- filename = globals.get('__file__')
- if filename:
- fnl = filename.lower()
- if fnl.endswith(".pyc") or fnl.endswith(".pyo"):
- filename = filename[:-1]
- elif fnl.endswith("$py.class"):
- filename = filename.replace('$py.class', '.py')
- else:
- if module == "__main__":
- try:
- filename = sys.argv[0]
- except AttributeError:
- # embedded interpreters don't have sys.argv, see bug #839151
- filename = '__main__'
- if not filename:
- filename = module
- path = py.path.local(filename)
- warning = DeprecationWarning(msg, path, lineno)
- py.std.warnings.warn_explicit(warning, category=Warning,
- filename=str(warning.path),
- lineno=warning.lineno,
- registry=py.std.warnings.__dict__.setdefault(
- "__warningsregistry__", {})
- )
-
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_path/common.py b/tests/wpt/web-platform-tests/tools/py/py/_path/common.py
deleted file mode 100644
index d407434cb2a..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_path/common.py
+++ /dev/null
@@ -1,403 +0,0 @@
-"""
-"""
-import os, sys, posixpath
-import py
-
-# Moved from local.py.
-iswin32 = sys.platform == "win32" or (getattr(os, '_name', False) == 'nt')
-
-class Checkers:
- _depend_on_existence = 'exists', 'link', 'dir', 'file'
-
- def __init__(self, path):
- self.path = path
-
- def dir(self):
- raise NotImplementedError
-
- def file(self):
- raise NotImplementedError
-
- def dotfile(self):
- return self.path.basename.startswith('.')
-
- def ext(self, arg):
- if not arg.startswith('.'):
- arg = '.' + arg
- return self.path.ext == arg
-
- def exists(self):
- raise NotImplementedError
-
- def basename(self, arg):
- return self.path.basename == arg
-
- def basestarts(self, arg):
- return self.path.basename.startswith(arg)
-
- def relto(self, arg):
- return self.path.relto(arg)
-
- def fnmatch(self, arg):
- return self.path.fnmatch(arg)
-
- def endswith(self, arg):
- return str(self.path).endswith(arg)
-
- def _evaluate(self, kw):
- for name, value in kw.items():
- invert = False
- meth = None
- try:
- meth = getattr(self, name)
- except AttributeError:
- if name[:3] == 'not':
- invert = True
- try:
- meth = getattr(self, name[3:])
- except AttributeError:
- pass
- if meth is None:
- raise TypeError(
- "no %r checker available for %r" % (name, self.path))
- try:
- if py.code.getrawcode(meth).co_argcount > 1:
- if (not meth(value)) ^ invert:
- return False
- else:
- if bool(value) ^ bool(meth()) ^ invert:
- return False
- except (py.error.ENOENT, py.error.ENOTDIR, py.error.EBUSY):
- # EBUSY feels not entirely correct,
- # but its kind of necessary since ENOMEDIUM
- # is not accessible in python
- for name in self._depend_on_existence:
- if name in kw:
- if kw.get(name):
- return False
- name = 'not' + name
- if name in kw:
- if not kw.get(name):
- return False
- return True
-
-class NeverRaised(Exception):
- pass
-
-class PathBase(object):
- """ shared implementation for filesystem path objects."""
- Checkers = Checkers
-
- def __div__(self, other):
- return self.join(str(other))
- __truediv__ = __div__ # py3k
-
- def basename(self):
- """ basename part of path. """
- return self._getbyspec('basename')[0]
- basename = property(basename, None, None, basename.__doc__)
-
- def dirname(self):
- """ dirname part of path. """
- return self._getbyspec('dirname')[0]
- dirname = property(dirname, None, None, dirname.__doc__)
-
- def purebasename(self):
- """ pure base name of the path."""
- return self._getbyspec('purebasename')[0]
- purebasename = property(purebasename, None, None, purebasename.__doc__)
-
- def ext(self):
- """ extension of the path (including the '.')."""
- return self._getbyspec('ext')[0]
- ext = property(ext, None, None, ext.__doc__)
-
- def dirpath(self, *args, **kwargs):
- """ return the directory path joined with any given path arguments. """
- return self.new(basename='').join(*args, **kwargs)
-
- def read_binary(self):
- """ read and return a bytestring from reading the path. """
- with self.open('rb') as f:
- return f.read()
-
- def read_text(self, encoding):
- """ read and return a Unicode string from reading the path. """
- with self.open("r", encoding=encoding) as f:
- return f.read()
-
-
- def read(self, mode='r'):
- """ read and return a bytestring from reading the path. """
- with self.open(mode) as f:
- return f.read()
-
- def readlines(self, cr=1):
- """ read and return a list of lines from the path. if cr is False, the
-newline will be removed from the end of each line. """
- if not cr:
- content = self.read('rU')
- return content.split('\n')
- else:
- f = self.open('rU')
- try:
- return f.readlines()
- finally:
- f.close()
-
- def load(self):
- """ (deprecated) return object unpickled from self.read() """
- f = self.open('rb')
- try:
- return py.error.checked_call(py.std.pickle.load, f)
- finally:
- f.close()
-
- def move(self, target):
- """ move this path to target. """
- if target.relto(self):
- raise py.error.EINVAL(target,
- "cannot move path into a subdirectory of itself")
- try:
- self.rename(target)
- except py.error.EXDEV: # invalid cross-device link
- self.copy(target)
- self.remove()
-
- def __repr__(self):
- """ return a string representation of this path. """
- return repr(str(self))
-
- def check(self, **kw):
- """ check a path for existence and properties.
-
- Without arguments, return True if the path exists, otherwise False.
-
- valid checkers::
-
- file=1 # is a file
- file=0 # is not a file (may not even exist)
- dir=1 # is a dir
- link=1 # is a link
- exists=1 # exists
-
- You can specify multiple checker definitions, for example::
-
- path.check(file=1, link=1) # a link pointing to a file
- """
- if not kw:
- kw = {'exists' : 1}
- return self.Checkers(self)._evaluate(kw)
-
- def fnmatch(self, pattern):
- """return true if the basename/fullname matches the glob-'pattern'.
-
- valid pattern characters::
-
- * matches everything
- ? matches any single character
- [seq] matches any character in seq
- [!seq] matches any char not in seq
-
- If the pattern contains a path-separator then the full path
- is used for pattern matching and a '*' is prepended to the
- pattern.
-
- if the pattern doesn't contain a path-separator the pattern
- is only matched against the basename.
- """
- return FNMatcher(pattern)(self)
-
- def relto(self, relpath):
- """ return a string which is the relative part of the path
- to the given 'relpath'.
- """
- if not isinstance(relpath, (str, PathBase)):
- raise TypeError("%r: not a string or path object" %(relpath,))
- strrelpath = str(relpath)
- if strrelpath and strrelpath[-1] != self.sep:
- strrelpath += self.sep
- #assert strrelpath[-1] == self.sep
- #assert strrelpath[-2] != self.sep
- strself = self.strpath
- if sys.platform == "win32" or getattr(os, '_name', None) == 'nt':
- if os.path.normcase(strself).startswith(
- os.path.normcase(strrelpath)):
- return strself[len(strrelpath):]
- elif strself.startswith(strrelpath):
- return strself[len(strrelpath):]
- return ""
-
- def ensure_dir(self, *args):
- """ ensure the path joined with args is a directory. """
- return self.ensure(*args, **{"dir": True})
-
- def bestrelpath(self, dest):
- """ return a string which is a relative path from self
- (assumed to be a directory) to dest such that
- self.join(bestrelpath) == dest and if not such
- path can be determined return dest.
- """
- try:
- if self == dest:
- return os.curdir
- base = self.common(dest)
- if not base: # can be the case on windows
- return str(dest)
- self2base = self.relto(base)
- reldest = dest.relto(base)
- if self2base:
- n = self2base.count(self.sep) + 1
- else:
- n = 0
- l = [os.pardir] * n
- if reldest:
- l.append(reldest)
- target = dest.sep.join(l)
- return target
- except AttributeError:
- return str(dest)
-
- def exists(self):
- return self.check()
-
- def isdir(self):
- return self.check(dir=1)
-
- def isfile(self):
- return self.check(file=1)
-
- def parts(self, reverse=False):
- """ return a root-first list of all ancestor directories
- plus the path itself.
- """
- current = self
- l = [self]
- while 1:
- last = current
- current = current.dirpath()
- if last == current:
- break
- l.append(current)
- if not reverse:
- l.reverse()
- return l
-
- def common(self, other):
- """ return the common part shared with the other path
- or None if there is no common part.
- """
- last = None
- for x, y in zip(self.parts(), other.parts()):
- if x != y:
- return last
- last = x
- return last
-
- def __add__(self, other):
- """ return new path object with 'other' added to the basename"""
- return self.new(basename=self.basename+str(other))
-
- def __cmp__(self, other):
- """ return sort value (-1, 0, +1). """
- try:
- return cmp(self.strpath, other.strpath)
- except AttributeError:
- return cmp(str(self), str(other)) # self.path, other.path)
-
- def __lt__(self, other):
- try:
- return self.strpath < other.strpath
- except AttributeError:
- return str(self) < str(other)
-
- def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False):
- """ yields all paths below the current one
-
- fil is a filter (glob pattern or callable), if not matching the
- path will not be yielded, defaulting to None (everything is
- returned)
-
- rec is a filter (glob pattern or callable) that controls whether
- a node is descended, defaulting to None
-
- ignore is an Exception class that is ignoredwhen calling dirlist()
- on any of the paths (by default, all exceptions are reported)
-
- bf if True will cause a breadthfirst search instead of the
- default depthfirst. Default: False
-
- sort if True will sort entries within each directory level.
- """
- for x in Visitor(fil, rec, ignore, bf, sort).gen(self):
- yield x
-
- def _sortlist(self, res, sort):
- if sort:
- if hasattr(sort, '__call__'):
- res.sort(sort)
- else:
- res.sort()
-
- def samefile(self, other):
- """ return True if other refers to the same stat object as self. """
- return self.strpath == str(other)
-
-class Visitor:
- def __init__(self, fil, rec, ignore, bf, sort):
- if isinstance(fil, str):
- fil = FNMatcher(fil)
- if isinstance(rec, str):
- self.rec = FNMatcher(rec)
- elif not hasattr(rec, '__call__') and rec:
- self.rec = lambda path: True
- else:
- self.rec = rec
- self.fil = fil
- self.ignore = ignore
- self.breadthfirst = bf
- self.optsort = sort and sorted or (lambda x: x)
-
- def gen(self, path):
- try:
- entries = path.listdir()
- except self.ignore:
- return
- rec = self.rec
- dirs = self.optsort([p for p in entries
- if p.check(dir=1) and (rec is None or rec(p))])
- if not self.breadthfirst:
- for subdir in dirs:
- for p in self.gen(subdir):
- yield p
- for p in self.optsort(entries):
- if self.fil is None or self.fil(p):
- yield p
- if self.breadthfirst:
- for subdir in dirs:
- for p in self.gen(subdir):
- yield p
-
-class FNMatcher:
- def __init__(self, pattern):
- self.pattern = pattern
-
- def __call__(self, path):
- pattern = self.pattern
-
- if (pattern.find(path.sep) == -1 and
- iswin32 and
- pattern.find(posixpath.sep) != -1):
- # Running on Windows, the pattern has no Windows path separators,
- # and the pattern has one or more Posix path separators. Replace
- # the Posix path separators with the Windows path separator.
- pattern = pattern.replace(posixpath.sep, path.sep)
-
- if pattern.find(path.sep) == -1:
- name = path.basename
- else:
- name = str(path) # path.strpath # XXX svn?
- if not os.path.isabs(pattern):
- pattern = '*' + path.sep + pattern
- return py.std.fnmatch.fnmatch(name, pattern)
-
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_path/local.py b/tests/wpt/web-platform-tests/tools/py/py/_path/local.py
deleted file mode 100644
index d569404ec21..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_path/local.py
+++ /dev/null
@@ -1,911 +0,0 @@
-"""
-local path implementation.
-"""
-from __future__ import with_statement
-
-from contextlib import contextmanager
-import sys, os, re, atexit, io
-import py
-from py._path import common
-from py._path.common import iswin32
-from stat import S_ISLNK, S_ISDIR, S_ISREG
-
-from os.path import abspath, normpath, isabs, exists, isdir, isfile, islink, dirname
-
-if sys.version_info > (3,0):
- def map_as_list(func, iter):
- return list(map(func, iter))
-else:
- map_as_list = map
-
-class Stat(object):
- def __getattr__(self, name):
- return getattr(self._osstatresult, "st_" + name)
-
- def __init__(self, path, osstatresult):
- self.path = path
- self._osstatresult = osstatresult
-
- @property
- def owner(self):
- if iswin32:
- raise NotImplementedError("XXX win32")
- import pwd
- entry = py.error.checked_call(pwd.getpwuid, self.uid)
- return entry[0]
-
- @property
- def group(self):
- """ return group name of file. """
- if iswin32:
- raise NotImplementedError("XXX win32")
- import grp
- entry = py.error.checked_call(grp.getgrgid, self.gid)
- return entry[0]
-
- def isdir(self):
- return S_ISDIR(self._osstatresult.st_mode)
-
- def isfile(self):
- return S_ISREG(self._osstatresult.st_mode)
-
- def islink(self):
- st = self.path.lstat()
- return S_ISLNK(self._osstatresult.st_mode)
-
-class PosixPath(common.PathBase):
- def chown(self, user, group, rec=0):
- """ change ownership to the given user and group.
- user and group may be specified by a number or
- by a name. if rec is True change ownership
- recursively.
- """
- uid = getuserid(user)
- gid = getgroupid(group)
- if rec:
- for x in self.visit(rec=lambda x: x.check(link=0)):
- if x.check(link=0):
- py.error.checked_call(os.chown, str(x), uid, gid)
- py.error.checked_call(os.chown, str(self), uid, gid)
-
- def readlink(self):
- """ return value of a symbolic link. """
- return py.error.checked_call(os.readlink, self.strpath)
-
- def mklinkto(self, oldname):
- """ posix style hard link to another name. """
- py.error.checked_call(os.link, str(oldname), str(self))
-
- def mksymlinkto(self, value, absolute=1):
- """ create a symbolic link with the given value (pointing to another name). """
- if absolute:
- py.error.checked_call(os.symlink, str(value), self.strpath)
- else:
- base = self.common(value)
- # with posix local paths '/' is always a common base
- relsource = self.__class__(value).relto(base)
- reldest = self.relto(base)
- n = reldest.count(self.sep)
- target = self.sep.join(('..', )*n + (relsource, ))
- py.error.checked_call(os.symlink, target, self.strpath)
-
-def getuserid(user):
- import pwd
- if not isinstance(user, int):
- user = pwd.getpwnam(user)[2]
- return user
-
-def getgroupid(group):
- import grp
- if not isinstance(group, int):
- group = grp.getgrnam(group)[2]
- return group
-
-FSBase = not iswin32 and PosixPath or common.PathBase
-
-class LocalPath(FSBase):
- """ object oriented interface to os.path and other local filesystem
- related information.
- """
- class ImportMismatchError(ImportError):
- """ raised on pyimport() if there is a mismatch of __file__'s"""
-
- sep = os.sep
- class Checkers(common.Checkers):
- def _stat(self):
- try:
- return self._statcache
- except AttributeError:
- try:
- self._statcache = self.path.stat()
- except py.error.ELOOP:
- self._statcache = self.path.lstat()
- return self._statcache
-
- def dir(self):
- return S_ISDIR(self._stat().mode)
-
- def file(self):
- return S_ISREG(self._stat().mode)
-
- def exists(self):
- return self._stat()
-
- def link(self):
- st = self.path.lstat()
- return S_ISLNK(st.mode)
-
- def __init__(self, path=None, expanduser=False):
- """ Initialize and return a local Path instance.
-
- Path can be relative to the current directory.
- If path is None it defaults to the current working directory.
- If expanduser is True, tilde-expansion is performed.
- Note that Path instances always carry an absolute path.
- Note also that passing in a local path object will simply return
- the exact same path object. Use new() to get a new copy.
- """
- if path is None:
- self.strpath = py.error.checked_call(os.getcwd)
- elif isinstance(path, common.PathBase):
- self.strpath = path.strpath
- elif isinstance(path, py.builtin._basestring):
- if expanduser:
- path = os.path.expanduser(path)
- self.strpath = abspath(path)
- else:
- raise ValueError("can only pass None, Path instances "
- "or non-empty strings to LocalPath")
-
- def __hash__(self):
- return hash(self.strpath)
-
- def __eq__(self, other):
- s1 = self.strpath
- s2 = getattr(other, "strpath", other)
- if iswin32:
- s1 = s1.lower()
- try:
- s2 = s2.lower()
- except AttributeError:
- return False
- return s1 == s2
-
- def __ne__(self, other):
- return not (self == other)
-
- def __lt__(self, other):
- return self.strpath < getattr(other, "strpath", other)
-
- def __gt__(self, other):
- return self.strpath > getattr(other, "strpath", other)
-
- def samefile(self, other):
- """ return True if 'other' references the same file as 'self'.
- """
- other = getattr(other, "strpath", other)
- if not isabs(other):
- other = abspath(other)
- if self == other:
- return True
- if iswin32:
- return False # there is no samefile
- return py.error.checked_call(
- os.path.samefile, self.strpath, other)
-
- def remove(self, rec=1, ignore_errors=False):
- """ remove a file or directory (or a directory tree if rec=1).
- if ignore_errors is True, errors while removing directories will
- be ignored.
- """
- if self.check(dir=1, link=0):
- if rec:
- # force remove of readonly files on windows
- if iswin32:
- self.chmod(448, rec=1) # octcal 0700
- py.error.checked_call(py.std.shutil.rmtree, self.strpath,
- ignore_errors=ignore_errors)
- else:
- py.error.checked_call(os.rmdir, self.strpath)
- else:
- if iswin32:
- self.chmod(448) # octcal 0700
- py.error.checked_call(os.remove, self.strpath)
-
- def computehash(self, hashtype="md5", chunksize=524288):
- """ return hexdigest of hashvalue for this file. """
- try:
- try:
- import hashlib as mod
- except ImportError:
- if hashtype == "sha1":
- hashtype = "sha"
- mod = __import__(hashtype)
- hash = getattr(mod, hashtype)()
- except (AttributeError, ImportError):
- raise ValueError("Don't know how to compute %r hash" %(hashtype,))
- f = self.open('rb')
- try:
- while 1:
- buf = f.read(chunksize)
- if not buf:
- return hash.hexdigest()
- hash.update(buf)
- finally:
- f.close()
-
- def new(self, **kw):
- """ create a modified version of this path.
- the following keyword arguments modify various path parts::
-
- a:/some/path/to/a/file.ext
- xx drive
- xxxxxxxxxxxxxxxxx dirname
- xxxxxxxx basename
- xxxx purebasename
- xxx ext
- """
- obj = object.__new__(self.__class__)
- if not kw:
- obj.strpath = self.strpath
- return obj
- drive, dirname, basename, purebasename,ext = self._getbyspec(
- "drive,dirname,basename,purebasename,ext")
- if 'basename' in kw:
- if 'purebasename' in kw or 'ext' in kw:
- raise ValueError("invalid specification %r" % kw)
- else:
- pb = kw.setdefault('purebasename', purebasename)
- try:
- ext = kw['ext']
- except KeyError:
- pass
- else:
- if ext and not ext.startswith('.'):
- ext = '.' + ext
- kw['basename'] = pb + ext
-
- if ('dirname' in kw and not kw['dirname']):
- kw['dirname'] = drive
- else:
- kw.setdefault('dirname', dirname)
- kw.setdefault('sep', self.sep)
- obj.strpath = normpath(
- "%(dirname)s%(sep)s%(basename)s" % kw)
- return obj
-
- def _getbyspec(self, spec):
- """ see new for what 'spec' can be. """
- res = []
- parts = self.strpath.split(self.sep)
-
- args = filter(None, spec.split(',') )
- append = res.append
- for name in args:
- if name == 'drive':
- append(parts[0])
- elif name == 'dirname':
- append(self.sep.join(parts[:-1]))
- else:
- basename = parts[-1]
- if name == 'basename':
- append(basename)
- else:
- i = basename.rfind('.')
- if i == -1:
- purebasename, ext = basename, ''
- else:
- purebasename, ext = basename[:i], basename[i:]
- if name == 'purebasename':
- append(purebasename)
- elif name == 'ext':
- append(ext)
- else:
- raise ValueError("invalid part specification %r" % name)
- return res
-
- def dirpath(self, *args, **kwargs):
- """ return the directory path joined with any given path arguments. """
- if not kwargs:
- path = object.__new__(self.__class__)
- path.strpath = dirname(self.strpath)
- if args:
- path = path.join(*args)
- return path
- return super(LocalPath, self).dirpath(*args, **kwargs)
-
- def join(self, *args, **kwargs):
- """ return a new path by appending all 'args' as path
- components. if abs=1 is used restart from root if any
- of the args is an absolute path.
- """
- sep = self.sep
- strargs = [getattr(arg, "strpath", arg) for arg in args]
- strpath = self.strpath
- if kwargs.get('abs'):
- newargs = []
- for arg in reversed(strargs):
- if isabs(arg):
- strpath = arg
- strargs = newargs
- break
- newargs.insert(0, arg)
- for arg in strargs:
- arg = arg.strip(sep)
- if iswin32:
- # allow unix style paths even on windows.
- arg = arg.strip('/')
- arg = arg.replace('/', sep)
- strpath = strpath + sep + arg
- obj = object.__new__(self.__class__)
- obj.strpath = normpath(strpath)
- return obj
-
- def open(self, mode='r', ensure=False, encoding=None):
- """ return an opened file with the given mode.
-
- If ensure is True, create parent directories if needed.
- """
- if ensure:
- self.dirpath().ensure(dir=1)
- if encoding:
- return py.error.checked_call(io.open, self.strpath, mode, encoding=encoding)
- return py.error.checked_call(open, self.strpath, mode)
-
- def _fastjoin(self, name):
- child = object.__new__(self.__class__)
- child.strpath = self.strpath + self.sep + name
- return child
-
- def islink(self):
- return islink(self.strpath)
-
- def check(self, **kw):
- if not kw:
- return exists(self.strpath)
- if len(kw) == 1:
- if "dir" in kw:
- return not kw["dir"] ^ isdir(self.strpath)
- if "file" in kw:
- return not kw["file"] ^ isfile(self.strpath)
- return super(LocalPath, self).check(**kw)
-
- _patternchars = set("*?[" + os.path.sep)
- def listdir(self, fil=None, sort=None):
- """ list directory contents, possibly filter by the given fil func
- and possibly sorted.
- """
- if fil is None and sort is None:
- names = py.error.checked_call(os.listdir, self.strpath)
- return map_as_list(self._fastjoin, names)
- if isinstance(fil, py.builtin._basestring):
- if not self._patternchars.intersection(fil):
- child = self._fastjoin(fil)
- if exists(child.strpath):
- return [child]
- return []
- fil = common.FNMatcher(fil)
- names = py.error.checked_call(os.listdir, self.strpath)
- res = []
- for name in names:
- child = self._fastjoin(name)
- if fil is None or fil(child):
- res.append(child)
- self._sortlist(res, sort)
- return res
-
- def size(self):
- """ return size of the underlying file object """
- return self.stat().size
-
- def mtime(self):
- """ return last modification time of the path. """
- return self.stat().mtime
-
- def copy(self, target, mode=False):
- """ copy path to target."""
- if self.check(file=1):
- if target.check(dir=1):
- target = target.join(self.basename)
- assert self!=target
- copychunked(self, target)
- if mode:
- copymode(self.strpath, target.strpath)
- else:
- def rec(p):
- return p.check(link=0)
- for x in self.visit(rec=rec):
- relpath = x.relto(self)
- newx = target.join(relpath)
- newx.dirpath().ensure(dir=1)
- if x.check(link=1):
- newx.mksymlinkto(x.readlink())
- continue
- elif x.check(file=1):
- copychunked(x, newx)
- elif x.check(dir=1):
- newx.ensure(dir=1)
- if mode:
- copymode(x.strpath, newx.strpath)
-
- def rename(self, target):
- """ rename this path to target. """
- target = getattr(target, "strpath", target)
- return py.error.checked_call(os.rename, self.strpath, target)
-
- def dump(self, obj, bin=1):
- """ pickle object into path location"""
- f = self.open('wb')
- try:
- py.error.checked_call(py.std.pickle.dump, obj, f, bin)
- finally:
- f.close()
-
- def mkdir(self, *args):
- """ create & return the directory joined with args. """
- p = self.join(*args)
- py.error.checked_call(os.mkdir, getattr(p, "strpath", p))
- return p
-
- def write_binary(self, data, ensure=False):
- """ write binary data into path. If ensure is True create
- missing parent directories.
- """
- if ensure:
- self.dirpath().ensure(dir=1)
- with self.open('wb') as f:
- f.write(data)
-
- def write_text(self, data, encoding, ensure=False):
- """ write text data into path using the specified encoding.
- If ensure is True create missing parent directories.
- """
- if ensure:
- self.dirpath().ensure(dir=1)
- with self.open('w', encoding=encoding) as f:
- f.write(data)
-
- def write(self, data, mode='w', ensure=False):
- """ write data into path. If ensure is True create
- missing parent directories.
- """
- if ensure:
- self.dirpath().ensure(dir=1)
- if 'b' in mode:
- if not py.builtin._isbytes(data):
- raise ValueError("can only process bytes")
- else:
- if not py.builtin._istext(data):
- if not py.builtin._isbytes(data):
- data = str(data)
- else:
- data = py.builtin._totext(data, sys.getdefaultencoding())
- f = self.open(mode)
- try:
- f.write(data)
- finally:
- f.close()
-
- def _ensuredirs(self):
- parent = self.dirpath()
- if parent == self:
- return self
- if parent.check(dir=0):
- parent._ensuredirs()
- if self.check(dir=0):
- try:
- self.mkdir()
- except py.error.EEXIST:
- # race condition: file/dir created by another thread/process.
- # complain if it is not a dir
- if self.check(dir=0):
- raise
- return self
-
- def ensure(self, *args, **kwargs):
- """ ensure that an args-joined path exists (by default as
- a file). if you specify a keyword argument 'dir=True'
- then the path is forced to be a directory path.
- """
- p = self.join(*args)
- if kwargs.get('dir', 0):
- return p._ensuredirs()
- else:
- p.dirpath()._ensuredirs()
- if not p.check(file=1):
- p.open('w').close()
- return p
-
- def stat(self, raising=True):
- """ Return an os.stat() tuple. """
- if raising == True:
- return Stat(self, py.error.checked_call(os.stat, self.strpath))
- try:
- return Stat(self, os.stat(self.strpath))
- except KeyboardInterrupt:
- raise
- except Exception:
- return None
-
- def lstat(self):
- """ Return an os.lstat() tuple. """
- return Stat(self, py.error.checked_call(os.lstat, self.strpath))
-
- def setmtime(self, mtime=None):
- """ set modification time for the given path. if 'mtime' is None
- (the default) then the file's mtime is set to current time.
-
- Note that the resolution for 'mtime' is platform dependent.
- """
- if mtime is None:
- return py.error.checked_call(os.utime, self.strpath, mtime)
- try:
- return py.error.checked_call(os.utime, self.strpath, (-1, mtime))
- except py.error.EINVAL:
- return py.error.checked_call(os.utime, self.strpath, (self.atime(), mtime))
-
- def chdir(self):
- """ change directory to self and return old current directory """
- try:
- old = self.__class__()
- except py.error.ENOENT:
- old = None
- py.error.checked_call(os.chdir, self.strpath)
- return old
-
-
- @contextmanager
- def as_cwd(self):
- """ return context manager which changes to current dir during the
- managed "with" context. On __enter__ it returns the old dir.
- """
- old = self.chdir()
- try:
- yield old
- finally:
- old.chdir()
-
- def realpath(self):
- """ return a new path which contains no symbolic links."""
- return self.__class__(os.path.realpath(self.strpath))
-
- def atime(self):
- """ return last access time of the path. """
- return self.stat().atime
-
- def __repr__(self):
- return 'local(%r)' % self.strpath
-
- def __str__(self):
- """ return string representation of the Path. """
- return self.strpath
-
- def chmod(self, mode, rec=0):
- """ change permissions to the given mode. If mode is an
- integer it directly encodes the os-specific modes.
- if rec is True perform recursively.
- """
- if not isinstance(mode, int):
- raise TypeError("mode %r must be an integer" % (mode,))
- if rec:
- for x in self.visit(rec=rec):
- py.error.checked_call(os.chmod, str(x), mode)
- py.error.checked_call(os.chmod, self.strpath, mode)
-
- def pypkgpath(self):
- """ return the Python package path by looking for the last
- directory upwards which still contains an __init__.py.
- Return None if a pkgpath can not be determined.
- """
- pkgpath = None
- for parent in self.parts(reverse=True):
- if parent.isdir():
- if not parent.join('__init__.py').exists():
- break
- if not isimportable(parent.basename):
- break
- pkgpath = parent
- return pkgpath
-
- def _ensuresyspath(self, ensuremode, path):
- if ensuremode:
- s = str(path)
- if ensuremode == "append":
- if s not in sys.path:
- sys.path.append(s)
- else:
- if s != sys.path[0]:
- sys.path.insert(0, s)
-
- def pyimport(self, modname=None, ensuresyspath=True):
- """ return path as an imported python module.
-
- If modname is None, look for the containing package
- and construct an according module name.
- The module will be put/looked up in sys.modules.
- if ensuresyspath is True then the root dir for importing
- the file (taking __init__.py files into account) will
- be prepended to sys.path if it isn't there already.
- If ensuresyspath=="append" the root dir will be appended
- if it isn't already contained in sys.path.
- if ensuresyspath is False no modification of syspath happens.
- """
- if not self.check():
- raise py.error.ENOENT(self)
-
- pkgpath = None
- if modname is None:
- pkgpath = self.pypkgpath()
- if pkgpath is not None:
- pkgroot = pkgpath.dirpath()
- names = self.new(ext="").relto(pkgroot).split(self.sep)
- if names[-1] == "__init__":
- names.pop()
- modname = ".".join(names)
- else:
- pkgroot = self.dirpath()
- modname = self.purebasename
-
- self._ensuresyspath(ensuresyspath, pkgroot)
- __import__(modname)
- mod = sys.modules[modname]
- if self.basename == "__init__.py":
- return mod # we don't check anything as we might
- # we in a namespace package ... too icky to check
- modfile = mod.__file__
- if modfile[-4:] in ('.pyc', '.pyo'):
- modfile = modfile[:-1]
- elif modfile.endswith('$py.class'):
- modfile = modfile[:-9] + '.py'
- if modfile.endswith(os.path.sep + "__init__.py"):
- if self.basename != "__init__.py":
- modfile = modfile[:-12]
- try:
- issame = self.samefile(modfile)
- except py.error.ENOENT:
- issame = False
- if not issame:
- raise self.ImportMismatchError(modname, modfile, self)
- return mod
- else:
- try:
- return sys.modules[modname]
- except KeyError:
- # we have a custom modname, do a pseudo-import
- mod = py.std.types.ModuleType(modname)
- mod.__file__ = str(self)
- sys.modules[modname] = mod
- try:
- py.builtin.execfile(str(self), mod.__dict__)
- except:
- del sys.modules[modname]
- raise
- return mod
-
- def sysexec(self, *argv, **popen_opts):
- """ return stdout text from executing a system child process,
- where the 'self' path points to executable.
- The process is directly invoked and not through a system shell.
- """
- from subprocess import Popen, PIPE
- argv = map_as_list(str, argv)
- popen_opts['stdout'] = popen_opts['stderr'] = PIPE
- proc = Popen([str(self)] + argv, **popen_opts)
- stdout, stderr = proc.communicate()
- ret = proc.wait()
- if py.builtin._isbytes(stdout):
- stdout = py.builtin._totext(stdout, sys.getdefaultencoding())
- if ret != 0:
- if py.builtin._isbytes(stderr):
- stderr = py.builtin._totext(stderr, sys.getdefaultencoding())
- raise py.process.cmdexec.Error(ret, ret, str(self),
- stdout, stderr,)
- return stdout
-
- def sysfind(cls, name, checker=None, paths=None):
- """ return a path object found by looking at the systems
- underlying PATH specification. If the checker is not None
- it will be invoked to filter matching paths. If a binary
- cannot be found, None is returned
- Note: This is probably not working on plain win32 systems
- but may work on cygwin.
- """
- if isabs(name):
- p = py.path.local(name)
- if p.check(file=1):
- return p
- else:
- if paths is None:
- if iswin32:
- paths = py.std.os.environ['Path'].split(';')
- if '' not in paths and '.' not in paths:
- paths.append('.')
- try:
- systemroot = os.environ['SYSTEMROOT']
- except KeyError:
- pass
- else:
- paths = [re.sub('%SystemRoot%', systemroot, path)
- for path in paths]
- else:
- paths = py.std.os.environ['PATH'].split(':')
- tryadd = []
- if iswin32:
- tryadd += os.environ['PATHEXT'].split(os.pathsep)
- tryadd.append("")
-
- for x in paths:
- for addext in tryadd:
- p = py.path.local(x).join(name, abs=True) + addext
- try:
- if p.check(file=1):
- if checker:
- if not checker(p):
- continue
- return p
- except py.error.EACCES:
- pass
- return None
- sysfind = classmethod(sysfind)
-
- def _gethomedir(cls):
- try:
- x = os.environ['HOME']
- except KeyError:
- try:
- x = os.environ["HOMEDRIVE"] + os.environ['HOMEPATH']
- except KeyError:
- return None
- return cls(x)
- _gethomedir = classmethod(_gethomedir)
-
- #"""
- #special class constructors for local filesystem paths
- #"""
- def get_temproot(cls):
- """ return the system's temporary directory
- (where tempfiles are usually created in)
- """
- return py.path.local(py.std.tempfile.gettempdir())
- get_temproot = classmethod(get_temproot)
-
- def mkdtemp(cls, rootdir=None):
- """ return a Path object pointing to a fresh new temporary directory
- (which we created ourself).
- """
- import tempfile
- if rootdir is None:
- rootdir = cls.get_temproot()
- return cls(py.error.checked_call(tempfile.mkdtemp, dir=str(rootdir)))
- mkdtemp = classmethod(mkdtemp)
-
- def make_numbered_dir(cls, prefix='session-', rootdir=None, keep=3,
- lock_timeout = 172800): # two days
- """ return unique directory with a number greater than the current
- maximum one. The number is assumed to start directly after prefix.
- if keep is true directories with a number less than (maxnum-keep)
- will be removed.
- """
- if rootdir is None:
- rootdir = cls.get_temproot()
-
- def parse_num(path):
- """ parse the number out of a path (if it matches the prefix) """
- bn = path.basename
- if bn.startswith(prefix):
- try:
- return int(bn[len(prefix):])
- except ValueError:
- pass
-
- # compute the maximum number currently in use with the
- # prefix
- lastmax = None
- while True:
- maxnum = -1
- for path in rootdir.listdir():
- num = parse_num(path)
- if num is not None:
- maxnum = max(maxnum, num)
-
- # make the new directory
- try:
- udir = rootdir.mkdir(prefix + str(maxnum+1))
- except py.error.EEXIST:
- # race condition: another thread/process created the dir
- # in the meantime. Try counting again
- if lastmax == maxnum:
- raise
- lastmax = maxnum
- continue
- break
-
- # put a .lock file in the new directory that will be removed at
- # process exit
- if lock_timeout:
- lockfile = udir.join('.lock')
- mypid = os.getpid()
- if hasattr(lockfile, 'mksymlinkto'):
- lockfile.mksymlinkto(str(mypid))
- else:
- lockfile.write(str(mypid))
- def try_remove_lockfile():
- # in a fork() situation, only the last process should
- # remove the .lock, otherwise the other processes run the
- # risk of seeing their temporary dir disappear. For now
- # we remove the .lock in the parent only (i.e. we assume
- # that the children finish before the parent).
- if os.getpid() != mypid:
- return
- try:
- lockfile.remove()
- except py.error.Error:
- pass
- atexit.register(try_remove_lockfile)
-
- # prune old directories
- if keep:
- for path in rootdir.listdir():
- num = parse_num(path)
- if num is not None and num <= (maxnum - keep):
- lf = path.join('.lock')
- try:
- t1 = lf.lstat().mtime
- t2 = lockfile.lstat().mtime
- if not lock_timeout or abs(t2-t1) < lock_timeout:
- continue # skip directories still locked
- except py.error.Error:
- pass # assume that it means that there is no 'lf'
- try:
- path.remove(rec=1)
- except KeyboardInterrupt:
- raise
- except: # this might be py.error.Error, WindowsError ...
- pass
-
- # make link...
- try:
- username = os.environ['USER'] #linux, et al
- except KeyError:
- try:
- username = os.environ['USERNAME'] #windows
- except KeyError:
- username = 'current'
-
- src = str(udir)
- dest = src[:src.rfind('-')] + '-' + username
- try:
- os.unlink(dest)
- except OSError:
- pass
- try:
- os.symlink(src, dest)
- except (OSError, AttributeError, NotImplementedError):
- pass
-
- return udir
- make_numbered_dir = classmethod(make_numbered_dir)
-
-def copymode(src, dest):
- py.std.shutil.copymode(src, dest)
-
-def copychunked(src, dest):
- chunksize = 524288 # half a meg of bytes
- fsrc = src.open('rb')
- try:
- fdest = dest.open('wb')
- try:
- while 1:
- buf = fsrc.read(chunksize)
- if not buf:
- break
- fdest.write(buf)
- finally:
- fdest.close()
- finally:
- fsrc.close()
-
-def isimportable(name):
- if name and (name[0].isalpha() or name[0] == '_'):
- name = name.replace("_", '')
- return not name or name.isalnum()
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_path/svnurl.py b/tests/wpt/web-platform-tests/tools/py/py/_path/svnurl.py
deleted file mode 100644
index 78d71317ac0..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_path/svnurl.py
+++ /dev/null
@@ -1,380 +0,0 @@
-"""
-module defining a subversion path object based on the external
-command 'svn'. This modules aims to work with svn 1.3 and higher
-but might also interact well with earlier versions.
-"""
-
-import os, sys, time, re
-import py
-from py import path, process
-from py._path import common
-from py._path import svnwc as svncommon
-from py._path.cacheutil import BuildcostAccessCache, AgingCache
-
-DEBUG=False
-
-class SvnCommandPath(svncommon.SvnPathBase):
- """ path implementation that offers access to (possibly remote) subversion
- repositories. """
-
- _lsrevcache = BuildcostAccessCache(maxentries=128)
- _lsnorevcache = AgingCache(maxentries=1000, maxseconds=60.0)
-
- def __new__(cls, path, rev=None, auth=None):
- self = object.__new__(cls)
- if isinstance(path, cls):
- rev = path.rev
- auth = path.auth
- path = path.strpath
- svncommon.checkbadchars(path)
- path = path.rstrip('/')
- self.strpath = path
- self.rev = rev
- self.auth = auth
- return self
-
- def __repr__(self):
- if self.rev == -1:
- return 'svnurl(%r)' % self.strpath
- else:
- return 'svnurl(%r, %r)' % (self.strpath, self.rev)
-
- def _svnwithrev(self, cmd, *args):
- """ execute an svn command, append our own url and revision """
- if self.rev is None:
- return self._svnwrite(cmd, *args)
- else:
- args = ['-r', self.rev] + list(args)
- return self._svnwrite(cmd, *args)
-
- def _svnwrite(self, cmd, *args):
- """ execute an svn command, append our own url """
- l = ['svn %s' % cmd]
- args = ['"%s"' % self._escape(item) for item in args]
- l.extend(args)
- l.append('"%s"' % self._encodedurl())
- # fixing the locale because we can't otherwise parse
- string = " ".join(l)
- if DEBUG:
- print("execing %s" % string)
- out = self._svncmdexecauth(string)
- return out
-
- def _svncmdexecauth(self, cmd):
- """ execute an svn command 'as is' """
- cmd = svncommon.fixlocale() + cmd
- if self.auth is not None:
- cmd += ' ' + self.auth.makecmdoptions()
- return self._cmdexec(cmd)
-
- def _cmdexec(self, cmd):
- try:
- out = process.cmdexec(cmd)
- except py.process.cmdexec.Error:
- e = sys.exc_info()[1]
- if (e.err.find('File Exists') != -1 or
- e.err.find('File already exists') != -1):
- raise py.error.EEXIST(self)
- raise
- return out
-
- def _svnpopenauth(self, cmd):
- """ execute an svn command, return a pipe for reading stdin """
- cmd = svncommon.fixlocale() + cmd
- if self.auth is not None:
- cmd += ' ' + self.auth.makecmdoptions()
- return self._popen(cmd)
-
- def _popen(self, cmd):
- return os.popen(cmd)
-
- def _encodedurl(self):
- return self._escape(self.strpath)
-
- def _norev_delentry(self, path):
- auth = self.auth and self.auth.makecmdoptions() or None
- self._lsnorevcache.delentry((str(path), auth))
-
- def open(self, mode='r'):
- """ return an opened file with the given mode. """
- if mode not in ("r", "rU",):
- raise ValueError("mode %r not supported" % (mode,))
- assert self.check(file=1) # svn cat returns an empty file otherwise
- if self.rev is None:
- return self._svnpopenauth('svn cat "%s"' % (
- self._escape(self.strpath), ))
- else:
- return self._svnpopenauth('svn cat -r %s "%s"' % (
- self.rev, self._escape(self.strpath)))
-
- def dirpath(self, *args, **kwargs):
- """ return the directory path of the current path joined
- with any given path arguments.
- """
- l = self.strpath.split(self.sep)
- if len(l) < 4:
- raise py.error.EINVAL(self, "base is not valid")
- elif len(l) == 4:
- return self.join(*args, **kwargs)
- else:
- return self.new(basename='').join(*args, **kwargs)
-
- # modifying methods (cache must be invalidated)
- def mkdir(self, *args, **kwargs):
- """ create & return the directory joined with args.
- pass a 'msg' keyword argument to set the commit message.
- """
- commit_msg = kwargs.get('msg', "mkdir by py lib invocation")
- createpath = self.join(*args)
- createpath._svnwrite('mkdir', '-m', commit_msg)
- self._norev_delentry(createpath.dirpath())
- return createpath
-
- def copy(self, target, msg='copied by py lib invocation'):
- """ copy path to target with checkin message msg."""
- if getattr(target, 'rev', None) is not None:
- raise py.error.EINVAL(target, "revisions are immutable")
- self._svncmdexecauth('svn copy -m "%s" "%s" "%s"' %(msg,
- self._escape(self), self._escape(target)))
- self._norev_delentry(target.dirpath())
-
- def rename(self, target, msg="renamed by py lib invocation"):
- """ rename this path to target with checkin message msg. """
- if getattr(self, 'rev', None) is not None:
- raise py.error.EINVAL(self, "revisions are immutable")
- self._svncmdexecauth('svn move -m "%s" --force "%s" "%s"' %(
- msg, self._escape(self), self._escape(target)))
- self._norev_delentry(self.dirpath())
- self._norev_delentry(self)
-
- def remove(self, rec=1, msg='removed by py lib invocation'):
- """ remove a file or directory (or a directory tree if rec=1) with
-checkin message msg."""
- if self.rev is not None:
- raise py.error.EINVAL(self, "revisions are immutable")
- self._svncmdexecauth('svn rm -m "%s" "%s"' %(msg, self._escape(self)))
- self._norev_delentry(self.dirpath())
-
- def export(self, topath):
- """ export to a local path
-
- topath should not exist prior to calling this, returns a
- py.path.local instance
- """
- topath = py.path.local(topath)
- args = ['"%s"' % (self._escape(self),),
- '"%s"' % (self._escape(topath),)]
- if self.rev is not None:
- args = ['-r', str(self.rev)] + args
- self._svncmdexecauth('svn export %s' % (' '.join(args),))
- return topath
-
- def ensure(self, *args, **kwargs):
- """ ensure that an args-joined path exists (by default as
- a file). If you specify a keyword argument 'dir=True'
- then the path is forced to be a directory path.
- """
- if getattr(self, 'rev', None) is not None:
- raise py.error.EINVAL(self, "revisions are immutable")
- target = self.join(*args)
- dir = kwargs.get('dir', 0)
- for x in target.parts(reverse=True):
- if x.check():
- break
- else:
- raise py.error.ENOENT(target, "has not any valid base!")
- if x == target:
- if not x.check(dir=dir):
- raise dir and py.error.ENOTDIR(x) or py.error.EISDIR(x)
- return x
- tocreate = target.relto(x)
- basename = tocreate.split(self.sep, 1)[0]
- tempdir = py.path.local.mkdtemp()
- try:
- tempdir.ensure(tocreate, dir=dir)
- cmd = 'svn import -m "%s" "%s" "%s"' % (
- "ensure %s" % self._escape(tocreate),
- self._escape(tempdir.join(basename)),
- x.join(basename)._encodedurl())
- self._svncmdexecauth(cmd)
- self._norev_delentry(x)
- finally:
- tempdir.remove()
- return target
-
- # end of modifying methods
- def _propget(self, name):
- res = self._svnwithrev('propget', name)
- return res[:-1] # strip trailing newline
-
- def _proplist(self):
- res = self._svnwithrev('proplist')
- lines = res.split('\n')
- lines = [x.strip() for x in lines[1:]]
- return svncommon.PropListDict(self, lines)
-
- def info(self):
- """ return an Info structure with svn-provided information. """
- parent = self.dirpath()
- nameinfo_seq = parent._listdir_nameinfo()
- bn = self.basename
- for name, info in nameinfo_seq:
- if name == bn:
- return info
- raise py.error.ENOENT(self)
-
-
- def _listdir_nameinfo(self):
- """ return sequence of name-info directory entries of self """
- def builder():
- try:
- res = self._svnwithrev('ls', '-v')
- except process.cmdexec.Error:
- e = sys.exc_info()[1]
- if e.err.find('non-existent in that revision') != -1:
- raise py.error.ENOENT(self, e.err)
- elif e.err.find("E200009:") != -1:
- raise py.error.ENOENT(self, e.err)
- elif e.err.find('File not found') != -1:
- raise py.error.ENOENT(self, e.err)
- elif e.err.find('not part of a repository')!=-1:
- raise py.error.ENOENT(self, e.err)
- elif e.err.find('Unable to open')!=-1:
- raise py.error.ENOENT(self, e.err)
- elif e.err.lower().find('method not allowed')!=-1:
- raise py.error.EACCES(self, e.err)
- raise py.error.Error(e.err)
- lines = res.split('\n')
- nameinfo_seq = []
- for lsline in lines:
- if lsline:
- info = InfoSvnCommand(lsline)
- if info._name != '.': # svn 1.5 produces '.' dirs,
- nameinfo_seq.append((info._name, info))
- nameinfo_seq.sort()
- return nameinfo_seq
- auth = self.auth and self.auth.makecmdoptions() or None
- if self.rev is not None:
- return self._lsrevcache.getorbuild((self.strpath, self.rev, auth),
- builder)
- else:
- return self._lsnorevcache.getorbuild((self.strpath, auth),
- builder)
-
- def listdir(self, fil=None, sort=None):
- """ list directory contents, possibly filter by the given fil func
- and possibly sorted.
- """
- if isinstance(fil, str):
- fil = common.FNMatcher(fil)
- nameinfo_seq = self._listdir_nameinfo()
- if len(nameinfo_seq) == 1:
- name, info = nameinfo_seq[0]
- if name == self.basename and info.kind == 'file':
- #if not self.check(dir=1):
- raise py.error.ENOTDIR(self)
- paths = [self.join(name) for (name, info) in nameinfo_seq]
- if fil:
- paths = [x for x in paths if fil(x)]
- self._sortlist(paths, sort)
- return paths
-
-
- def log(self, rev_start=None, rev_end=1, verbose=False):
- """ return a list of LogEntry instances for this path.
-rev_start is the starting revision (defaulting to the first one).
-rev_end is the last revision (defaulting to HEAD).
-if verbose is True, then the LogEntry instances also know which files changed.
-"""
- assert self.check() #make it simpler for the pipe
- rev_start = rev_start is None and "HEAD" or rev_start
- rev_end = rev_end is None and "HEAD" or rev_end
-
- if rev_start == "HEAD" and rev_end == 1:
- rev_opt = ""
- else:
- rev_opt = "-r %s:%s" % (rev_start, rev_end)
- verbose_opt = verbose and "-v" or ""
- xmlpipe = self._svnpopenauth('svn log --xml %s %s "%s"' %
- (rev_opt, verbose_opt, self.strpath))
- from xml.dom import minidom
- tree = minidom.parse(xmlpipe)
- result = []
- for logentry in filter(None, tree.firstChild.childNodes):
- if logentry.nodeType == logentry.ELEMENT_NODE:
- result.append(svncommon.LogEntry(logentry))
- return result
-
-#01234567890123456789012345678901234567890123467
-# 2256 hpk 165 Nov 24 17:55 __init__.py
-# XXX spotted by Guido, SVN 1.3.0 has different aligning, breaks the code!!!
-# 1312 johnny 1627 May 05 14:32 test_decorators.py
-#
-class InfoSvnCommand:
- # the '0?' part in the middle is an indication of whether the resource is
- # locked, see 'svn help ls'
- lspattern = re.compile(
- r'^ *(?P<rev>\d+) +(?P<author>.+?) +(0? *(?P<size>\d+))? '
- '*(?P<date>\w+ +\d{2} +[\d:]+) +(?P<file>.*)$')
- def __init__(self, line):
- # this is a typical line from 'svn ls http://...'
- #_ 1127 jum 0 Jul 13 15:28 branch/
- match = self.lspattern.match(line)
- data = match.groupdict()
- self._name = data['file']
- if self._name[-1] == '/':
- self._name = self._name[:-1]
- self.kind = 'dir'
- else:
- self.kind = 'file'
- #self.has_props = l.pop(0) == 'P'
- self.created_rev = int(data['rev'])
- self.last_author = data['author']
- self.size = data['size'] and int(data['size']) or 0
- self.mtime = parse_time_with_missing_year(data['date'])
- self.time = self.mtime * 1000000
-
- def __eq__(self, other):
- return self.__dict__ == other.__dict__
-
-
-#____________________________________________________
-#
-# helper functions
-#____________________________________________________
-def parse_time_with_missing_year(timestr):
- """ analyze the time part from a single line of "svn ls -v"
- the svn output doesn't show the year makes the 'timestr'
- ambigous.
- """
- import calendar
- t_now = time.gmtime()
-
- tparts = timestr.split()
- month = time.strptime(tparts.pop(0), '%b')[1]
- day = time.strptime(tparts.pop(0), '%d')[2]
- last = tparts.pop(0) # year or hour:minute
- try:
- if ":" in last:
- raise ValueError()
- year = time.strptime(last, '%Y')[0]
- hour = minute = 0
- except ValueError:
- hour, minute = time.strptime(last, '%H:%M')[3:5]
- year = t_now[0]
-
- t_result = (year, month, day, hour, minute, 0,0,0,0)
- if t_result > t_now:
- year -= 1
- t_result = (year, month, day, hour, minute, 0,0,0,0)
- return calendar.timegm(t_result)
-
-class PathEntry:
- def __init__(self, ppart):
- self.strpath = ppart.firstChild.nodeValue.encode('UTF-8')
- self.action = ppart.getAttribute('action').encode('UTF-8')
- if self.action == 'A':
- self.copyfrom_path = ppart.getAttribute('copyfrom-path').encode('UTF-8')
- if self.copyfrom_path:
- self.copyfrom_rev = int(ppart.getAttribute('copyfrom-rev'))
-
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_path/svnwc.py b/tests/wpt/web-platform-tests/tools/py/py/_path/svnwc.py
deleted file mode 100644
index 00d3b4bbaf3..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_path/svnwc.py
+++ /dev/null
@@ -1,1240 +0,0 @@
-"""
-svn-Command based Implementation of a Subversion WorkingCopy Path.
-
- SvnWCCommandPath is the main class.
-
-"""
-
-import os, sys, time, re, calendar
-import py
-import subprocess
-from py._path import common
-
-#-----------------------------------------------------------
-# Caching latest repository revision and repo-paths
-# (getting them is slow with the current implementations)
-#
-# XXX make mt-safe
-#-----------------------------------------------------------
-
-class cache:
- proplist = {}
- info = {}
- entries = {}
- prop = {}
-
-class RepoEntry:
- def __init__(self, url, rev, timestamp):
- self.url = url
- self.rev = rev
- self.timestamp = timestamp
-
- def __str__(self):
- return "repo: %s;%s %s" %(self.url, self.rev, self.timestamp)
-
-class RepoCache:
- """ The Repocache manages discovered repository paths
- and their revisions. If inside a timeout the cache
- will even return the revision of the root.
- """
- timeout = 20 # seconds after which we forget that we know the last revision
-
- def __init__(self):
- self.repos = []
-
- def clear(self):
- self.repos = []
-
- def put(self, url, rev, timestamp=None):
- if rev is None:
- return
- if timestamp is None:
- timestamp = time.time()
-
- for entry in self.repos:
- if url == entry.url:
- entry.timestamp = timestamp
- entry.rev = rev
- #print "set repo", entry
- break
- else:
- entry = RepoEntry(url, rev, timestamp)
- self.repos.append(entry)
- #print "appended repo", entry
-
- def get(self, url):
- now = time.time()
- for entry in self.repos:
- if url.startswith(entry.url):
- if now < entry.timestamp + self.timeout:
- #print "returning immediate Etrny", entry
- return entry.url, entry.rev
- return entry.url, -1
- return url, -1
-
-repositories = RepoCache()
-
-
-# svn support code
-
-ALLOWED_CHARS = "_ -/\\=$.~+%" #add characters as necessary when tested
-if sys.platform == "win32":
- ALLOWED_CHARS += ":"
-ALLOWED_CHARS_HOST = ALLOWED_CHARS + '@:'
-
-def _getsvnversion(ver=[]):
- try:
- return ver[0]
- except IndexError:
- v = py.process.cmdexec("svn -q --version")
- v.strip()
- v = '.'.join(v.split('.')[:2])
- ver.append(v)
- return v
-
-def _escape_helper(text):
- text = str(text)
- if py.std.sys.platform != 'win32':
- text = str(text).replace('$', '\\$')
- return text
-
-def _check_for_bad_chars(text, allowed_chars=ALLOWED_CHARS):
- for c in str(text):
- if c.isalnum():
- continue
- if c in allowed_chars:
- continue
- return True
- return False
-
-def checkbadchars(url):
- # (hpk) not quite sure about the exact purpose, guido w.?
- proto, uri = url.split("://", 1)
- if proto != "file":
- host, uripath = uri.split('/', 1)
- # only check for bad chars in the non-protocol parts
- if (_check_for_bad_chars(host, ALLOWED_CHARS_HOST) \
- or _check_for_bad_chars(uripath, ALLOWED_CHARS)):
- raise ValueError("bad char in %r" % (url, ))
-
-
-#_______________________________________________________________
-
-class SvnPathBase(common.PathBase):
- """ Base implementation for SvnPath implementations. """
- sep = '/'
-
- def _geturl(self):
- return self.strpath
- url = property(_geturl, None, None, "url of this svn-path.")
-
- def __str__(self):
- """ return a string representation (including rev-number) """
- return self.strpath
-
- def __hash__(self):
- return hash(self.strpath)
-
- def new(self, **kw):
- """ create a modified version of this path. A 'rev' argument
- indicates a new revision.
- the following keyword arguments modify various path parts::
-
- http://host.com/repo/path/file.ext
- |-----------------------| dirname
- |------| basename
- |--| purebasename
- |--| ext
- """
- obj = object.__new__(self.__class__)
- obj.rev = kw.get('rev', self.rev)
- obj.auth = kw.get('auth', self.auth)
- dirname, basename, purebasename, ext = self._getbyspec(
- "dirname,basename,purebasename,ext")
- if 'basename' in kw:
- if 'purebasename' in kw or 'ext' in kw:
- raise ValueError("invalid specification %r" % kw)
- else:
- pb = kw.setdefault('purebasename', purebasename)
- ext = kw.setdefault('ext', ext)
- if ext and not ext.startswith('.'):
- ext = '.' + ext
- kw['basename'] = pb + ext
-
- kw.setdefault('dirname', dirname)
- kw.setdefault('sep', self.sep)
- if kw['basename']:
- obj.strpath = "%(dirname)s%(sep)s%(basename)s" % kw
- else:
- obj.strpath = "%(dirname)s" % kw
- return obj
-
- def _getbyspec(self, spec):
- """ get specified parts of the path. 'arg' is a string
- with comma separated path parts. The parts are returned
- in exactly the order of the specification.
-
- you may specify the following parts:
-
- http://host.com/repo/path/file.ext
- |-----------------------| dirname
- |------| basename
- |--| purebasename
- |--| ext
- """
- res = []
- parts = self.strpath.split(self.sep)
- for name in spec.split(','):
- name = name.strip()
- if name == 'dirname':
- res.append(self.sep.join(parts[:-1]))
- elif name == 'basename':
- res.append(parts[-1])
- else:
- basename = parts[-1]
- i = basename.rfind('.')
- if i == -1:
- purebasename, ext = basename, ''
- else:
- purebasename, ext = basename[:i], basename[i:]
- if name == 'purebasename':
- res.append(purebasename)
- elif name == 'ext':
- res.append(ext)
- else:
- raise NameError("Don't know part %r" % name)
- return res
-
- def __eq__(self, other):
- """ return true if path and rev attributes each match """
- return (str(self) == str(other) and
- (self.rev == other.rev or self.rev == other.rev))
-
- def __ne__(self, other):
- return not self == other
-
- def join(self, *args):
- """ return a new Path (with the same revision) which is composed
- of the self Path followed by 'args' path components.
- """
- if not args:
- return self
-
- args = tuple([arg.strip(self.sep) for arg in args])
- parts = (self.strpath, ) + args
- newpath = self.__class__(self.sep.join(parts), self.rev, self.auth)
- return newpath
-
- def propget(self, name):
- """ return the content of the given property. """
- value = self._propget(name)
- return value
-
- def proplist(self):
- """ list all property names. """
- content = self._proplist()
- return content
-
- def size(self):
- """ Return the size of the file content of the Path. """
- return self.info().size
-
- def mtime(self):
- """ Return the last modification time of the file. """
- return self.info().mtime
-
- # shared help methods
-
- def _escape(self, cmd):
- return _escape_helper(cmd)
-
-
- #def _childmaxrev(self):
- # """ return maximum revision number of childs (or self.rev if no childs) """
- # rev = self.rev
- # for name, info in self._listdir_nameinfo():
- # rev = max(rev, info.created_rev)
- # return rev
-
- #def _getlatestrevision(self):
- # """ return latest repo-revision for this path. """
- # url = self.strpath
- # path = self.__class__(url, None)
- #
- # # we need a long walk to find the root-repo and revision
- # while 1:
- # try:
- # rev = max(rev, path._childmaxrev())
- # previous = path
- # path = path.dirpath()
- # except (IOError, process.cmdexec.Error):
- # break
- # if rev is None:
- # raise IOError, "could not determine newest repo revision for %s" % self
- # return rev
-
- class Checkers(common.Checkers):
- def dir(self):
- try:
- return self.path.info().kind == 'dir'
- except py.error.Error:
- return self._listdirworks()
-
- def _listdirworks(self):
- try:
- self.path.listdir()
- except py.error.ENOENT:
- return False
- else:
- return True
-
- def file(self):
- try:
- return self.path.info().kind == 'file'
- except py.error.ENOENT:
- return False
-
- def exists(self):
- try:
- return self.path.info()
- except py.error.ENOENT:
- return self._listdirworks()
-
-def parse_apr_time(timestr):
- i = timestr.rfind('.')
- if i == -1:
- raise ValueError("could not parse %s" % timestr)
- timestr = timestr[:i]
- parsedtime = time.strptime(timestr, "%Y-%m-%dT%H:%M:%S")
- return time.mktime(parsedtime)
-
-class PropListDict(dict):
- """ a Dictionary which fetches values (InfoSvnCommand instances) lazily"""
- def __init__(self, path, keynames):
- dict.__init__(self, [(x, None) for x in keynames])
- self.path = path
-
- def __getitem__(self, key):
- value = dict.__getitem__(self, key)
- if value is None:
- value = self.path.propget(key)
- dict.__setitem__(self, key, value)
- return value
-
-def fixlocale():
- if sys.platform != 'win32':
- return 'LC_ALL=C '
- return ''
-
-# some nasty chunk of code to solve path and url conversion and quoting issues
-ILLEGAL_CHARS = '* | \ / : < > ? \t \n \x0b \x0c \r'.split(' ')
-if os.sep in ILLEGAL_CHARS:
- ILLEGAL_CHARS.remove(os.sep)
-ISWINDOWS = sys.platform == 'win32'
-_reg_allow_disk = re.compile(r'^([a-z]\:\\)?[^:]+$', re.I)
-def _check_path(path):
- illegal = ILLEGAL_CHARS[:]
- sp = path.strpath
- if ISWINDOWS:
- illegal.remove(':')
- if not _reg_allow_disk.match(sp):
- raise ValueError('path may not contain a colon (:)')
- for char in sp:
- if char not in string.printable or char in illegal:
- raise ValueError('illegal character %r in path' % (char,))
-
-def path_to_fspath(path, addat=True):
- _check_path(path)
- sp = path.strpath
- if addat and path.rev != -1:
- sp = '%s@%s' % (sp, path.rev)
- elif addat:
- sp = '%s@HEAD' % (sp,)
- return sp
-
-def url_from_path(path):
- fspath = path_to_fspath(path, False)
- quote = py.std.urllib.quote
- if ISWINDOWS:
- match = _reg_allow_disk.match(fspath)
- fspath = fspath.replace('\\', '/')
- if match.group(1):
- fspath = '/%s%s' % (match.group(1).replace('\\', '/'),
- quote(fspath[len(match.group(1)):]))
- else:
- fspath = quote(fspath)
- else:
- fspath = quote(fspath)
- if path.rev != -1:
- fspath = '%s@%s' % (fspath, path.rev)
- else:
- fspath = '%s@HEAD' % (fspath,)
- return 'file://%s' % (fspath,)
-
-class SvnAuth(object):
- """ container for auth information for Subversion """
- def __init__(self, username, password, cache_auth=True, interactive=True):
- self.username = username
- self.password = password
- self.cache_auth = cache_auth
- self.interactive = interactive
-
- def makecmdoptions(self):
- uname = self.username.replace('"', '\\"')
- passwd = self.password.replace('"', '\\"')
- ret = []
- if uname:
- ret.append('--username="%s"' % (uname,))
- if passwd:
- ret.append('--password="%s"' % (passwd,))
- if not self.cache_auth:
- ret.append('--no-auth-cache')
- if not self.interactive:
- ret.append('--non-interactive')
- return ' '.join(ret)
-
- def __str__(self):
- return "<SvnAuth username=%s ...>" %(self.username,)
-
-rex_blame = re.compile(r'\s*(\d+)\s*(\S+) (.*)')
-
-class SvnWCCommandPath(common.PathBase):
- """ path implementation offering access/modification to svn working copies.
- It has methods similar to the functions in os.path and similar to the
- commands of the svn client.
- """
- sep = os.sep
-
- def __new__(cls, wcpath=None, auth=None):
- self = object.__new__(cls)
- if isinstance(wcpath, cls):
- if wcpath.__class__ == cls:
- return wcpath
- wcpath = wcpath.localpath
- if _check_for_bad_chars(str(wcpath),
- ALLOWED_CHARS):
- raise ValueError("bad char in wcpath %s" % (wcpath, ))
- self.localpath = py.path.local(wcpath)
- self.auth = auth
- return self
-
- strpath = property(lambda x: str(x.localpath), None, None, "string path")
- rev = property(lambda x: x.info(usecache=0).rev, None, None, "revision")
-
- def __eq__(self, other):
- return self.localpath == getattr(other, 'localpath', None)
-
- def _geturl(self):
- if getattr(self, '_url', None) is None:
- info = self.info()
- self._url = info.url #SvnPath(info.url, info.rev)
- assert isinstance(self._url, py.builtin._basestring)
- return self._url
-
- url = property(_geturl, None, None, "url of this WC item")
-
- def _escape(self, cmd):
- return _escape_helper(cmd)
-
- def dump(self, obj):
- """ pickle object into path location"""
- return self.localpath.dump(obj)
-
- def svnurl(self):
- """ return current SvnPath for this WC-item. """
- info = self.info()
- return py.path.svnurl(info.url)
-
- def __repr__(self):
- return "svnwc(%r)" % (self.strpath) # , self._url)
-
- def __str__(self):
- return str(self.localpath)
-
- def _makeauthoptions(self):
- if self.auth is None:
- return ''
- return self.auth.makecmdoptions()
-
- def _authsvn(self, cmd, args=None):
- args = args and list(args) or []
- args.append(self._makeauthoptions())
- return self._svn(cmd, *args)
-
- def _svn(self, cmd, *args):
- l = ['svn %s' % cmd]
- args = [self._escape(item) for item in args]
- l.extend(args)
- l.append('"%s"' % self._escape(self.strpath))
- # try fixing the locale because we can't otherwise parse
- string = fixlocale() + " ".join(l)
- try:
- try:
- key = 'LC_MESSAGES'
- hold = os.environ.get(key)
- os.environ[key] = 'C'
- out = py.process.cmdexec(string)
- finally:
- if hold:
- os.environ[key] = hold
- else:
- del os.environ[key]
- except py.process.cmdexec.Error:
- e = sys.exc_info()[1]
- strerr = e.err.lower()
- if strerr.find('not found') != -1:
- raise py.error.ENOENT(self)
- elif strerr.find("E200009:") != -1:
- raise py.error.ENOENT(self)
- if (strerr.find('file exists') != -1 or
- strerr.find('file already exists') != -1 or
- strerr.find('w150002:') != -1 or
- strerr.find("can't create directory") != -1):
- raise py.error.EEXIST(strerr) #self)
- raise
- return out
-
- def switch(self, url):
- """ switch to given URL. """
- self._authsvn('switch', [url])
-
- def checkout(self, url=None, rev=None):
- """ checkout from url to local wcpath. """
- args = []
- if url is None:
- url = self.url
- if rev is None or rev == -1:
- if (py.std.sys.platform != 'win32' and
- _getsvnversion() == '1.3'):
- url += "@HEAD"
- else:
- if _getsvnversion() == '1.3':
- url += "@%d" % rev
- else:
- args.append('-r' + str(rev))
- args.append(url)
- self._authsvn('co', args)
-
- def update(self, rev='HEAD', interactive=True):
- """ update working copy item to given revision. (None -> HEAD). """
- opts = ['-r', rev]
- if not interactive:
- opts.append("--non-interactive")
- self._authsvn('up', opts)
-
- def write(self, content, mode='w'):
- """ write content into local filesystem wc. """
- self.localpath.write(content, mode)
-
- def dirpath(self, *args):
- """ return the directory Path of the current Path. """
- return self.__class__(self.localpath.dirpath(*args), auth=self.auth)
-
- def _ensuredirs(self):
- parent = self.dirpath()
- if parent.check(dir=0):
- parent._ensuredirs()
- if self.check(dir=0):
- self.mkdir()
- return self
-
- def ensure(self, *args, **kwargs):
- """ ensure that an args-joined path exists (by default as
- a file). if you specify a keyword argument 'directory=True'
- then the path is forced to be a directory path.
- """
- p = self.join(*args)
- if p.check():
- if p.check(versioned=False):
- p.add()
- return p
- if kwargs.get('dir', 0):
- return p._ensuredirs()
- parent = p.dirpath()
- parent._ensuredirs()
- p.write("")
- p.add()
- return p
-
- def mkdir(self, *args):
- """ create & return the directory joined with args. """
- if args:
- return self.join(*args).mkdir()
- else:
- self._svn('mkdir')
- return self
-
- def add(self):
- """ add ourself to svn """
- self._svn('add')
-
- def remove(self, rec=1, force=1):
- """ remove a file or a directory tree. 'rec'ursive is
- ignored and considered always true (because of
- underlying svn semantics.
- """
- assert rec, "svn cannot remove non-recursively"
- if not self.check(versioned=True):
- # not added to svn (anymore?), just remove
- py.path.local(self).remove()
- return
- flags = []
- if force:
- flags.append('--force')
- self._svn('remove', *flags)
-
- def copy(self, target):
- """ copy path to target."""
- py.process.cmdexec("svn copy %s %s" %(str(self), str(target)))
-
- def rename(self, target):
- """ rename this path to target. """
- py.process.cmdexec("svn move --force %s %s" %(str(self), str(target)))
-
- def lock(self):
- """ set a lock (exclusive) on the resource """
- out = self._authsvn('lock').strip()
- if not out:
- # warning or error, raise exception
- raise ValueError("unknown error in svn lock command")
-
- def unlock(self):
- """ unset a previously set lock """
- out = self._authsvn('unlock').strip()
- if out.startswith('svn:'):
- # warning or error, raise exception
- raise Exception(out[4:])
-
- def cleanup(self):
- """ remove any locks from the resource """
- # XXX should be fixed properly!!!
- try:
- self.unlock()
- except:
- pass
-
- def status(self, updates=0, rec=0, externals=0):
- """ return (collective) Status object for this file. """
- # http://svnbook.red-bean.com/book.html#svn-ch-3-sect-4.3.1
- # 2201 2192 jum test
- # XXX
- if externals:
- raise ValueError("XXX cannot perform status() "
- "on external items yet")
- else:
- #1.2 supports: externals = '--ignore-externals'
- externals = ''
- if rec:
- rec= ''
- else:
- rec = '--non-recursive'
-
- # XXX does not work on all subversion versions
- #if not externals:
- # externals = '--ignore-externals'
-
- if updates:
- updates = '-u'
- else:
- updates = ''
-
- try:
- cmd = 'status -v --xml --no-ignore %s %s %s' % (
- updates, rec, externals)
- out = self._authsvn(cmd)
- except py.process.cmdexec.Error:
- cmd = 'status -v --no-ignore %s %s %s' % (
- updates, rec, externals)
- out = self._authsvn(cmd)
- rootstatus = WCStatus(self).fromstring(out, self)
- else:
- rootstatus = XMLWCStatus(self).fromstring(out, self)
- return rootstatus
-
- def diff(self, rev=None):
- """ return a diff of the current path against revision rev (defaulting
- to the last one).
- """
- args = []
- if rev is not None:
- args.append("-r %d" % rev)
- out = self._authsvn('diff', args)
- return out
-
- def blame(self):
- """ return a list of tuples of three elements:
- (revision, commiter, line)
- """
- out = self._svn('blame')
- result = []
- blamelines = out.splitlines()
- reallines = py.path.svnurl(self.url).readlines()
- for i, (blameline, line) in enumerate(
- zip(blamelines, reallines)):
- m = rex_blame.match(blameline)
- if not m:
- raise ValueError("output line %r of svn blame does not match "
- "expected format" % (line, ))
- rev, name, _ = m.groups()
- result.append((int(rev), name, line))
- return result
-
- _rex_commit = re.compile(r'.*Committed revision (\d+)\.$', re.DOTALL)
- def commit(self, msg='', rec=1):
- """ commit with support for non-recursive commits """
- # XXX i guess escaping should be done better here?!?
- cmd = 'commit -m "%s" --force-log' % (msg.replace('"', '\\"'),)
- if not rec:
- cmd += ' -N'
- out = self._authsvn(cmd)
- try:
- del cache.info[self]
- except KeyError:
- pass
- if out:
- m = self._rex_commit.match(out)
- return int(m.group(1))
-
- def propset(self, name, value, *args):
- """ set property name to value on this path. """
- d = py.path.local.mkdtemp()
- try:
- p = d.join('value')
- p.write(value)
- self._svn('propset', name, '--file', str(p), *args)
- finally:
- d.remove()
-
- def propget(self, name):
- """ get property name on this path. """
- res = self._svn('propget', name)
- return res[:-1] # strip trailing newline
-
- def propdel(self, name):
- """ delete property name on this path. """
- res = self._svn('propdel', name)
- return res[:-1] # strip trailing newline
-
- def proplist(self, rec=0):
- """ return a mapping of property names to property values.
-If rec is True, then return a dictionary mapping sub-paths to such mappings.
-"""
- if rec:
- res = self._svn('proplist -R')
- return make_recursive_propdict(self, res)
- else:
- res = self._svn('proplist')
- lines = res.split('\n')
- lines = [x.strip() for x in lines[1:]]
- return PropListDict(self, lines)
-
- def revert(self, rec=0):
- """ revert the local changes of this path. if rec is True, do so
-recursively. """
- if rec:
- result = self._svn('revert -R')
- else:
- result = self._svn('revert')
- return result
-
- def new(self, **kw):
- """ create a modified version of this path. A 'rev' argument
- indicates a new revision.
- the following keyword arguments modify various path parts:
-
- http://host.com/repo/path/file.ext
- |-----------------------| dirname
- |------| basename
- |--| purebasename
- |--| ext
- """
- if kw:
- localpath = self.localpath.new(**kw)
- else:
- localpath = self.localpath
- return self.__class__(localpath, auth=self.auth)
-
- def join(self, *args, **kwargs):
- """ return a new Path (with the same revision) which is composed
- of the self Path followed by 'args' path components.
- """
- if not args:
- return self
- localpath = self.localpath.join(*args, **kwargs)
- return self.__class__(localpath, auth=self.auth)
-
- def info(self, usecache=1):
- """ return an Info structure with svn-provided information. """
- info = usecache and cache.info.get(self)
- if not info:
- try:
- output = self._svn('info')
- except py.process.cmdexec.Error:
- e = sys.exc_info()[1]
- if e.err.find('Path is not a working copy directory') != -1:
- raise py.error.ENOENT(self, e.err)
- elif e.err.find("is not under version control") != -1:
- raise py.error.ENOENT(self, e.err)
- raise
- # XXX SVN 1.3 has output on stderr instead of stdout (while it does
- # return 0!), so a bit nasty, but we assume no output is output
- # to stderr...
- if (output.strip() == '' or
- output.lower().find('not a versioned resource') != -1):
- raise py.error.ENOENT(self, output)
- info = InfoSvnWCCommand(output)
-
- # Can't reliably compare on Windows without access to win32api
- if py.std.sys.platform != 'win32':
- if info.path != self.localpath:
- raise py.error.ENOENT(self, "not a versioned resource:" +
- " %s != %s" % (info.path, self.localpath))
- cache.info[self] = info
- return info
-
- def listdir(self, fil=None, sort=None):
- """ return a sequence of Paths.
-
- listdir will return either a tuple or a list of paths
- depending on implementation choices.
- """
- if isinstance(fil, str):
- fil = common.FNMatcher(fil)
- # XXX unify argument naming with LocalPath.listdir
- def notsvn(path):
- return path.basename != '.svn'
-
- paths = []
- for localpath in self.localpath.listdir(notsvn):
- p = self.__class__(localpath, auth=self.auth)
- if notsvn(p) and (not fil or fil(p)):
- paths.append(p)
- self._sortlist(paths, sort)
- return paths
-
- def open(self, mode='r'):
- """ return an opened file with the given mode. """
- return open(self.strpath, mode)
-
- def _getbyspec(self, spec):
- return self.localpath._getbyspec(spec)
-
- class Checkers(py.path.local.Checkers):
- def __init__(self, path):
- self.svnwcpath = path
- self.path = path.localpath
- def versioned(self):
- try:
- s = self.svnwcpath.info()
- except (py.error.ENOENT, py.error.EEXIST):
- return False
- except py.process.cmdexec.Error:
- e = sys.exc_info()[1]
- if e.err.find('is not a working copy')!=-1:
- return False
- if e.err.lower().find('not a versioned resource') != -1:
- return False
- raise
- else:
- return True
-
- def log(self, rev_start=None, rev_end=1, verbose=False):
- """ return a list of LogEntry instances for this path.
-rev_start is the starting revision (defaulting to the first one).
-rev_end is the last revision (defaulting to HEAD).
-if verbose is True, then the LogEntry instances also know which files changed.
-"""
- assert self.check() # make it simpler for the pipe
- rev_start = rev_start is None and "HEAD" or rev_start
- rev_end = rev_end is None and "HEAD" or rev_end
- if rev_start == "HEAD" and rev_end == 1:
- rev_opt = ""
- else:
- rev_opt = "-r %s:%s" % (rev_start, rev_end)
- verbose_opt = verbose and "-v" or ""
- locale_env = fixlocale()
- # some blather on stderr
- auth_opt = self._makeauthoptions()
- #stdin, stdout, stderr = os.popen3(locale_env +
- # 'svn log --xml %s %s %s "%s"' % (
- # rev_opt, verbose_opt, auth_opt,
- # self.strpath))
- cmd = locale_env + 'svn log --xml %s %s %s "%s"' % (
- rev_opt, verbose_opt, auth_opt, self.strpath)
-
- popen = subprocess.Popen(cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- shell=True,
- )
- stdout, stderr = popen.communicate()
- stdout = py.builtin._totext(stdout, sys.getdefaultencoding())
- minidom,ExpatError = importxml()
- try:
- tree = minidom.parseString(stdout)
- except ExpatError:
- raise ValueError('no such revision')
- result = []
- for logentry in filter(None, tree.firstChild.childNodes):
- if logentry.nodeType == logentry.ELEMENT_NODE:
- result.append(LogEntry(logentry))
- return result
-
- def size(self):
- """ Return the size of the file content of the Path. """
- return self.info().size
-
- def mtime(self):
- """ Return the last modification time of the file. """
- return self.info().mtime
-
- def __hash__(self):
- return hash((self.strpath, self.__class__, self.auth))
-
-
-class WCStatus:
- attrnames = ('modified','added', 'conflict', 'unchanged', 'external',
- 'deleted', 'prop_modified', 'unknown', 'update_available',
- 'incomplete', 'kindmismatch', 'ignored', 'locked', 'replaced'
- )
-
- def __init__(self, wcpath, rev=None, modrev=None, author=None):
- self.wcpath = wcpath
- self.rev = rev
- self.modrev = modrev
- self.author = author
-
- for name in self.attrnames:
- setattr(self, name, [])
-
- def allpath(self, sort=True, **kw):
- d = {}
- for name in self.attrnames:
- if name not in kw or kw[name]:
- for path in getattr(self, name):
- d[path] = 1
- l = d.keys()
- if sort:
- l.sort()
- return l
-
- # XXX a bit scary to assume there's always 2 spaces between username and
- # path, however with win32 allowing spaces in user names there doesn't
- # seem to be a more solid approach :(
- _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)')
-
- def fromstring(data, rootwcpath, rev=None, modrev=None, author=None):
- """ return a new WCStatus object from data 's'
- """
- rootstatus = WCStatus(rootwcpath, rev, modrev, author)
- update_rev = None
- for line in data.split('\n'):
- if not line.strip():
- continue
- #print "processing %r" % line
- flags, rest = line[:8], line[8:]
- # first column
- c0,c1,c2,c3,c4,c5,x6,c7 = flags
- #if '*' in line:
- # print "flags", repr(flags), "rest", repr(rest)
-
- if c0 in '?XI':
- fn = line.split(None, 1)[1]
- if c0 == '?':
- wcpath = rootwcpath.join(fn, abs=1)
- rootstatus.unknown.append(wcpath)
- elif c0 == 'X':
- wcpath = rootwcpath.__class__(
- rootwcpath.localpath.join(fn, abs=1),
- auth=rootwcpath.auth)
- rootstatus.external.append(wcpath)
- elif c0 == 'I':
- wcpath = rootwcpath.join(fn, abs=1)
- rootstatus.ignored.append(wcpath)
-
- continue
-
- #elif c0 in '~!' or c4 == 'S':
- # raise NotImplementedError("received flag %r" % c0)
-
- m = WCStatus._rex_status.match(rest)
- if not m:
- if c7 == '*':
- fn = rest.strip()
- wcpath = rootwcpath.join(fn, abs=1)
- rootstatus.update_available.append(wcpath)
- continue
- if line.lower().find('against revision:')!=-1:
- update_rev = int(rest.split(':')[1].strip())
- continue
- if line.lower().find('status on external') > -1:
- # XXX not sure what to do here... perhaps we want to
- # store some state instead of just continuing, as right
- # now it makes the top-level external get added twice
- # (once as external, once as 'normal' unchanged item)
- # because of the way SVN presents external items
- continue
- # keep trying
- raise ValueError("could not parse line %r" % line)
- else:
- rev, modrev, author, fn = m.groups()
- wcpath = rootwcpath.join(fn, abs=1)
- #assert wcpath.check()
- if c0 == 'M':
- assert wcpath.check(file=1), "didn't expect a directory with changed content here"
- rootstatus.modified.append(wcpath)
- elif c0 == 'A' or c3 == '+' :
- rootstatus.added.append(wcpath)
- elif c0 == 'D':
- rootstatus.deleted.append(wcpath)
- elif c0 == 'C':
- rootstatus.conflict.append(wcpath)
- elif c0 == '~':
- rootstatus.kindmismatch.append(wcpath)
- elif c0 == '!':
- rootstatus.incomplete.append(wcpath)
- elif c0 == 'R':
- rootstatus.replaced.append(wcpath)
- elif not c0.strip():
- rootstatus.unchanged.append(wcpath)
- else:
- raise NotImplementedError("received flag %r" % c0)
-
- if c1 == 'M':
- rootstatus.prop_modified.append(wcpath)
- # XXX do we cover all client versions here?
- if c2 == 'L' or c5 == 'K':
- rootstatus.locked.append(wcpath)
- if c7 == '*':
- rootstatus.update_available.append(wcpath)
-
- if wcpath == rootwcpath:
- rootstatus.rev = rev
- rootstatus.modrev = modrev
- rootstatus.author = author
- if update_rev:
- rootstatus.update_rev = update_rev
- continue
- return rootstatus
- fromstring = staticmethod(fromstring)
-
-class XMLWCStatus(WCStatus):
- def fromstring(data, rootwcpath, rev=None, modrev=None, author=None):
- """ parse 'data' (XML string as outputted by svn st) into a status obj
- """
- # XXX for externals, the path is shown twice: once
- # with external information, and once with full info as if
- # the item was a normal non-external... the current way of
- # dealing with this issue is by ignoring it - this does make
- # externals appear as external items as well as 'normal',
- # unchanged ones in the status object so this is far from ideal
- rootstatus = WCStatus(rootwcpath, rev, modrev, author)
- update_rev = None
- minidom, ExpatError = importxml()
- try:
- doc = minidom.parseString(data)
- except ExpatError:
- e = sys.exc_info()[1]
- raise ValueError(str(e))
- urevels = doc.getElementsByTagName('against')
- if urevels:
- rootstatus.update_rev = urevels[-1].getAttribute('revision')
- for entryel in doc.getElementsByTagName('entry'):
- path = entryel.getAttribute('path')
- statusel = entryel.getElementsByTagName('wc-status')[0]
- itemstatus = statusel.getAttribute('item')
-
- if itemstatus == 'unversioned':
- wcpath = rootwcpath.join(path, abs=1)
- rootstatus.unknown.append(wcpath)
- continue
- elif itemstatus == 'external':
- wcpath = rootwcpath.__class__(
- rootwcpath.localpath.join(path, abs=1),
- auth=rootwcpath.auth)
- rootstatus.external.append(wcpath)
- continue
- elif itemstatus == 'ignored':
- wcpath = rootwcpath.join(path, abs=1)
- rootstatus.ignored.append(wcpath)
- continue
- elif itemstatus == 'incomplete':
- wcpath = rootwcpath.join(path, abs=1)
- rootstatus.incomplete.append(wcpath)
- continue
-
- rev = statusel.getAttribute('revision')
- if itemstatus == 'added' or itemstatus == 'none':
- rev = '0'
- modrev = '?'
- author = '?'
- date = ''
- elif itemstatus == "replaced":
- pass
- else:
- #print entryel.toxml()
- commitel = entryel.getElementsByTagName('commit')[0]
- if commitel:
- modrev = commitel.getAttribute('revision')
- author = ''
- author_els = commitel.getElementsByTagName('author')
- if author_els:
- for c in author_els[0].childNodes:
- author += c.nodeValue
- date = ''
- for c in commitel.getElementsByTagName('date')[0]\
- .childNodes:
- date += c.nodeValue
-
- wcpath = rootwcpath.join(path, abs=1)
-
- assert itemstatus != 'modified' or wcpath.check(file=1), (
- 'did\'t expect a directory with changed content here')
-
- itemattrname = {
- 'normal': 'unchanged',
- 'unversioned': 'unknown',
- 'conflicted': 'conflict',
- 'none': 'added',
- }.get(itemstatus, itemstatus)
-
- attr = getattr(rootstatus, itemattrname)
- attr.append(wcpath)
-
- propsstatus = statusel.getAttribute('props')
- if propsstatus not in ('none', 'normal'):
- rootstatus.prop_modified.append(wcpath)
-
- if wcpath == rootwcpath:
- rootstatus.rev = rev
- rootstatus.modrev = modrev
- rootstatus.author = author
- rootstatus.date = date
-
- # handle repos-status element (remote info)
- rstatusels = entryel.getElementsByTagName('repos-status')
- if rstatusels:
- rstatusel = rstatusels[0]
- ritemstatus = rstatusel.getAttribute('item')
- if ritemstatus in ('added', 'modified'):
- rootstatus.update_available.append(wcpath)
-
- lockels = entryel.getElementsByTagName('lock')
- if len(lockels):
- rootstatus.locked.append(wcpath)
-
- return rootstatus
- fromstring = staticmethod(fromstring)
-
-class InfoSvnWCCommand:
- def __init__(self, output):
- # Path: test
- # URL: http://codespeak.net/svn/std.path/trunk/dist/std.path/test
- # Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada
- # Revision: 2151
- # Node Kind: directory
- # Schedule: normal
- # Last Changed Author: hpk
- # Last Changed Rev: 2100
- # Last Changed Date: 2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003)
- # Properties Last Updated: 2003-11-03 14:47:48 +0100 (Mon, 03 Nov 2003)
-
- d = {}
- for line in output.split('\n'):
- if not line.strip():
- continue
- key, value = line.split(':', 1)
- key = key.lower().replace(' ', '')
- value = value.strip()
- d[key] = value
- try:
- self.url = d['url']
- except KeyError:
- raise ValueError("Not a versioned resource")
- #raise ValueError, "Not a versioned resource %r" % path
- self.kind = d['nodekind'] == 'directory' and 'dir' or d['nodekind']
- try:
- self.rev = int(d['revision'])
- except KeyError:
- self.rev = None
-
- self.path = py.path.local(d['path'])
- self.size = self.path.size()
- if 'lastchangedrev' in d:
- self.created_rev = int(d['lastchangedrev'])
- if 'lastchangedauthor' in d:
- self.last_author = d['lastchangedauthor']
- if 'lastchangeddate' in d:
- self.mtime = parse_wcinfotime(d['lastchangeddate'])
- self.time = self.mtime * 1000000
-
- def __eq__(self, other):
- return self.__dict__ == other.__dict__
-
-def parse_wcinfotime(timestr):
- """ Returns seconds since epoch, UTC. """
- # example: 2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003)
- m = re.match(r'(\d+-\d+-\d+ \d+:\d+:\d+) ([+-]\d+) .*', timestr)
- if not m:
- raise ValueError("timestring %r does not match" % timestr)
- timestr, timezone = m.groups()
- # do not handle timezone specially, return value should be UTC
- parsedtime = time.strptime(timestr, "%Y-%m-%d %H:%M:%S")
- return calendar.timegm(parsedtime)
-
-def make_recursive_propdict(wcroot,
- output,
- rex = re.compile("Properties on '(.*)':")):
- """ Return a dictionary of path->PropListDict mappings. """
- lines = [x for x in output.split('\n') if x]
- pdict = {}
- while lines:
- line = lines.pop(0)
- m = rex.match(line)
- if not m:
- raise ValueError("could not parse propget-line: %r" % line)
- path = m.groups()[0]
- wcpath = wcroot.join(path, abs=1)
- propnames = []
- while lines and lines[0].startswith(' '):
- propname = lines.pop(0).strip()
- propnames.append(propname)
- assert propnames, "must have found properties!"
- pdict[wcpath] = PropListDict(wcpath, propnames)
- return pdict
-
-
-def importxml(cache=[]):
- if cache:
- return cache
- from xml.dom import minidom
- from xml.parsers.expat import ExpatError
- cache.extend([minidom, ExpatError])
- return cache
-
-class LogEntry:
- def __init__(self, logentry):
- self.rev = int(logentry.getAttribute('revision'))
- for lpart in filter(None, logentry.childNodes):
- if lpart.nodeType == lpart.ELEMENT_NODE:
- if lpart.nodeName == 'author':
- self.author = lpart.firstChild.nodeValue
- elif lpart.nodeName == 'msg':
- if lpart.firstChild:
- self.msg = lpart.firstChild.nodeValue
- else:
- self.msg = ''
- elif lpart.nodeName == 'date':
- #2003-07-29T20:05:11.598637Z
- timestr = lpart.firstChild.nodeValue
- self.date = parse_apr_time(timestr)
- elif lpart.nodeName == 'paths':
- self.strpaths = []
- for ppart in filter(None, lpart.childNodes):
- if ppart.nodeType == ppart.ELEMENT_NODE:
- self.strpaths.append(PathEntry(ppart))
- def __repr__(self):
- return '<Logentry rev=%d author=%s date=%s>' % (
- self.rev, self.author, self.date)
-
-
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_std.py b/tests/wpt/web-platform-tests/tools/py/py/_std.py
deleted file mode 100644
index 97a9853323b..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_std.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import sys
-
-class Std(object):
- """ makes top-level python modules available as an attribute,
- importing them on first access.
- """
-
- def __init__(self):
- self.__dict__ = sys.modules
-
- def __getattr__(self, name):
- try:
- m = __import__(name)
- except ImportError:
- raise AttributeError("py.std: could not import %s" % name)
- return m
-
-std = Std()
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_xmlgen.py b/tests/wpt/web-platform-tests/tools/py/py/_xmlgen.py
deleted file mode 100644
index 2ffcaa14b8e..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/py/_xmlgen.py
+++ /dev/null
@@ -1,253 +0,0 @@
-"""
-module for generating and serializing xml and html structures
-by using simple python objects.
-
-(c) holger krekel, holger at merlinux eu. 2009
-"""
-import sys, re
-
-if sys.version_info >= (3,0):
- def u(s):
- return s
- def unicode(x, errors=None):
- if hasattr(x, '__unicode__'):
- return x.__unicode__()
- return str(x)
-else:
- def u(s):
- return unicode(s)
- unicode = unicode
-
-
-class NamespaceMetaclass(type):
- def __getattr__(self, name):
- if name[:1] == '_':
- raise AttributeError(name)
- if self == Namespace:
- raise ValueError("Namespace class is abstract")
- tagspec = self.__tagspec__
- if tagspec is not None and name not in tagspec:
- raise AttributeError(name)
- classattr = {}
- if self.__stickyname__:
- classattr['xmlname'] = name
- cls = type(name, (self.__tagclass__,), classattr)
- setattr(self, name, cls)
- return cls
-
-class Tag(list):
- class Attr(object):
- def __init__(self, **kwargs):
- self.__dict__.update(kwargs)
-
- def __init__(self, *args, **kwargs):
- super(Tag, self).__init__(args)
- self.attr = self.Attr(**kwargs)
-
- def __unicode__(self):
- return self.unicode(indent=0)
- __str__ = __unicode__
-
- def unicode(self, indent=2):
- l = []
- SimpleUnicodeVisitor(l.append, indent).visit(self)
- return u("").join(l)
-
- def __repr__(self):
- name = self.__class__.__name__
- return "<%r tag object %d>" % (name, id(self))
-
-Namespace = NamespaceMetaclass('Namespace', (object, ), {
- '__tagspec__': None,
- '__tagclass__': Tag,
- '__stickyname__': False,
-})
-
-class HtmlTag(Tag):
- def unicode(self, indent=2):
- l = []
- HtmlVisitor(l.append, indent, shortempty=False).visit(self)
- return u("").join(l)
-
-# exported plain html namespace
-class html(Namespace):
- __tagclass__ = HtmlTag
- __stickyname__ = True
- __tagspec__ = dict([(x,1) for x in (
- 'a,abbr,acronym,address,applet,area,b,bdo,big,blink,'
- 'blockquote,body,br,button,caption,center,cite,code,col,'
- 'colgroup,comment,dd,del,dfn,dir,div,dl,dt,em,embed,'
- 'fieldset,font,form,frameset,h1,h2,h3,h4,h5,h6,head,html,'
- 'i,iframe,img,input,ins,kbd,label,legend,li,link,listing,'
- 'map,marquee,menu,meta,multicol,nobr,noembed,noframes,'
- 'noscript,object,ol,optgroup,option,p,pre,q,s,script,'
- 'select,small,span,strike,strong,style,sub,sup,table,'
- 'tbody,td,textarea,tfoot,th,thead,title,tr,tt,u,ul,xmp,'
- 'base,basefont,frame,hr,isindex,param,samp,var'
- ).split(',') if x])
-
- class Style(object):
- def __init__(self, **kw):
- for x, y in kw.items():
- x = x.replace('_', '-')
- setattr(self, x, y)
-
-
-class raw(object):
- """just a box that can contain a unicode string that will be
- included directly in the output"""
- def __init__(self, uniobj):
- self.uniobj = uniobj
-
-class SimpleUnicodeVisitor(object):
- """ recursive visitor to write unicode. """
- def __init__(self, write, indent=0, curindent=0, shortempty=True):
- self.write = write
- self.cache = {}
- self.visited = {} # for detection of recursion
- self.indent = indent
- self.curindent = curindent
- self.parents = []
- self.shortempty = shortempty # short empty tags or not
-
- def visit(self, node):
- """ dispatcher on node's class/bases name. """
- cls = node.__class__
- try:
- visitmethod = self.cache[cls]
- except KeyError:
- for subclass in cls.__mro__:
- visitmethod = getattr(self, subclass.__name__, None)
- if visitmethod is not None:
- break
- else:
- visitmethod = self.__object
- self.cache[cls] = visitmethod
- visitmethod(node)
-
- # the default fallback handler is marked private
- # to avoid clashes with the tag name object
- def __object(self, obj):
- #self.write(obj)
- self.write(escape(unicode(obj)))
-
- def raw(self, obj):
- self.write(obj.uniobj)
-
- def list(self, obj):
- assert id(obj) not in self.visited
- self.visited[id(obj)] = 1
- for elem in obj:
- self.visit(elem)
-
- def Tag(self, tag):
- assert id(tag) not in self.visited
- try:
- tag.parent = self.parents[-1]
- except IndexError:
- tag.parent = None
- self.visited[id(tag)] = 1
- tagname = getattr(tag, 'xmlname', tag.__class__.__name__)
- if self.curindent and not self._isinline(tagname):
- self.write("\n" + u(' ') * self.curindent)
- if tag:
- self.curindent += self.indent
- self.write(u('<%s%s>') % (tagname, self.attributes(tag)))
- self.parents.append(tag)
- for x in tag:
- self.visit(x)
- self.parents.pop()
- self.write(u('</%s>') % tagname)
- self.curindent -= self.indent
- else:
- nameattr = tagname+self.attributes(tag)
- if self._issingleton(tagname):
- self.write(u('<%s/>') % (nameattr,))
- else:
- self.write(u('<%s></%s>') % (nameattr, tagname))
-
- def attributes(self, tag):
- # serialize attributes
- attrlist = dir(tag.attr)
- attrlist.sort()
- l = []
- for name in attrlist:
- res = self.repr_attribute(tag.attr, name)
- if res is not None:
- l.append(res)
- l.extend(self.getstyle(tag))
- return u("").join(l)
-
- def repr_attribute(self, attrs, name):
- if name[:2] != '__':
- value = getattr(attrs, name)
- if name.endswith('_'):
- name = name[:-1]
- if isinstance(value, raw):
- insert = value.uniobj
- else:
- insert = escape(unicode(value))
- return ' %s="%s"' % (name, insert)
-
- def getstyle(self, tag):
- """ return attribute list suitable for styling. """
- try:
- styledict = tag.style.__dict__
- except AttributeError:
- return []
- else:
- stylelist = [x+': ' + y for x,y in styledict.items()]
- return [u(' style="%s"') % u('; ').join(stylelist)]
-
- def _issingleton(self, tagname):
- """can (and will) be overridden in subclasses"""
- return self.shortempty
-
- def _isinline(self, tagname):
- """can (and will) be overridden in subclasses"""
- return False
-
-class HtmlVisitor(SimpleUnicodeVisitor):
-
- single = dict([(x, 1) for x in
- ('br,img,area,param,col,hr,meta,link,base,'
- 'input,frame').split(',')])
- inline = dict([(x, 1) for x in
- ('a abbr acronym b basefont bdo big br cite code dfn em font '
- 'i img input kbd label q s samp select small span strike '
- 'strong sub sup textarea tt u var'.split(' '))])
-
- def repr_attribute(self, attrs, name):
- if name == 'class_':
- value = getattr(attrs, name)
- if value is None:
- return
- return super(HtmlVisitor, self).repr_attribute(attrs, name)
-
- def _issingleton(self, tagname):
- return tagname in self.single
-
- def _isinline(self, tagname):
- return tagname in self.inline
-
-
-class _escape:
- def __init__(self):
- self.escape = {
- u('"') : u('&quot;'), u('<') : u('&lt;'), u('>') : u('&gt;'),
- u('&') : u('&amp;'), u("'") : u('&apos;'),
- }
- self.charef_rex = re.compile(u("|").join(self.escape.keys()))
-
- def _replacer(self, match):
- return self.escape[match.group(0)]
-
- def __call__(self, ustring):
- """ xml-escape the given unicode string. """
- try:
- ustring = unicode(ustring)
- except UnicodeDecodeError:
- ustring = unicode(ustring, 'utf-8', errors='replace')
- return self.charef_rex.sub(self._replacer, ustring)
-
-escape = _escape()
diff --git a/tests/wpt/web-platform-tests/tools/py/setup.cfg b/tests/wpt/web-platform-tests/tools/py/setup.cfg
deleted file mode 100644
index 272e488f363..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/setup.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-[wheel]
-universal = 1
-
-[devpi:upload]
-formats=sdist.tgz,bdist_wheel
diff --git a/tests/wpt/web-platform-tests/tools/py/setup.py b/tests/wpt/web-platform-tests/tools/py/setup.py
deleted file mode 100644
index 06f0885cd71..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/setup.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import os, sys
-
-from setuptools import setup
-
-def main():
- setup(
- name='py',
- description='library with cross-python path, ini-parsing, io, code, log facilities',
- long_description = open('README.txt').read(),
- version='1.4.31',
- url='http://pylib.readthedocs.org/',
- license='MIT license',
- platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
- author='holger krekel, Ronny Pfannschmidt, Benjamin Peterson and others',
- author_email='pytest-dev@python.org',
- classifiers=['Development Status :: 6 - Mature',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: MIT License',
- 'Operating System :: POSIX',
- 'Operating System :: Microsoft :: Windows',
- 'Operating System :: MacOS :: MacOS X',
- 'Topic :: Software Development :: Testing',
- 'Topic :: Software Development :: Libraries',
- 'Topic :: Utilities',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 3'],
- packages=['py',
- 'py._code',
- 'py._io',
- 'py._log',
- 'py._path',
- 'py._process',
- ],
- zip_safe=False,
- )
-
-if __name__ == '__main__':
- main()
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/code/test_assertion.py b/tests/wpt/web-platform-tests/tools/py/testing/code/test_assertion.py
deleted file mode 100644
index e2154d0fc7a..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/code/test_assertion.py
+++ /dev/null
@@ -1,308 +0,0 @@
-import pytest, py
-
-def exvalue():
- return py.std.sys.exc_info()[1]
-
-def f():
- return 2
-
-def test_assert():
- try:
- assert f() == 3
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith('assert 2 == 3\n')
-
-
-def test_assert_within_finally():
- excinfo = py.test.raises(ZeroDivisionError, """
- try:
- 1/0
- finally:
- i = 42
- """)
- s = excinfo.exconly()
- assert py.std.re.search("division.+by zero", s) is not None
-
- #def g():
- # A.f()
- #excinfo = getexcinfo(TypeError, g)
- #msg = getmsg(excinfo)
- #assert msg.find("must be called with A") != -1
-
-
-def test_assert_multiline_1():
- try:
- assert (f() ==
- 3)
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith('assert 2 == 3\n')
-
-def test_assert_multiline_2():
- try:
- assert (f() == (4,
- 3)[-1])
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith('assert 2 ==')
-
-def test_in():
- try:
- assert "hi" in [1, 2]
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith("assert 'hi' in")
-
-def test_is():
- try:
- assert 1 is 2
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith("assert 1 is 2")
-
-
-@py.test.mark.skipif("sys.version_info < (2,6)")
-def test_attrib():
- class Foo(object):
- b = 1
- i = Foo()
- try:
- assert i.b == 2
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith("assert 1 == 2")
-
-@py.test.mark.skipif("sys.version_info < (2,6)")
-def test_attrib_inst():
- class Foo(object):
- b = 1
- try:
- assert Foo().b == 2
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith("assert 1 == 2")
-
-def test_len():
- l = list(range(42))
- try:
- assert len(l) == 100
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith("assert 42 == 100")
- assert "where 42 = len([" in s
-
-
-def test_assert_keyword_arg():
- def f(x=3):
- return False
- try:
- assert f(x=5)
- except AssertionError:
- e = exvalue()
- assert "x=5" in e.msg
-
-# These tests should both fail, but should fail nicely...
-class WeirdRepr:
- def __repr__(self):
- return '<WeirdRepr\nsecond line>'
-
-def bug_test_assert_repr():
- v = WeirdRepr()
- try:
- assert v == 1
- except AssertionError:
- e = exvalue()
- assert e.msg.find('WeirdRepr') != -1
- assert e.msg.find('second line') != -1
- assert 0
-
-def test_assert_non_string():
- try:
- assert 0, ['list']
- except AssertionError:
- e = exvalue()
- assert e.msg.find("list") != -1
-
-def test_assert_implicit_multiline():
- try:
- x = [1,2,3]
- assert x != [1,
- 2, 3]
- except AssertionError:
- e = exvalue()
- assert e.msg.find('assert [1, 2, 3] !=') != -1
-
-
-def test_assert_with_brokenrepr_arg():
- class BrokenRepr:
- def __repr__(self): 0 / 0
- e = AssertionError(BrokenRepr())
- if e.msg.find("broken __repr__") == -1:
- py.test.fail("broken __repr__ not handle correctly")
-
-def test_multiple_statements_per_line():
- try:
- a = 1; assert a == 2
- except AssertionError:
- e = exvalue()
- assert "assert 1 == 2" in e.msg
-
-def test_power():
- try:
- assert 2**3 == 7
- except AssertionError:
- e = exvalue()
- assert "assert (2 ** 3) == 7" in e.msg
-
-
-class TestView:
-
- def setup_class(cls):
- cls.View = py.test.importorskip("py._code._assertionold").View
-
- def test_class_dispatch(self):
- ### Use a custom class hierarchy with existing instances
-
- class Picklable(self.View):
- pass
-
- class Simple(Picklable):
- __view__ = object
- def pickle(self):
- return repr(self.__obj__)
-
- class Seq(Picklable):
- __view__ = list, tuple, dict
- def pickle(self):
- return ';'.join(
- [Picklable(item).pickle() for item in self.__obj__])
-
- class Dict(Seq):
- __view__ = dict
- def pickle(self):
- return Seq.pickle(self) + '!' + Seq(self.values()).pickle()
-
- assert Picklable(123).pickle() == '123'
- assert Picklable([1,[2,3],4]).pickle() == '1;2;3;4'
- assert Picklable({1:2}).pickle() == '1!2'
-
- def test_viewtype_class_hierarchy(self):
- # Use a custom class hierarchy based on attributes of existing instances
- class Operation:
- "Existing class that I don't want to change."
- def __init__(self, opname, *args):
- self.opname = opname
- self.args = args
-
- existing = [Operation('+', 4, 5),
- Operation('getitem', '', 'join'),
- Operation('setattr', 'x', 'y', 3),
- Operation('-', 12, 1)]
-
- class PyOp(self.View):
- def __viewkey__(self):
- return self.opname
- def generate(self):
- return '%s(%s)' % (self.opname, ', '.join(map(repr, self.args)))
-
- class PyBinaryOp(PyOp):
- __view__ = ('+', '-', '*', '/')
- def generate(self):
- return '%s %s %s' % (self.args[0], self.opname, self.args[1])
-
- codelines = [PyOp(op).generate() for op in existing]
- assert codelines == ["4 + 5", "getitem('', 'join')",
- "setattr('x', 'y', 3)", "12 - 1"]
-
-def test_underscore_api():
- py.code._AssertionError
- py.code._reinterpret_old # used by pypy
- py.code._reinterpret
-
-@py.test.mark.skipif("sys.version_info < (2,6)")
-def test_assert_customizable_reprcompare(monkeypatch):
- util = pytest.importorskip("_pytest.assertion.util")
- monkeypatch.setattr(util, '_reprcompare', lambda *args: 'hello')
- try:
- assert 3 == 4
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert "hello" in s
-
-def test_assert_long_source_1():
- try:
- assert len == [
- (None, ['somet text', 'more text']),
- ]
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert 're-run' not in s
- assert 'somet text' in s
-
-def test_assert_long_source_2():
- try:
- assert(len == [
- (None, ['somet text', 'more text']),
- ])
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert 're-run' not in s
- assert 'somet text' in s
-
-def test_assert_raise_alias(testdir):
- testdir.makepyfile("""
- import sys
- EX = AssertionError
- def test_hello():
- raise EX("hello"
- "multi"
- "line")
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*def test_hello*",
- "*raise EX*",
- "*1 failed*",
- ])
-
-
-@pytest.mark.skipif("sys.version_info < (2,5)")
-def test_assert_raise_subclass():
- class SomeEx(AssertionError):
- def __init__(self, *args):
- super(SomeEx, self).__init__()
- try:
- raise SomeEx("hello")
- except AssertionError:
- s = str(exvalue())
- assert 're-run' not in s
- assert 'could not determine' in s
-
-def test_assert_raises_in_nonzero_of_object_pytest_issue10():
- class A(object):
- def __nonzero__(self):
- raise ValueError(42)
- def __lt__(self, other):
- return A()
- def __repr__(self):
- return "<MY42 object>"
- def myany(x):
- return True
- try:
- assert not(myany(A() < 0))
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert "<MY42 object> < 0" in s
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/code/test_excinfo.py b/tests/wpt/web-platform-tests/tools/py/testing/code/test_excinfo.py
deleted file mode 100644
index 65742c6f62e..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/code/test_excinfo.py
+++ /dev/null
@@ -1,909 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import py
-from py._code.code import FormattedExcinfo, ReprExceptionInfo
-queue = py.builtin._tryimport('queue', 'Queue')
-
-failsonjython = py.test.mark.xfail("sys.platform.startswith('java')")
-from test_source import astonly
-
-try:
- import importlib
-except ImportError:
- invalidate_import_caches = None
-else:
- invalidate_import_caches = getattr(importlib, "invalidate_caches", None)
-
-import pytest
-pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
-
-class TWMock:
- def __init__(self):
- self.lines = []
- def sep(self, sep, line=None):
- self.lines.append((sep, line))
- def line(self, line, **kw):
- self.lines.append(line)
- def markup(self, text, **kw):
- return text
-
- fullwidth = 80
-
-def test_excinfo_simple():
- try:
- raise ValueError
- except ValueError:
- info = py.code.ExceptionInfo()
- assert info.type == ValueError
-
-def test_excinfo_getstatement():
- def g():
- raise ValueError
- def f():
- g()
- try:
- f()
- except ValueError:
- excinfo = py.code.ExceptionInfo()
- linenumbers = [py.code.getrawcode(f).co_firstlineno-1+3,
- py.code.getrawcode(f).co_firstlineno-1+1,
- py.code.getrawcode(g).co_firstlineno-1+1,]
- l = list(excinfo.traceback)
- foundlinenumbers = [x.lineno for x in l]
- assert foundlinenumbers == linenumbers
- #for x in info:
- # print "%s:%d %s" %(x.path.relto(root), x.lineno, x.statement)
- #xxx
-
-# testchain for getentries test below
-def f():
- #
- raise ValueError
- #
-def g():
- #
- __tracebackhide__ = True
- f()
- #
-def h():
- #
- g()
- #
-
-class TestTraceback_f_g_h:
- def setup_method(self, method):
- try:
- h()
- except ValueError:
- self.excinfo = py.code.ExceptionInfo()
-
- def test_traceback_entries(self):
- tb = self.excinfo.traceback
- entries = list(tb)
- assert len(tb) == 4 # maybe fragile test
- assert len(entries) == 4 # maybe fragile test
- names = ['f', 'g', 'h']
- for entry in entries:
- try:
- names.remove(entry.frame.code.name)
- except ValueError:
- pass
- assert not names
-
- def test_traceback_entry_getsource(self):
- tb = self.excinfo.traceback
- s = str(tb[-1].getsource() )
- assert s.startswith("def f():")
- assert s.endswith("raise ValueError")
-
- @astonly
- @failsonjython
- def test_traceback_entry_getsource_in_construct(self):
- source = py.code.Source("""\
- def xyz():
- try:
- raise ValueError
- except somenoname:
- pass
- xyz()
- """)
- try:
- exec (source.compile())
- except NameError:
- tb = py.code.ExceptionInfo().traceback
- print (tb[-1].getsource())
- s = str(tb[-1].getsource())
- assert s.startswith("def xyz():\n try:")
- assert s.strip().endswith("except somenoname:")
-
- def test_traceback_cut(self):
- co = py.code.Code(f)
- path, firstlineno = co.path, co.firstlineno
- traceback = self.excinfo.traceback
- newtraceback = traceback.cut(path=path, firstlineno=firstlineno)
- assert len(newtraceback) == 1
- newtraceback = traceback.cut(path=path, lineno=firstlineno+2)
- assert len(newtraceback) == 1
-
- def test_traceback_cut_excludepath(self, testdir):
- p = testdir.makepyfile("def f(): raise ValueError")
- excinfo = py.test.raises(ValueError, "p.pyimport().f()")
- basedir = py.path.local(py.test.__file__).dirpath()
- newtraceback = excinfo.traceback.cut(excludepath=basedir)
- for x in newtraceback:
- if hasattr(x, 'path'):
- assert not py.path.local(x.path).relto(basedir)
- assert newtraceback[-1].frame.code.path == p
-
- def test_traceback_filter(self):
- traceback = self.excinfo.traceback
- ntraceback = traceback.filter()
- assert len(ntraceback) == len(traceback) - 1
-
- def test_traceback_recursion_index(self):
- def f(n):
- if n < 10:
- n += 1
- f(n)
- excinfo = py.test.raises(RuntimeError, f, 8)
- traceback = excinfo.traceback
- recindex = traceback.recursionindex()
- assert recindex == 3
-
- def test_traceback_only_specific_recursion_errors(self, monkeypatch):
- def f(n):
- if n == 0:
- raise RuntimeError("hello")
- f(n-1)
-
- excinfo = pytest.raises(RuntimeError, f, 100)
- monkeypatch.delattr(excinfo.traceback.__class__, "recursionindex")
- repr = excinfo.getrepr()
- assert "RuntimeError: hello" in str(repr.reprcrash)
-
- def test_traceback_no_recursion_index(self):
- def do_stuff():
- raise RuntimeError
- def reraise_me():
- import sys
- exc, val, tb = sys.exc_info()
- py.builtin._reraise(exc, val, tb)
- def f(n):
- try:
- do_stuff()
- except:
- reraise_me()
- excinfo = py.test.raises(RuntimeError, f, 8)
- traceback = excinfo.traceback
- recindex = traceback.recursionindex()
- assert recindex is None
-
- def test_traceback_messy_recursion(self):
- #XXX: simplified locally testable version
- decorator = py.test.importorskip('decorator').decorator
-
- def log(f, *k, **kw):
- print('%s %s' % (k, kw))
- f(*k, **kw)
- log = decorator(log)
-
- def fail():
- raise ValueError('')
-
- fail = log(log(fail))
-
- excinfo = py.test.raises(ValueError, fail)
- assert excinfo.traceback.recursionindex() is None
-
-
-
- def test_traceback_getcrashentry(self):
- def i():
- __tracebackhide__ = True
- raise ValueError
- def h():
- i()
- def g():
- __tracebackhide__ = True
- h()
- def f():
- g()
-
- excinfo = py.test.raises(ValueError, f)
- tb = excinfo.traceback
- entry = tb.getcrashentry()
- co = py.code.Code(h)
- assert entry.frame.code.path == co.path
- assert entry.lineno == co.firstlineno + 1
- assert entry.frame.code.name == 'h'
-
- def test_traceback_getcrashentry_empty(self):
- def g():
- __tracebackhide__ = True
- raise ValueError
- def f():
- __tracebackhide__ = True
- g()
-
- excinfo = py.test.raises(ValueError, f)
- tb = excinfo.traceback
- entry = tb.getcrashentry()
- co = py.code.Code(g)
- assert entry.frame.code.path == co.path
- assert entry.lineno == co.firstlineno + 2
- assert entry.frame.code.name == 'g'
-
-def hello(x):
- x + 5
-
-def test_tbentry_reinterpret():
- try:
- hello("hello")
- except TypeError:
- excinfo = py.code.ExceptionInfo()
- tbentry = excinfo.traceback[-1]
- msg = tbentry.reinterpret()
- assert msg.startswith("TypeError: ('hello' + 5)")
-
-def test_excinfo_exconly():
- excinfo = py.test.raises(ValueError, h)
- assert excinfo.exconly().startswith('ValueError')
- excinfo = py.test.raises(ValueError,
- "raise ValueError('hello\\nworld')")
- msg = excinfo.exconly(tryshort=True)
- assert msg.startswith('ValueError')
- assert msg.endswith("world")
-
-def test_excinfo_repr():
- excinfo = py.test.raises(ValueError, h)
- s = repr(excinfo)
- assert s == "<ExceptionInfo ValueError tblen=4>"
-
-def test_excinfo_str():
- excinfo = py.test.raises(ValueError, h)
- s = str(excinfo)
- assert s.startswith(__file__[:-9]) # pyc file and $py.class
- assert s.endswith("ValueError")
- assert len(s.split(":")) >= 3 # on windows it's 4
-
-def test_excinfo_errisinstance():
- excinfo = py.test.raises(ValueError, h)
- assert excinfo.errisinstance(ValueError)
-
-def test_excinfo_no_sourcecode():
- try:
- exec ("raise ValueError()")
- except ValueError:
- excinfo = py.code.ExceptionInfo()
- s = str(excinfo.traceback[-1])
- if py.std.sys.version_info < (2,5):
- assert s == " File '<string>':1 in ?\n ???\n"
- else:
- assert s == " File '<string>':1 in <module>\n ???\n"
-
-def test_excinfo_no_python_sourcecode(tmpdir):
- #XXX: simplified locally testable version
- tmpdir.join('test.txt').write("{{ h()}}:")
-
- jinja2 = py.test.importorskip('jinja2')
- loader = jinja2.FileSystemLoader(str(tmpdir))
- env = jinja2.Environment(loader=loader)
- template = env.get_template('test.txt')
- excinfo = py.test.raises(ValueError,
- template.render, h=h)
- for item in excinfo.traceback:
- print(item) #XXX: for some reason jinja.Template.render is printed in full
- item.source # shouldnt fail
- if item.path.basename == 'test.txt':
- assert str(item.source) == '{{ h()}}:'
-
-
-def test_entrysource_Queue_example():
- try:
- queue.Queue().get(timeout=0.001)
- except queue.Empty:
- excinfo = py.code.ExceptionInfo()
- entry = excinfo.traceback[-1]
- source = entry.getsource()
- assert source is not None
- s = str(source).strip()
- assert s.startswith("def get")
-
-def test_codepath_Queue_example():
- try:
- queue.Queue().get(timeout=0.001)
- except queue.Empty:
- excinfo = py.code.ExceptionInfo()
- entry = excinfo.traceback[-1]
- path = entry.path
- assert isinstance(path, py.path.local)
- assert path.basename.lower() == "queue.py"
- assert path.check()
-
-class TestFormattedExcinfo:
- def pytest_funcarg__importasmod(self, request):
- def importasmod(source):
- source = py.code.Source(source)
- tmpdir = request.getfuncargvalue("tmpdir")
- modpath = tmpdir.join("mod.py")
- tmpdir.ensure("__init__.py")
- modpath.write(source)
- if invalidate_import_caches is not None:
- invalidate_import_caches()
- return modpath.pyimport()
- return importasmod
-
- def excinfo_from_exec(self, source):
- source = py.code.Source(source).strip()
- try:
- exec (source.compile())
- except KeyboardInterrupt:
- raise
- except:
- return py.code.ExceptionInfo()
- assert 0, "did not raise"
-
- def test_repr_source(self):
- pr = FormattedExcinfo()
- source = py.code.Source("""
- def f(x):
- pass
- """).strip()
- pr.flow_marker = "|"
- lines = pr.get_source(source, 0)
- assert len(lines) == 2
- assert lines[0] == "| def f(x):"
- assert lines[1] == " pass"
-
- def test_repr_source_excinfo(self):
- """ check if indentation is right """
- pr = FormattedExcinfo()
- excinfo = self.excinfo_from_exec("""
- def f():
- assert 0
- f()
- """)
- pr = FormattedExcinfo()
- source = pr._getentrysource(excinfo.traceback[-1])
- lines = pr.get_source(source, 1, excinfo)
- assert lines == [
- ' def f():',
- '> assert 0',
- 'E assert 0'
- ]
-
-
- def test_repr_source_not_existing(self):
- pr = FormattedExcinfo()
- co = compile("raise ValueError()", "", "exec")
- try:
- exec (co)
- except ValueError:
- excinfo = py.code.ExceptionInfo()
- repr = pr.repr_excinfo(excinfo)
- assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
-
- def test_repr_many_line_source_not_existing(self):
- pr = FormattedExcinfo()
- co = compile("""
-a = 1
-raise ValueError()
-""", "", "exec")
- try:
- exec (co)
- except ValueError:
- excinfo = py.code.ExceptionInfo()
- repr = pr.repr_excinfo(excinfo)
- assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
-
- def test_repr_source_failing_fullsource(self):
- pr = FormattedExcinfo()
-
- class FakeCode(object):
- class raw:
- co_filename = '?'
- path = '?'
- firstlineno = 5
-
- def fullsource(self):
- return None
- fullsource = property(fullsource)
-
- class FakeFrame(object):
- code = FakeCode()
- f_locals = {}
- f_globals = {}
-
- class FakeTracebackEntry(py.code.Traceback.Entry):
- def __init__(self, tb):
- self.lineno = 5+3
-
- @property
- def frame(self):
- return FakeFrame()
-
- class Traceback(py.code.Traceback):
- Entry = FakeTracebackEntry
-
- class FakeExcinfo(py.code.ExceptionInfo):
- typename = "Foo"
- def __init__(self):
- pass
-
- def exconly(self, tryshort):
- return "EXC"
- def errisinstance(self, cls):
- return False
-
- excinfo = FakeExcinfo()
- class FakeRawTB(object):
- tb_next = None
- tb = FakeRawTB()
- excinfo.traceback = Traceback(tb)
-
- fail = IOError()
- repr = pr.repr_excinfo(excinfo)
- assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
-
- fail = py.error.ENOENT
- repr = pr.repr_excinfo(excinfo)
- assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
-
-
- def test_repr_local(self):
- p = FormattedExcinfo(showlocals=True)
- loc = {'y': 5, 'z': 7, 'x': 3, '@x': 2, '__builtins__': {}}
- reprlocals = p.repr_locals(loc)
- assert reprlocals.lines
- assert reprlocals.lines[0] == '__builtins__ = <builtins>'
- assert reprlocals.lines[1] == 'x = 3'
- assert reprlocals.lines[2] == 'y = 5'
- assert reprlocals.lines[3] == 'z = 7'
-
- def test_repr_tracebackentry_lines(self, importasmod):
- mod = importasmod("""
- def func1():
- raise ValueError("hello\\nworld")
- """)
- excinfo = py.test.raises(ValueError, mod.func1)
- excinfo.traceback = excinfo.traceback.filter()
- p = FormattedExcinfo()
- reprtb = p.repr_traceback_entry(excinfo.traceback[-1])
-
- # test as intermittent entry
- lines = reprtb.lines
- assert lines[0] == ' def func1():'
- assert lines[1] == '> raise ValueError("hello\\nworld")'
-
- # test as last entry
- p = FormattedExcinfo(showlocals=True)
- repr_entry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
- lines = repr_entry.lines
- assert lines[0] == ' def func1():'
- assert lines[1] == '> raise ValueError("hello\\nworld")'
- assert lines[2] == 'E ValueError: hello'
- assert lines[3] == 'E world'
- assert not lines[4:]
-
- loc = repr_entry.reprlocals is not None
- loc = repr_entry.reprfileloc
- assert loc.path == mod.__file__
- assert loc.lineno == 3
- #assert loc.message == "ValueError: hello"
-
- def test_repr_tracebackentry_lines(self, importasmod):
- mod = importasmod("""
- def func1(m, x, y, z):
- raise ValueError("hello\\nworld")
- """)
- excinfo = py.test.raises(ValueError, mod.func1, "m"*90, 5, 13, "z"*120)
- excinfo.traceback = excinfo.traceback.filter()
- entry = excinfo.traceback[-1]
- p = FormattedExcinfo(funcargs=True)
- reprfuncargs = p.repr_args(entry)
- assert reprfuncargs.args[0] == ('m', repr("m"*90))
- assert reprfuncargs.args[1] == ('x', '5')
- assert reprfuncargs.args[2] == ('y', '13')
- assert reprfuncargs.args[3] == ('z', repr("z" * 120))
-
- p = FormattedExcinfo(funcargs=True)
- repr_entry = p.repr_traceback_entry(entry)
- assert repr_entry.reprfuncargs.args == reprfuncargs.args
- tw = TWMock()
- repr_entry.toterminal(tw)
- assert tw.lines[0] == "m = " + repr('m' * 90)
- assert tw.lines[1] == "x = 5, y = 13"
- assert tw.lines[2] == "z = " + repr('z' * 120)
-
- def test_repr_tracebackentry_lines_var_kw_args(self, importasmod):
- mod = importasmod("""
- def func1(x, *y, **z):
- raise ValueError("hello\\nworld")
- """)
- excinfo = py.test.raises(ValueError, mod.func1, 'a', 'b', c='d')
- excinfo.traceback = excinfo.traceback.filter()
- entry = excinfo.traceback[-1]
- p = FormattedExcinfo(funcargs=True)
- reprfuncargs = p.repr_args(entry)
- assert reprfuncargs.args[0] == ('x', repr('a'))
- assert reprfuncargs.args[1] == ('y', repr(('b',)))
- assert reprfuncargs.args[2] == ('z', repr({'c': 'd'}))
-
- p = FormattedExcinfo(funcargs=True)
- repr_entry = p.repr_traceback_entry(entry)
- assert repr_entry.reprfuncargs.args == reprfuncargs.args
- tw = TWMock()
- repr_entry.toterminal(tw)
- assert tw.lines[0] == "x = 'a', y = ('b',), z = {'c': 'd'}"
-
- def test_repr_tracebackentry_short(self, importasmod):
- mod = importasmod("""
- def func1():
- raise ValueError("hello")
- def entry():
- func1()
- """)
- excinfo = py.test.raises(ValueError, mod.entry)
- p = FormattedExcinfo(style="short")
- reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
- lines = reprtb.lines
- basename = py.path.local(mod.__file__).basename
- assert lines[0] == ' func1()'
- assert basename in str(reprtb.reprfileloc.path)
- assert reprtb.reprfileloc.lineno == 5
-
- # test last entry
- p = FormattedExcinfo(style="short")
- reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
- lines = reprtb.lines
- assert lines[0] == ' raise ValueError("hello")'
- assert lines[1] == 'E ValueError: hello'
- assert basename in str(reprtb.reprfileloc.path)
- assert reprtb.reprfileloc.lineno == 3
-
- def test_repr_tracebackentry_no(self, importasmod):
- mod = importasmod("""
- def func1():
- raise ValueError("hello")
- def entry():
- func1()
- """)
- excinfo = py.test.raises(ValueError, mod.entry)
- p = FormattedExcinfo(style="no")
- p.repr_traceback_entry(excinfo.traceback[-2])
-
- p = FormattedExcinfo(style="no")
- reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
- lines = reprentry.lines
- assert lines[0] == 'E ValueError: hello'
- assert not lines[1:]
-
- def test_repr_traceback_tbfilter(self, importasmod):
- mod = importasmod("""
- def f(x):
- raise ValueError(x)
- def entry():
- f(0)
- """)
- excinfo = py.test.raises(ValueError, mod.entry)
- p = FormattedExcinfo(tbfilter=True)
- reprtb = p.repr_traceback(excinfo)
- assert len(reprtb.reprentries) == 2
- p = FormattedExcinfo(tbfilter=False)
- reprtb = p.repr_traceback(excinfo)
- assert len(reprtb.reprentries) == 3
-
- def test_traceback_short_no_source(self, importasmod, monkeypatch):
- mod = importasmod("""
- def func1():
- raise ValueError("hello")
- def entry():
- func1()
- """)
- excinfo = py.test.raises(ValueError, mod.entry)
- from py._code.code import Code
- monkeypatch.setattr(Code, 'path', 'bogus')
- excinfo.traceback[0].frame.code.path = "bogus"
- p = FormattedExcinfo(style="short")
- reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
- lines = reprtb.lines
- last_p = FormattedExcinfo(style="short")
- last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
- last_lines = last_reprtb.lines
- monkeypatch.undo()
- basename = py.path.local(mod.__file__).basename
- assert lines[0] == ' func1()'
-
- assert last_lines[0] == ' raise ValueError("hello")'
- assert last_lines[1] == 'E ValueError: hello'
-
- def test_repr_traceback_and_excinfo(self, importasmod):
- mod = importasmod("""
- def f(x):
- raise ValueError(x)
- def entry():
- f(0)
- """)
- excinfo = py.test.raises(ValueError, mod.entry)
-
- for style in ("long", "short"):
- p = FormattedExcinfo(style=style)
- reprtb = p.repr_traceback(excinfo)
- assert len(reprtb.reprentries) == 2
- assert reprtb.style == style
- assert not reprtb.extraline
- repr = p.repr_excinfo(excinfo)
- assert repr.reprtraceback
- assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries)
- assert repr.reprcrash.path.endswith("mod.py")
- assert repr.reprcrash.message == "ValueError: 0"
-
- def test_repr_traceback_with_invalid_cwd(self, importasmod, monkeypatch):
- mod = importasmod("""
- def f(x):
- raise ValueError(x)
- def entry():
- f(0)
- """)
- excinfo = py.test.raises(ValueError, mod.entry)
-
- p = FormattedExcinfo()
- def raiseos():
- raise OSError(2)
- monkeypatch.setattr(py.std.os, 'getcwd', raiseos)
- assert p._makepath(__file__) == __file__
- reprtb = p.repr_traceback(excinfo)
-
- def test_repr_excinfo_addouterr(self, importasmod):
- mod = importasmod("""
- def entry():
- raise ValueError()
- """)
- excinfo = py.test.raises(ValueError, mod.entry)
- repr = excinfo.getrepr()
- repr.addsection("title", "content")
- twmock = TWMock()
- repr.toterminal(twmock)
- assert twmock.lines[-1] == "content"
- assert twmock.lines[-2] == ("-", "title")
-
- def test_repr_excinfo_reprcrash(self, importasmod):
- mod = importasmod("""
- def entry():
- raise ValueError()
- """)
- excinfo = py.test.raises(ValueError, mod.entry)
- repr = excinfo.getrepr()
- assert repr.reprcrash.path.endswith("mod.py")
- assert repr.reprcrash.lineno == 3
- assert repr.reprcrash.message == "ValueError"
- assert str(repr.reprcrash).endswith("mod.py:3: ValueError")
-
- def test_repr_traceback_recursion(self, importasmod):
- mod = importasmod("""
- def rec2(x):
- return rec1(x+1)
- def rec1(x):
- return rec2(x-1)
- def entry():
- rec1(42)
- """)
- excinfo = py.test.raises(RuntimeError, mod.entry)
-
- for style in ("short", "long", "no"):
- p = FormattedExcinfo(style="short")
- reprtb = p.repr_traceback(excinfo)
- assert reprtb.extraline == "!!! Recursion detected (same locals & position)"
- assert str(reprtb)
-
- def test_tb_entry_AssertionError(self, importasmod):
- # probably this test is a bit redundant
- # as py/magic/testing/test_assertion.py
- # already tests correctness of
- # assertion-reinterpretation logic
- mod = importasmod("""
- def somefunc():
- x = 1
- assert x == 2
- """)
- excinfo = py.test.raises(AssertionError, mod.somefunc)
-
- p = FormattedExcinfo()
- reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
- lines = reprentry.lines
- assert lines[-1] == "E assert 1 == 2"
-
- def test_reprexcinfo_getrepr(self, importasmod):
- mod = importasmod("""
- def f(x):
- raise ValueError(x)
- def entry():
- f(0)
- """)
- excinfo = py.test.raises(ValueError, mod.entry)
-
- for style in ("short", "long", "no"):
- for showlocals in (True, False):
- repr = excinfo.getrepr(style=style, showlocals=showlocals)
- assert isinstance(repr, ReprExceptionInfo)
- assert repr.reprtraceback.style == style
-
- def test_reprexcinfo_unicode(self):
- from py._code.code import TerminalRepr
- class MyRepr(TerminalRepr):
- def toterminal(self, tw):
- tw.line(py.builtin._totext("я", "utf-8"))
- x = py.builtin._totext(MyRepr())
- assert x == py.builtin._totext("я", "utf-8")
-
- def test_toterminal_long(self, importasmod):
- mod = importasmod("""
- def g(x):
- raise ValueError(x)
- def f():
- g(3)
- """)
- excinfo = py.test.raises(ValueError, mod.f)
- excinfo.traceback = excinfo.traceback.filter()
- repr = excinfo.getrepr()
- tw = TWMock()
- repr.toterminal(tw)
- assert tw.lines[0] == ""
- tw.lines.pop(0)
- assert tw.lines[0] == " def f():"
- assert tw.lines[1] == "> g(3)"
- assert tw.lines[2] == ""
- assert tw.lines[3].endswith("mod.py:5: ")
- assert tw.lines[4] == ("_ ", None)
- assert tw.lines[5] == ""
- assert tw.lines[6] == " def g(x):"
- assert tw.lines[7] == "> raise ValueError(x)"
- assert tw.lines[8] == "E ValueError: 3"
- assert tw.lines[9] == ""
- assert tw.lines[10].endswith("mod.py:3: ValueError")
-
- def test_toterminal_long_missing_source(self, importasmod, tmpdir):
- mod = importasmod("""
- def g(x):
- raise ValueError(x)
- def f():
- g(3)
- """)
- excinfo = py.test.raises(ValueError, mod.f)
- tmpdir.join('mod.py').remove()
- excinfo.traceback = excinfo.traceback.filter()
- repr = excinfo.getrepr()
- tw = TWMock()
- repr.toterminal(tw)
- assert tw.lines[0] == ""
- tw.lines.pop(0)
- assert tw.lines[0] == "> ???"
- assert tw.lines[1] == ""
- assert tw.lines[2].endswith("mod.py:5: ")
- assert tw.lines[3] == ("_ ", None)
- assert tw.lines[4] == ""
- assert tw.lines[5] == "> ???"
- assert tw.lines[6] == "E ValueError: 3"
- assert tw.lines[7] == ""
- assert tw.lines[8].endswith("mod.py:3: ValueError")
-
- def test_toterminal_long_incomplete_source(self, importasmod, tmpdir):
- mod = importasmod("""
- def g(x):
- raise ValueError(x)
- def f():
- g(3)
- """)
- excinfo = py.test.raises(ValueError, mod.f)
- tmpdir.join('mod.py').write('asdf')
- excinfo.traceback = excinfo.traceback.filter()
- repr = excinfo.getrepr()
- tw = TWMock()
- repr.toterminal(tw)
- assert tw.lines[0] == ""
- tw.lines.pop(0)
- assert tw.lines[0] == "> ???"
- assert tw.lines[1] == ""
- assert tw.lines[2].endswith("mod.py:5: ")
- assert tw.lines[3] == ("_ ", None)
- assert tw.lines[4] == ""
- assert tw.lines[5] == "> ???"
- assert tw.lines[6] == "E ValueError: 3"
- assert tw.lines[7] == ""
- assert tw.lines[8].endswith("mod.py:3: ValueError")
-
- def test_toterminal_long_filenames(self, importasmod):
- mod = importasmod("""
- def f():
- raise ValueError()
- """)
- excinfo = py.test.raises(ValueError, mod.f)
- tw = TWMock()
- path = py.path.local(mod.__file__)
- old = path.dirpath().chdir()
- try:
- repr = excinfo.getrepr(abspath=False)
- repr.toterminal(tw)
- line = tw.lines[-1]
- x = py.path.local().bestrelpath(path)
- if len(x) < len(str(path)):
- assert line == "mod.py:3: ValueError"
-
- repr = excinfo.getrepr(abspath=True)
- repr.toterminal(tw)
- line = tw.lines[-1]
- assert line == "%s:3: ValueError" %(path,)
- finally:
- old.chdir()
-
- @py.test.mark.multi(reproptions=[
- {'style': style, 'showlocals': showlocals,
- 'funcargs': funcargs, 'tbfilter': tbfilter
- } for style in ("long", "short", "no")
- for showlocals in (True, False)
- for tbfilter in (True, False)
- for funcargs in (True, False)])
- def test_format_excinfo(self, importasmod, reproptions):
- mod = importasmod("""
- def g(x):
- raise ValueError(x)
- def f():
- g(3)
- """)
- excinfo = py.test.raises(ValueError, mod.f)
- tw = py.io.TerminalWriter(stringio=True)
- repr = excinfo.getrepr(**reproptions)
- repr.toterminal(tw)
- assert tw.stringio.getvalue()
-
-
- def test_native_style(self):
- excinfo = self.excinfo_from_exec("""
- assert 0
- """)
- repr = excinfo.getrepr(style='native')
- assert "assert 0" in str(repr.reprcrash)
- s = str(repr)
- assert s.startswith('Traceback (most recent call last):\n File')
- assert s.endswith('\nAssertionError: assert 0')
- assert 'exec (source.compile())' in s
- # python 2.4 fails to get the source line for the assert
- if py.std.sys.version_info >= (2, 5):
- assert s.count('assert 0') == 2
-
- def test_traceback_repr_style(self, importasmod):
- mod = importasmod("""
- def f():
- g()
- def g():
- h()
- def h():
- i()
- def i():
- raise ValueError()
- """)
- excinfo = py.test.raises(ValueError, mod.f)
- excinfo.traceback = excinfo.traceback.filter()
- excinfo.traceback[1].set_repr_style("short")
- excinfo.traceback[2].set_repr_style("short")
- r = excinfo.getrepr(style="long")
- tw = TWMock()
- r.toterminal(tw)
- for line in tw.lines: print (line)
- assert tw.lines[0] == ""
- assert tw.lines[1] == " def f():"
- assert tw.lines[2] == "> g()"
- assert tw.lines[3] == ""
- assert tw.lines[4].endswith("mod.py:3: ")
- assert tw.lines[5] == ("_ ", None)
- assert tw.lines[6].endswith("in g")
- assert tw.lines[7] == " h()"
- assert tw.lines[8].endswith("in h")
- assert tw.lines[9] == " i()"
- assert tw.lines[10] == ("_ ", None)
- assert tw.lines[11] == ""
- assert tw.lines[12] == " def i():"
- assert tw.lines[13] == "> raise ValueError()"
- assert tw.lines[14] == "E ValueError"
- assert tw.lines[15] == ""
- assert tw.lines[16].endswith("mod.py:9: ValueError")
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/code/test_source.py b/tests/wpt/web-platform-tests/tools/py/testing/code/test_source.py
deleted file mode 100644
index 830de2c95de..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/code/test_source.py
+++ /dev/null
@@ -1,651 +0,0 @@
-from py.code import Source
-import py
-import sys
-
-from py._code.source import _ast
-if _ast is not None:
- astonly = py.test.mark.nothing
-else:
- astonly = py.test.mark.xfail("True", reason="only works with AST-compile")
-
-failsonjython = py.test.mark.xfail("sys.platform.startswith('java')")
-
-def test_source_str_function():
- x = Source("3")
- assert str(x) == "3"
-
- x = Source(" 3")
- assert str(x) == "3"
-
- x = Source("""
- 3
- """, rstrip=False)
- assert str(x) == "\n3\n "
-
- x = Source("""
- 3
- """, rstrip=True)
- assert str(x) == "\n3"
-
-def test_unicode():
- try:
- unicode
- except NameError:
- return
- x = Source(unicode("4"))
- assert str(x) == "4"
- co = py.code.compile(unicode('u"\xc3\xa5"', 'utf8'), mode='eval')
- val = eval(co)
- assert isinstance(val, unicode)
-
-def test_source_from_function():
- source = py.code.Source(test_source_str_function)
- assert str(source).startswith('def test_source_str_function():')
-
-def test_source_from_method():
- class TestClass:
- def test_method(self):
- pass
- source = py.code.Source(TestClass().test_method)
- assert source.lines == ["def test_method(self):",
- " pass"]
-
-def test_source_from_lines():
- lines = ["a \n", "b\n", "c"]
- source = py.code.Source(lines)
- assert source.lines == ['a ', 'b', 'c']
-
-def test_source_from_inner_function():
- def f():
- pass
- source = py.code.Source(f, deindent=False)
- assert str(source).startswith(' def f():')
- source = py.code.Source(f)
- assert str(source).startswith('def f():')
-
-def test_source_putaround_simple():
- source = Source("raise ValueError")
- source = source.putaround(
- "try:", """\
- except ValueError:
- x = 42
- else:
- x = 23""")
- assert str(source)=="""\
-try:
- raise ValueError
-except ValueError:
- x = 42
-else:
- x = 23"""
-
-def test_source_putaround():
- source = Source()
- source = source.putaround("""
- if 1:
- x=1
- """)
- assert str(source).strip() == "if 1:\n x=1"
-
-def test_source_strips():
- source = Source("")
- assert source == Source()
- assert str(source) == ''
- assert source.strip() == source
-
-def test_source_strip_multiline():
- source = Source()
- source.lines = ["", " hello", " "]
- source2 = source.strip()
- assert source2.lines == [" hello"]
-
-def test_syntaxerror_rerepresentation():
- ex = py.test.raises(SyntaxError, py.code.compile, 'xyz xyz')
- assert ex.value.lineno == 1
- assert ex.value.offset in (4,7) # XXX pypy/jython versus cpython?
- assert ex.value.text.strip(), 'x x'
-
-def test_isparseable():
- assert Source("hello").isparseable()
- assert Source("if 1:\n pass").isparseable()
- assert Source(" \nif 1:\n pass").isparseable()
- assert not Source("if 1:\n").isparseable()
- assert not Source(" \nif 1:\npass").isparseable()
- assert not Source(chr(0)).isparseable()
-
-class TestAccesses:
- source = Source("""\
- def f(x):
- pass
- def g(x):
- pass
- """)
- def test_getrange(self):
- x = self.source[0:2]
- assert x.isparseable()
- assert len(x.lines) == 2
- assert str(x) == "def f(x):\n pass"
-
- def test_getline(self):
- x = self.source[0]
- assert x == "def f(x):"
-
- def test_len(self):
- assert len(self.source) == 4
-
- def test_iter(self):
- l = [x for x in self.source]
- assert len(l) == 4
-
-class TestSourceParsingAndCompiling:
- source = Source("""\
- def f(x):
- assert (x ==
- 3 +
- 4)
- """).strip()
-
- def test_compile(self):
- co = py.code.compile("x=3")
- d = {}
- exec (co, d)
- assert d['x'] == 3
-
- def test_compile_and_getsource_simple(self):
- co = py.code.compile("x=3")
- exec (co)
- source = py.code.Source(co)
- assert str(source) == "x=3"
-
- def test_compile_and_getsource_through_same_function(self):
- def gensource(source):
- return py.code.compile(source)
- co1 = gensource("""
- def f():
- raise KeyError()
- """)
- co2 = gensource("""
- def f():
- raise ValueError()
- """)
- source1 = py.std.inspect.getsource(co1)
- assert 'KeyError' in source1
- source2 = py.std.inspect.getsource(co2)
- assert 'ValueError' in source2
-
- def test_getstatement(self):
- #print str(self.source)
- ass = str(self.source[1:])
- for i in range(1, 4):
- #print "trying start in line %r" % self.source[i]
- s = self.source.getstatement(i)
- #x = s.deindent()
- assert str(s) == ass
-
- def test_getstatementrange_triple_quoted(self):
- #print str(self.source)
- source = Source("""hello('''
- ''')""")
- s = source.getstatement(0)
- assert s == str(source)
- s = source.getstatement(1)
- assert s == str(source)
-
- @astonly
- def test_getstatementrange_within_constructs(self):
- source = Source("""\
- try:
- try:
- raise ValueError
- except SomeThing:
- pass
- finally:
- 42
- """)
- assert len(source) == 7
- # check all lineno's that could occur in a traceback
- #assert source.getstatementrange(0) == (0, 7)
- #assert source.getstatementrange(1) == (1, 5)
- assert source.getstatementrange(2) == (2, 3)
- assert source.getstatementrange(3) == (3, 4)
- assert source.getstatementrange(4) == (4, 5)
- #assert source.getstatementrange(5) == (0, 7)
- assert source.getstatementrange(6) == (6, 7)
-
- def test_getstatementrange_bug(self):
- source = Source("""\
- try:
- x = (
- y +
- z)
- except:
- pass
- """)
- assert len(source) == 6
- assert source.getstatementrange(2) == (1, 4)
-
- def test_getstatementrange_bug2(self):
- source = Source("""\
- assert (
- 33
- ==
- [
- X(3,
- b=1, c=2
- ),
- ]
- )
- """)
- assert len(source) == 9
- assert source.getstatementrange(5) == (0, 9)
-
- def test_getstatementrange_ast_issue58(self):
- source = Source("""\
-
- def test_some():
- for a in [a for a in
- CAUSE_ERROR]: pass
-
- x = 3
- """)
- assert getstatement(2, source).lines == source.lines[2:3]
- assert getstatement(3, source).lines == source.lines[3:4]
-
- @py.test.mark.skipif("sys.version_info < (2,6)")
- def test_getstatementrange_out_of_bounds_py3(self):
- source = Source("if xxx:\n from .collections import something")
- r = source.getstatementrange(1)
- assert r == (1,2)
-
- def test_getstatementrange_with_syntaxerror_issue7(self):
- source = Source(":")
- py.test.raises(SyntaxError, lambda: source.getstatementrange(0))
-
- @py.test.mark.skipif("sys.version_info < (2,6)")
- def test_compile_to_ast(self):
- import ast
- source = Source("x = 4")
- mod = source.compile(flag=ast.PyCF_ONLY_AST)
- assert isinstance(mod, ast.Module)
- compile(mod, "<filename>", "exec")
-
- def test_compile_and_getsource(self):
- co = self.source.compile()
- py.builtin.exec_(co, globals())
- f(7)
- excinfo = py.test.raises(AssertionError, "f(6)")
- frame = excinfo.traceback[-1].frame
- stmt = frame.code.fullsource.getstatement(frame.lineno)
- #print "block", str(block)
- assert str(stmt).strip().startswith('assert')
-
- def test_compilefuncs_and_path_sanity(self):
- def check(comp, name):
- co = comp(self.source, name)
- if not name:
- expected = "codegen %s:%d>" %(mypath, mylineno+2+1)
- else:
- expected = "codegen %r %s:%d>" % (name, mypath, mylineno+2+1)
- fn = co.co_filename
- assert fn.endswith(expected)
-
- mycode = py.code.Code(self.test_compilefuncs_and_path_sanity)
- mylineno = mycode.firstlineno
- mypath = mycode.path
-
- for comp in py.code.compile, py.code.Source.compile:
- for name in '', None, 'my':
- yield check, comp, name
-
- def test_offsetless_synerr(self):
- py.test.raises(SyntaxError, py.code.compile, "lambda a,a: 0", mode='eval')
-
-def test_getstartingblock_singleline():
- class A:
- def __init__(self, *args):
- frame = sys._getframe(1)
- self.source = py.code.Frame(frame).statement
-
- x = A('x', 'y')
-
- l = [i for i in x.source.lines if i.strip()]
- assert len(l) == 1
-
-def test_getstartingblock_multiline():
- class A:
- def __init__(self, *args):
- frame = sys._getframe(1)
- self.source = py.code.Frame(frame).statement
-
- x = A('x',
- 'y' \
- ,
- 'z')
-
- l = [i for i in x.source.lines if i.strip()]
- assert len(l) == 4
-
-def test_getline_finally():
- def c(): pass
- excinfo = py.test.raises(TypeError, """
- teardown = None
- try:
- c(1)
- finally:
- if teardown:
- teardown()
- """)
- source = excinfo.traceback[-1].statement
- assert str(source).strip() == 'c(1)'
-
-def test_getfuncsource_dynamic():
- source = """
- def f():
- raise ValueError
-
- def g(): pass
- """
- co = py.code.compile(source)
- py.builtin.exec_(co, globals())
- assert str(py.code.Source(f)).strip() == 'def f():\n raise ValueError'
- assert str(py.code.Source(g)).strip() == 'def g(): pass'
-
-
-def test_getfuncsource_with_multine_string():
- def f():
- c = '''while True:
- pass
-'''
- assert str(py.code.Source(f)).strip() == "def f():\n c = '''while True:\n pass\n'''"
-
-
-def test_deindent():
- from py._code.source import deindent as deindent
- assert deindent(['\tfoo', '\tbar', ]) == ['foo', 'bar']
-
- def f():
- c = '''while True:
- pass
-'''
- import inspect
- lines = deindent(inspect.getsource(f).splitlines())
- assert lines == ["def f():", " c = '''while True:", " pass", "'''"]
-
- source = """
- def f():
- def g():
- pass
- """
- lines = deindent(source.splitlines())
- assert lines == ['', 'def f():', ' def g():', ' pass', ' ']
-
-@py.test.mark.xfail("sys.version_info[:3] < (2,7,0) or "
- "((3,0) <= sys.version_info[:2] < (3,2))")
-def test_source_of_class_at_eof_without_newline(tmpdir):
- # this test fails because the implicit inspect.getsource(A) below
- # does not return the "x = 1" last line.
- source = py.code.Source('''
- class A(object):
- def method(self):
- x = 1
- ''')
- path = tmpdir.join("a.py")
- path.write(source)
- s2 = py.code.Source(tmpdir.join("a.py").pyimport().A)
- assert str(source).strip() == str(s2).strip()
-
-if True:
- def x():
- pass
-
-def test_getsource_fallback():
- from py._code.source import getsource
- expected = """def x():
- pass"""
- src = getsource(x)
- assert src == expected
-
-def test_idem_compile_and_getsource():
- from py._code.source import getsource
- expected = "def x(): pass"
- co = py.code.compile(expected)
- src = getsource(co)
- assert src == expected
-
-def test_findsource_fallback():
- from py._code.source import findsource
- src, lineno = findsource(x)
- assert 'test_findsource_simple' in str(src)
- assert src[lineno] == ' def x():'
-
-def test_findsource():
- from py._code.source import findsource
- co = py.code.compile("""if 1:
- def x():
- pass
-""")
-
- src, lineno = findsource(co)
- assert 'if 1:' in str(src)
-
- d = {}
- eval(co, d)
- src, lineno = findsource(d['x'])
- assert 'if 1:' in str(src)
- assert src[lineno] == " def x():"
-
-
-def test_getfslineno():
- from py.code import getfslineno
-
- def f(x):
- pass
-
- fspath, lineno = getfslineno(f)
-
- assert fspath.basename == "test_source.py"
- assert lineno == py.code.getrawcode(f).co_firstlineno-1 # see findsource
-
- class A(object):
- pass
-
- fspath, lineno = getfslineno(A)
-
- _, A_lineno = py.std.inspect.findsource(A)
- assert fspath.basename == "test_source.py"
- assert lineno == A_lineno
-
- assert getfslineno(3) == ("", -1)
- class B:
- pass
- B.__name__ = "B2"
- assert getfslineno(B)[1] == -1
-
-def test_code_of_object_instance_with_call():
- class A:
- pass
- py.test.raises(TypeError, lambda: py.code.Source(A()))
- class WithCall:
- def __call__(self):
- pass
-
- code = py.code.Code(WithCall())
- assert 'pass' in str(code.source())
-
- class Hello(object):
- def __call__(self):
- pass
- py.test.raises(TypeError, lambda: py.code.Code(Hello))
-
-
-def getstatement(lineno, source):
- from py._code.source import getstatementrange_ast
- source = py.code.Source(source, deindent=False)
- ast, start, end = getstatementrange_ast(lineno, source)
- return source[start:end]
-
-def test_oneline():
- source = getstatement(0, "raise ValueError")
- assert str(source) == "raise ValueError"
-
-def test_comment_and_no_newline_at_end():
- from py._code.source import getstatementrange_ast
- source = Source(['def test_basic_complex():',
- ' assert 1 == 2',
- '# vim: filetype=pyopencl:fdm=marker'])
- ast, start, end = getstatementrange_ast(1, source)
- assert end == 2
-
-def test_oneline_and_comment():
- source = getstatement(0, "raise ValueError\n#hello")
- assert str(source) == "raise ValueError"
-
-def test_comments():
- source = '''def test():
- "comment 1"
- x = 1
- # comment 2
- # comment 3
-
- assert False
-
-"""
-comment 4
-"""
-'''
- for line in range(2,6):
- assert str(getstatement(line, source)) == ' x = 1'
- for line in range(6,10):
- assert str(getstatement(line, source)) == ' assert False'
- assert str(getstatement(10, source)) == '"""'
-
-def test_comment_in_statement():
- source = '''test(foo=1,
- # comment 1
- bar=2)
-'''
- for line in range(1,3):
- assert str(getstatement(line, source)) == \
- 'test(foo=1,\n # comment 1\n bar=2)'
-
-def test_single_line_else():
- source = getstatement(1, "if False: 2\nelse: 3")
- assert str(source) == "else: 3"
-
-def test_single_line_finally():
- source = getstatement(1, "try: 1\nfinally: 3")
- assert str(source) == "finally: 3"
-
-def test_issue55():
- source = ('def round_trip(dinp):\n assert 1 == dinp\n'
- 'def test_rt():\n round_trip("""\n""")\n')
- s = getstatement(3, source)
- assert str(s) == ' round_trip("""\n""")'
-
-
-def XXXtest_multiline():
- source = getstatement(0, """\
-raise ValueError(
- 23
-)
-x = 3
-""")
- assert str(source) == "raise ValueError(\n 23\n)"
-
-class TestTry:
- pytestmark = astonly
- source = """\
-try:
- raise ValueError
-except Something:
- raise IndexError(1)
-else:
- raise KeyError()
-"""
-
- def test_body(self):
- source = getstatement(1, self.source)
- assert str(source) == " raise ValueError"
-
- def test_except_line(self):
- source = getstatement(2, self.source)
- assert str(source) == "except Something:"
-
- def test_except_body(self):
- source = getstatement(3, self.source)
- assert str(source) == " raise IndexError(1)"
-
- def test_else(self):
- source = getstatement(5, self.source)
- assert str(source) == " raise KeyError()"
-
-class TestTryFinally:
- source = """\
-try:
- raise ValueError
-finally:
- raise IndexError(1)
-"""
-
- def test_body(self):
- source = getstatement(1, self.source)
- assert str(source) == " raise ValueError"
-
- def test_finally(self):
- source = getstatement(3, self.source)
- assert str(source) == " raise IndexError(1)"
-
-
-
-class TestIf:
- pytestmark = astonly
- source = """\
-if 1:
- y = 3
-elif False:
- y = 5
-else:
- y = 7
-"""
-
- def test_body(self):
- source = getstatement(1, self.source)
- assert str(source) == " y = 3"
-
- def test_elif_clause(self):
- source = getstatement(2, self.source)
- assert str(source) == "elif False:"
-
- def test_elif(self):
- source = getstatement(3, self.source)
- assert str(source) == " y = 5"
-
- def test_else(self):
- source = getstatement(5, self.source)
- assert str(source) == " y = 7"
-
-def test_semicolon():
- s = """\
-hello ; pytest.skip()
-"""
- source = getstatement(0, s)
- assert str(source) == s.strip()
-
-def test_def_online():
- s = """\
-def func(): raise ValueError(42)
-
-def something():
- pass
-"""
- source = getstatement(0, s)
- assert str(source) == "def func(): raise ValueError(42)"
-
-def XXX_test_expression_multiline():
- source = """\
-something
-'''
-'''"""
- result = getstatement(1, source)
- assert str(result) == "'''\n'''"
-
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/io_/test_capture.py b/tests/wpt/web-platform-tests/tools/py/testing/io_/test_capture.py
deleted file mode 100644
index 5745e12a1a1..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/io_/test_capture.py
+++ /dev/null
@@ -1,501 +0,0 @@
-from __future__ import with_statement
-
-import os, sys
-import py
-
-needsdup = py.test.mark.skipif("not hasattr(os, 'dup')")
-
-from py.builtin import print_
-
-if sys.version_info >= (3,0):
- def tobytes(obj):
- if isinstance(obj, str):
- obj = obj.encode('UTF-8')
- assert isinstance(obj, bytes)
- return obj
- def totext(obj):
- if isinstance(obj, bytes):
- obj = str(obj, 'UTF-8')
- assert isinstance(obj, str)
- return obj
-else:
- def tobytes(obj):
- if isinstance(obj, unicode):
- obj = obj.encode('UTF-8')
- assert isinstance(obj, str)
- return obj
- def totext(obj):
- if isinstance(obj, str):
- obj = unicode(obj, 'UTF-8')
- assert isinstance(obj, unicode)
- return obj
-
-def oswritebytes(fd, obj):
- os.write(fd, tobytes(obj))
-
-class TestTextIO:
- def test_text(self):
- f = py.io.TextIO()
- f.write("hello")
- s = f.getvalue()
- assert s == "hello"
- f.close()
-
- def test_unicode_and_str_mixture(self):
- f = py.io.TextIO()
- if sys.version_info >= (3,0):
- f.write("\u00f6")
- py.test.raises(TypeError, "f.write(bytes('hello', 'UTF-8'))")
- else:
- f.write(unicode("\u00f6", 'UTF-8'))
- f.write("hello") # bytes
- s = f.getvalue()
- f.close()
- assert isinstance(s, unicode)
-
-def test_bytes_io():
- f = py.io.BytesIO()
- f.write(tobytes("hello"))
- py.test.raises(TypeError, "f.write(totext('hello'))")
- s = f.getvalue()
- assert s == tobytes("hello")
-
-def test_dontreadfrominput():
- from py._io.capture import DontReadFromInput
- f = DontReadFromInput()
- assert not f.isatty()
- py.test.raises(IOError, f.read)
- py.test.raises(IOError, f.readlines)
- py.test.raises(IOError, iter, f)
- py.test.raises(ValueError, f.fileno)
- f.close() # just for completeness
-
-def pytest_funcarg__tmpfile(request):
- testdir = request.getfuncargvalue("testdir")
- f = testdir.makepyfile("").open('wb+')
- request.addfinalizer(f.close)
- return f
-
-@needsdup
-def test_dupfile(tmpfile):
- flist = []
- for i in range(5):
- nf = py.io.dupfile(tmpfile, encoding="utf-8")
- assert nf != tmpfile
- assert nf.fileno() != tmpfile.fileno()
- assert nf not in flist
- print_(i, end="", file=nf)
- flist.append(nf)
- for i in range(5):
- f = flist[i]
- f.close()
- tmpfile.seek(0)
- s = tmpfile.read()
- assert "01234" in repr(s)
- tmpfile.close()
-
-def test_dupfile_no_mode():
- """
- dupfile should trap an AttributeError and return f if no mode is supplied.
- """
- class SomeFileWrapper(object):
- "An object with a fileno method but no mode attribute"
- def fileno(self):
- return 1
- tmpfile = SomeFileWrapper()
- assert py.io.dupfile(tmpfile) is tmpfile
- with py.test.raises(AttributeError):
- py.io.dupfile(tmpfile, raising=True)
-
-def lsof_check(func):
- pid = os.getpid()
- try:
- out = py.process.cmdexec("lsof -p %d" % pid)
- except py.process.cmdexec.Error:
- py.test.skip("could not run 'lsof'")
- func()
- out2 = py.process.cmdexec("lsof -p %d" % pid)
- len1 = len([x for x in out.split("\n") if "REG" in x])
- len2 = len([x for x in out2.split("\n") if "REG" in x])
- assert len2 < len1 + 3, out2
-
-class TestFDCapture:
- pytestmark = needsdup
-
- def test_not_now(self, tmpfile):
- fd = tmpfile.fileno()
- cap = py.io.FDCapture(fd, now=False)
- data = tobytes("hello")
- os.write(fd, data)
- f = cap.done()
- s = f.read()
- assert not s
- cap = py.io.FDCapture(fd, now=False)
- cap.start()
- os.write(fd, data)
- f = cap.done()
- s = f.read()
- assert s == "hello"
-
- def test_simple(self, tmpfile):
- fd = tmpfile.fileno()
- cap = py.io.FDCapture(fd)
- data = tobytes("hello")
- os.write(fd, data)
- f = cap.done()
- s = f.read()
- assert s == "hello"
- f.close()
-
- def test_simple_many(self, tmpfile):
- for i in range(10):
- self.test_simple(tmpfile)
-
- def test_simple_many_check_open_files(self, tmpfile):
- lsof_check(lambda: self.test_simple_many(tmpfile))
-
- def test_simple_fail_second_start(self, tmpfile):
- fd = tmpfile.fileno()
- cap = py.io.FDCapture(fd)
- f = cap.done()
- py.test.raises(ValueError, cap.start)
- f.close()
-
- def test_stderr(self):
- cap = py.io.FDCapture(2, patchsys=True)
- print_("hello", file=sys.stderr)
- f = cap.done()
- s = f.read()
- assert s == "hello\n"
-
- def test_stdin(self, tmpfile):
- tmpfile.write(tobytes("3"))
- tmpfile.seek(0)
- cap = py.io.FDCapture(0, tmpfile=tmpfile)
- # check with os.read() directly instead of raw_input(), because
- # sys.stdin itself may be redirected (as py.test now does by default)
- x = os.read(0, 100).strip()
- f = cap.done()
- assert x == tobytes("3")
-
- def test_writeorg(self, tmpfile):
- data1, data2 = tobytes("foo"), tobytes("bar")
- try:
- cap = py.io.FDCapture(tmpfile.fileno())
- tmpfile.write(data1)
- cap.writeorg(data2)
- finally:
- tmpfile.close()
- f = cap.done()
- scap = f.read()
- assert scap == totext(data1)
- stmp = open(tmpfile.name, 'rb').read()
- assert stmp == data2
-
-
-class TestStdCapture:
- def getcapture(self, **kw):
- return py.io.StdCapture(**kw)
-
- def test_capturing_done_simple(self):
- cap = self.getcapture()
- sys.stdout.write("hello")
- sys.stderr.write("world")
- outfile, errfile = cap.done()
- s = outfile.read()
- assert s == "hello"
- s = errfile.read()
- assert s == "world"
-
- def test_capturing_reset_simple(self):
- cap = self.getcapture()
- print("hello world")
- sys.stderr.write("hello error\n")
- out, err = cap.reset()
- assert out == "hello world\n"
- assert err == "hello error\n"
-
- def test_capturing_readouterr(self):
- cap = self.getcapture()
- try:
- print ("hello world")
- sys.stderr.write("hello error\n")
- out, err = cap.readouterr()
- assert out == "hello world\n"
- assert err == "hello error\n"
- sys.stderr.write("error2")
- finally:
- out, err = cap.reset()
- assert err == "error2"
-
- def test_capturing_readouterr_unicode(self):
- cap = self.getcapture()
- print ("hx\xc4\x85\xc4\x87")
- out, err = cap.readouterr()
- assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8")
-
- @py.test.mark.skipif('sys.version_info >= (3,)',
- reason='text output different for bytes on python3')
- def test_capturing_readouterr_decode_error_handling(self):
- cap = self.getcapture()
- # triggered a internal error in pytest
- print('\xa6')
- out, err = cap.readouterr()
- assert out == py.builtin._totext('\ufffd\n', 'unicode-escape')
-
- def test_capturing_mixed(self):
- cap = self.getcapture(mixed=True)
- sys.stdout.write("hello ")
- sys.stderr.write("world")
- sys.stdout.write(".")
- out, err = cap.reset()
- assert out.strip() == "hello world."
- assert not err
-
- def test_reset_twice_error(self):
- cap = self.getcapture()
- print ("hello")
- out, err = cap.reset()
- py.test.raises(ValueError, cap.reset)
- assert out == "hello\n"
- assert not err
-
- def test_capturing_modify_sysouterr_in_between(self):
- oldout = sys.stdout
- olderr = sys.stderr
- cap = self.getcapture()
- sys.stdout.write("hello")
- sys.stderr.write("world")
- sys.stdout = py.io.TextIO()
- sys.stderr = py.io.TextIO()
- print ("not seen")
- sys.stderr.write("not seen\n")
- out, err = cap.reset()
- assert out == "hello"
- assert err == "world"
- assert sys.stdout == oldout
- assert sys.stderr == olderr
-
- def test_capturing_error_recursive(self):
- cap1 = self.getcapture()
- print ("cap1")
- cap2 = self.getcapture()
- print ("cap2")
- out2, err2 = cap2.reset()
- out1, err1 = cap1.reset()
- assert out1 == "cap1\n"
- assert out2 == "cap2\n"
-
- def test_just_out_capture(self):
- cap = self.getcapture(out=True, err=False)
- sys.stdout.write("hello")
- sys.stderr.write("world")
- out, err = cap.reset()
- assert out == "hello"
- assert not err
-
- def test_just_err_capture(self):
- cap = self.getcapture(out=False, err=True)
- sys.stdout.write("hello")
- sys.stderr.write("world")
- out, err = cap.reset()
- assert err == "world"
- assert not out
-
- def test_stdin_restored(self):
- old = sys.stdin
- cap = self.getcapture(in_=True)
- newstdin = sys.stdin
- out, err = cap.reset()
- assert newstdin != sys.stdin
- assert sys.stdin is old
-
- def test_stdin_nulled_by_default(self):
- print ("XXX this test may well hang instead of crashing")
- print ("XXX which indicates an error in the underlying capturing")
- print ("XXX mechanisms")
- cap = self.getcapture()
- py.test.raises(IOError, "sys.stdin.read()")
- out, err = cap.reset()
-
- def test_suspend_resume(self):
- cap = self.getcapture(out=True, err=False, in_=False)
- try:
- print ("hello")
- sys.stderr.write("error\n")
- out, err = cap.suspend()
- assert out == "hello\n"
- assert not err
- print ("in between")
- sys.stderr.write("in between\n")
- cap.resume()
- print ("after")
- sys.stderr.write("error_after\n")
- finally:
- out, err = cap.reset()
- assert out == "after\n"
- assert not err
-
-class TestStdCaptureNotNow(TestStdCapture):
- def getcapture(self, **kw):
- kw['now'] = False
- cap = py.io.StdCapture(**kw)
- cap.startall()
- return cap
-
-class TestStdCaptureFD(TestStdCapture):
- pytestmark = needsdup
-
- def getcapture(self, **kw):
- return py.io.StdCaptureFD(**kw)
-
- def test_intermingling(self):
- cap = self.getcapture()
- oswritebytes(1, "1")
- sys.stdout.write(str(2))
- sys.stdout.flush()
- oswritebytes(1, "3")
- oswritebytes(2, "a")
- sys.stderr.write("b")
- sys.stderr.flush()
- oswritebytes(2, "c")
- out, err = cap.reset()
- assert out == "123"
- assert err == "abc"
-
- def test_callcapture(self):
- def func(x, y):
- print (x)
- py.std.sys.stderr.write(str(y))
- return 42
-
- res, out, err = py.io.StdCaptureFD.call(func, 3, y=4)
- assert res == 42
- assert out.startswith("3")
- assert err.startswith("4")
-
- def test_many(self, capfd):
- def f():
- for i in range(10):
- cap = py.io.StdCaptureFD()
- cap.reset()
- lsof_check(f)
-
-class TestStdCaptureFDNotNow(TestStdCaptureFD):
- pytestmark = needsdup
-
- def getcapture(self, **kw):
- kw['now'] = False
- cap = py.io.StdCaptureFD(**kw)
- cap.startall()
- return cap
-
-@needsdup
-def test_stdcapture_fd_tmpfile(tmpfile):
- capfd = py.io.StdCaptureFD(out=tmpfile)
- os.write(1, "hello".encode("ascii"))
- os.write(2, "world".encode("ascii"))
- outf, errf = capfd.done()
- assert outf == tmpfile
-
-class TestStdCaptureFDinvalidFD:
- pytestmark = needsdup
- def test_stdcapture_fd_invalid_fd(self, testdir):
- testdir.makepyfile("""
- import py, os
- def test_stdout():
- os.close(1)
- cap = py.io.StdCaptureFD(out=True, err=False, in_=False)
- cap.done()
- def test_stderr():
- os.close(2)
- cap = py.io.StdCaptureFD(out=False, err=True, in_=False)
- cap.done()
- def test_stdin():
- os.close(0)
- cap = py.io.StdCaptureFD(out=False, err=False, in_=True)
- cap.done()
- """)
- result = testdir.runpytest("--capture=fd")
- assert result.ret == 0
- assert result.parseoutcomes()['passed'] == 3
-
-def test_capture_not_started_but_reset():
- capsys = py.io.StdCapture(now=False)
- capsys.done()
- capsys.done()
- capsys.reset()
-
-@needsdup
-def test_capture_no_sys():
- capsys = py.io.StdCapture()
- try:
- cap = py.io.StdCaptureFD(patchsys=False)
- sys.stdout.write("hello")
- sys.stderr.write("world")
- oswritebytes(1, "1")
- oswritebytes(2, "2")
- out, err = cap.reset()
- assert out == "1"
- assert err == "2"
- finally:
- capsys.reset()
-
-@needsdup
-def test_callcapture_nofd():
- def func(x, y):
- oswritebytes(1, "hello")
- oswritebytes(2, "hello")
- print (x)
- sys.stderr.write(str(y))
- return 42
-
- capfd = py.io.StdCaptureFD(patchsys=False)
- try:
- res, out, err = py.io.StdCapture.call(func, 3, y=4)
- finally:
- capfd.reset()
- assert res == 42
- assert out.startswith("3")
- assert err.startswith("4")
-
-@needsdup
-@py.test.mark.multi(use=[True, False])
-def test_fdcapture_tmpfile_remains_the_same(tmpfile, use):
- if not use:
- tmpfile = True
- cap = py.io.StdCaptureFD(out=False, err=tmpfile, now=False)
- cap.startall()
- capfile = cap.err.tmpfile
- cap.suspend()
- cap.resume()
- capfile2 = cap.err.tmpfile
- assert capfile2 == capfile
-
-@py.test.mark.multi(method=['StdCapture', 'StdCaptureFD'])
-def test_capturing_and_logging_fundamentals(testdir, method):
- if method == "StdCaptureFD" and not hasattr(os, 'dup'):
- py.test.skip("need os.dup")
- # here we check a fundamental feature
- p = testdir.makepyfile("""
- import sys, os
- import py, logging
- cap = py.io.%s(out=False, in_=False)
-
- logging.warn("hello1")
- outerr = cap.suspend()
- print ("suspend, captured %%s" %%(outerr,))
- logging.warn("hello2")
-
- cap.resume()
- logging.warn("hello3")
-
- outerr = cap.suspend()
- print ("suspend2, captured %%s" %% (outerr,))
- """ % (method,))
- result = testdir.runpython(p)
- result.stdout.fnmatch_lines([
- "suspend, captured*hello1*",
- "suspend2, captured*hello2*WARNING:root:hello3*",
- ])
- assert "atexit" not in result.stderr.str()
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/io_/test_saferepr.py b/tests/wpt/web-platform-tests/tools/py/testing/io_/test_saferepr.py
deleted file mode 100644
index 1ed9c4faf62..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/io_/test_saferepr.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import generators
-import py
-import sys
-
-saferepr = py.io.saferepr
-
-class TestSafeRepr:
- def test_simple_repr(self):
- assert saferepr(1) == '1'
- assert saferepr(None) == 'None'
-
- def test_maxsize(self):
- s = saferepr('x'*50, maxsize=25)
- assert len(s) == 25
- expected = repr('x'*10 + '...' + 'x'*10)
- assert s == expected
-
- def test_maxsize_error_on_instance(self):
- class A:
- def __repr__(self):
- raise ValueError('...')
-
- s = saferepr(('*'*50, A()), maxsize=25)
- assert len(s) == 25
- assert s[0] == '(' and s[-1] == ')'
-
- def test_exceptions(self):
- class BrokenRepr:
- def __init__(self, ex):
- self.ex = ex
- foo = 0
- def __repr__(self):
- raise self.ex
- class BrokenReprException(Exception):
- __str__ = None
- __repr__ = None
- assert 'Exception' in saferepr(BrokenRepr(Exception("broken")))
- s = saferepr(BrokenReprException("really broken"))
- assert 'TypeError' in s
- if py.std.sys.version_info < (2,6):
- assert 'unknown' in saferepr(BrokenRepr("string"))
- else:
- assert 'TypeError' in saferepr(BrokenRepr("string"))
-
- s2 = saferepr(BrokenRepr(BrokenReprException('omg even worse')))
- assert 'NameError' not in s2
- assert 'unknown' in s2
-
- def test_big_repr(self):
- from py._io.saferepr import SafeRepr
- assert len(saferepr(range(1000))) <= \
- len('[' + SafeRepr().maxlist * "1000" + ']')
-
- def test_repr_on_newstyle(self):
- class Function(object):
- def __repr__(self):
- return "<%s>" %(self.name)
- try:
- s = saferepr(Function())
- except Exception:
- py.test.fail("saferepr failed for newstyle class")
-
- def test_unicode(self):
- val = py.builtin._totext('£€', 'utf-8')
- reprval = py.builtin._totext("'£€'", 'utf-8')
- assert saferepr(val) == reprval
-
-def test_unicode_handling():
- value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
- def f():
- raise Exception(value)
- excinfo = py.test.raises(Exception, f)
- s = str(excinfo)
- if sys.version_info[0] < 3:
- u = unicode(excinfo)
-
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/io_/test_terminalwriter.py b/tests/wpt/web-platform-tests/tools/py/testing/io_/test_terminalwriter.py
deleted file mode 100644
index 0a15541bd23..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/io_/test_terminalwriter.py
+++ /dev/null
@@ -1,271 +0,0 @@
-
-import py
-import os, sys
-from py._io import terminalwriter
-import codecs
-import pytest
-
-def test_get_terminal_width():
- x = py.io.get_terminal_width
- assert x == terminalwriter.get_terminal_width
-
-def test_getdimensions(monkeypatch):
- fcntl = py.test.importorskip("fcntl")
- import struct
- l = []
- monkeypatch.setattr(fcntl, 'ioctl', lambda *args: l.append(args))
- try:
- terminalwriter._getdimensions()
- except (TypeError, struct.error):
- pass
- assert len(l) == 1
- assert l[0][0] == 1
-
-def test_terminal_width_COLUMNS(monkeypatch):
- """ Dummy test for get_terminal_width
- """
- fcntl = py.test.importorskip("fcntl")
- monkeypatch.setattr(fcntl, 'ioctl', lambda *args: int('x'))
- monkeypatch.setenv('COLUMNS', '42')
- assert terminalwriter.get_terminal_width() == 42
- monkeypatch.delenv('COLUMNS', raising=False)
-
-def test_terminalwriter_defaultwidth_80(monkeypatch):
- monkeypatch.setattr(terminalwriter, '_getdimensions', lambda: 0/0)
- monkeypatch.delenv('COLUMNS', raising=False)
- tw = py.io.TerminalWriter()
- assert tw.fullwidth == 80
-
-def test_terminalwriter_getdimensions_bogus(monkeypatch):
- monkeypatch.setattr(terminalwriter, '_getdimensions', lambda: (10,10))
- monkeypatch.delenv('COLUMNS', raising=False)
- tw = py.io.TerminalWriter()
- assert tw.fullwidth == 80
-
-def test_terminalwriter_getdimensions_emacs(monkeypatch):
- # emacs terminal returns (0,0) but set COLUMNS properly
- monkeypatch.setattr(terminalwriter, '_getdimensions', lambda: (0,0))
- monkeypatch.setenv('COLUMNS', '42')
- tw = py.io.TerminalWriter()
- assert tw.fullwidth == 42
-
-def test_terminalwriter_computes_width(monkeypatch):
- monkeypatch.setattr(terminalwriter, 'get_terminal_width', lambda: 42)
- tw = py.io.TerminalWriter()
- assert tw.fullwidth == 42
-
-def test_terminalwriter_default_instantiation():
- tw = py.io.TerminalWriter(stringio=True)
- assert hasattr(tw, 'stringio')
-
-def test_terminalwriter_dumb_term_no_markup(monkeypatch):
- monkeypatch.setattr(os, 'environ', {'TERM': 'dumb', 'PATH': ''})
- class MyFile:
- closed = False
- def isatty(self):
- return True
- monkeypatch.setattr(sys, 'stdout', MyFile())
- try:
- assert sys.stdout.isatty()
- tw = py.io.TerminalWriter()
- assert not tw.hasmarkup
- finally:
- monkeypatch.undo()
-
-def test_terminalwriter_file_unicode(tmpdir):
- f = py.std.codecs.open(str(tmpdir.join("xyz")), "wb", "utf8")
- tw = py.io.TerminalWriter(file=f)
- assert tw.encoding == "utf8"
-
-def test_unicode_encoding():
- msg = py.builtin._totext('b\u00f6y', 'utf8')
- for encoding in 'utf8', 'latin1':
- l = []
- tw = py.io.TerminalWriter(l.append, encoding=encoding)
- tw.line(msg)
- assert l[0].strip() == msg.encode(encoding)
-
-@pytest.mark.parametrize("encoding", ["ascii"])
-def test_unicode_on_file_with_ascii_encoding(tmpdir, monkeypatch, encoding):
- msg = py.builtin._totext('hell\xf6', "latin1")
- #pytest.raises(UnicodeEncodeError, lambda: bytes(msg))
- f = py.std.codecs.open(str(tmpdir.join("x")), "w", encoding)
- tw = py.io.TerminalWriter(f)
- tw.line(msg)
- f.close()
- s = tmpdir.join("x").open("rb").read().strip()
- assert encoding == "ascii"
- assert s == msg.encode("unicode-escape")
-
-
-win32 = int(sys.platform == "win32")
-class TestTerminalWriter:
- def pytest_generate_tests(self, metafunc):
- if "tw" in metafunc.funcargnames:
- metafunc.addcall(id="path", param="path")
- metafunc.addcall(id="stringio", param="stringio")
- metafunc.addcall(id="callable", param="callable")
- def pytest_funcarg__tw(self, request):
- if request.param == "path":
- tmpdir = request.getfuncargvalue("tmpdir")
- p = tmpdir.join("tmpfile")
- f = codecs.open(str(p), 'w+', encoding='utf8')
- tw = py.io.TerminalWriter(f)
- def getlines():
- tw._file.flush()
- return codecs.open(str(p), 'r',
- encoding='utf8').readlines()
- elif request.param == "stringio":
- tw = py.io.TerminalWriter(stringio=True)
- def getlines():
- tw.stringio.seek(0)
- return tw.stringio.readlines()
- elif request.param == "callable":
- writes = []
- tw = py.io.TerminalWriter(writes.append)
- def getlines():
- io = py.io.TextIO()
- io.write("".join(writes))
- io.seek(0)
- return io.readlines()
- tw.getlines = getlines
- tw.getvalue = lambda: "".join(getlines())
- return tw
-
- def test_line(self, tw):
- tw.line("hello")
- l = tw.getlines()
- assert len(l) == 1
- assert l[0] == "hello\n"
-
- def test_line_unicode(self, tw):
- for encoding in 'utf8', 'latin1':
- tw._encoding = encoding
- msg = py.builtin._totext('b\u00f6y', 'utf8')
- tw.line(msg)
- l = tw.getlines()
- assert l[0] == msg + "\n"
-
- def test_sep_no_title(self, tw):
- tw.sep("-", fullwidth=60)
- l = tw.getlines()
- assert len(l) == 1
- assert l[0] == "-" * (60-win32) + "\n"
-
- def test_sep_with_title(self, tw):
- tw.sep("-", "hello", fullwidth=60)
- l = tw.getlines()
- assert len(l) == 1
- assert l[0] == "-" * 26 + " hello " + "-" * (27-win32) + "\n"
-
- @py.test.mark.skipif("sys.platform == 'win32'")
- def test__escaped(self, tw):
- text2 = tw._escaped("hello", (31))
- assert text2.find("hello") != -1
-
- @py.test.mark.skipif("sys.platform == 'win32'")
- def test_markup(self, tw):
- for bold in (True, False):
- for color in ("red", "green"):
- text2 = tw.markup("hello", **{color: True, 'bold': bold})
- assert text2.find("hello") != -1
- py.test.raises(ValueError, "tw.markup('x', wronkw=3)")
- py.test.raises(ValueError, "tw.markup('x', wronkw=0)")
-
- def test_line_write_markup(self, tw):
- tw.hasmarkup = True
- tw.line("x", bold=True)
- tw.write("x\n", red=True)
- l = tw.getlines()
- if sys.platform != "win32":
- assert len(l[0]) >= 2, l
- assert len(l[1]) >= 2, l
-
- def test_attr_fullwidth(self, tw):
- tw.sep("-", "hello", fullwidth=70)
- tw.fullwidth = 70
- tw.sep("-", "hello")
- l = tw.getlines()
- assert len(l[0]) == len(l[1])
-
- def test_reline(self, tw):
- tw.line("hello")
- tw.hasmarkup = False
- pytest.raises(ValueError, lambda: tw.reline("x"))
- tw.hasmarkup = True
- tw.reline("0 1 2")
- tw.getlines()
- l = tw.getvalue().split("\n")
- assert len(l) == 2
- tw.reline("0 1 3")
- l = tw.getvalue().split("\n")
- assert len(l) == 2
- assert l[1].endswith("0 1 3\r")
- tw.line("so")
- l = tw.getvalue().split("\n")
- assert len(l) == 3
- assert l[-1] == ""
- assert l[1] == ("0 1 2\r0 1 3\rso ")
- assert l[0] == "hello"
-
-
-def test_terminal_with_callable_write_and_flush():
- l = set()
- class fil:
- flush = lambda self: l.add("1")
- write = lambda self, x: l.add("1")
- __call__ = lambda self, x: l.add("2")
-
- tw = py.io.TerminalWriter(fil())
- tw.line("hello")
- assert l == set(["1"])
- del fil.flush
- l.clear()
- tw = py.io.TerminalWriter(fil())
- tw.line("hello")
- assert l == set(["2"])
-
-
-@pytest.mark.skipif(sys.platform == "win32", reason="win32 has no native ansi")
-def test_attr_hasmarkup():
- tw = py.io.TerminalWriter(stringio=True)
- assert not tw.hasmarkup
- tw.hasmarkup = True
- tw.line("hello", bold=True)
- s = tw.stringio.getvalue()
- assert len(s) > len("hello\n")
- assert '\x1b[1m' in s
- assert '\x1b[0m' in s
-
-@pytest.mark.skipif(sys.platform == "win32", reason="win32 has no native ansi")
-def test_ansi_print():
- # we have no easy way to construct a file that
- # represents a terminal
- f = py.io.TextIO()
- f.isatty = lambda: True
- py.io.ansi_print("hello", 0x32, file=f)
- text2 = f.getvalue()
- assert text2.find("hello") != -1
- assert len(text2) >= len("hello\n")
- assert '\x1b[50m' in text2
- assert '\x1b[0m' in text2
-
-def test_should_do_markup_PY_COLORS_eq_1(monkeypatch):
- monkeypatch.setitem(os.environ, 'PY_COLORS', '1')
- tw = py.io.TerminalWriter(stringio=True)
- assert tw.hasmarkup
- tw.line("hello", bold=True)
- s = tw.stringio.getvalue()
- assert len(s) > len("hello\n")
- assert '\x1b[1m' in s
- assert '\x1b[0m' in s
-
-def test_should_do_markup_PY_COLORS_eq_0(monkeypatch):
- monkeypatch.setitem(os.environ, 'PY_COLORS', '0')
- f = py.io.TextIO()
- f.isatty = lambda: True
- tw = py.io.TerminalWriter(file=f)
- assert not tw.hasmarkup
- tw.line("hello", bold=True)
- s = f.getvalue()
- assert s == "hello\n"
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/log/test_log.py b/tests/wpt/web-platform-tests/tools/py/testing/log/test_log.py
deleted file mode 100644
index b41bc3a5821..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/log/test_log.py
+++ /dev/null
@@ -1,190 +0,0 @@
-import py
-import sys
-
-from py._log.log import default_keywordmapper
-
-callcapture = py.io.StdCapture.call
-
-def setup_module(mod):
- mod._oldstate = default_keywordmapper.getstate()
-
-def teardown_module(mod):
- default_keywordmapper.setstate(mod._oldstate)
-
-class TestLogProducer:
- def setup_method(self, meth):
- default_keywordmapper.setstate(_oldstate)
-
- def test_getstate_setstate(self):
- state = py.log._getstate()
- py.log.setconsumer("hello", [].append)
- state2 = py.log._getstate()
- assert state2 != state
- py.log._setstate(state)
- state3 = py.log._getstate()
- assert state3 == state
-
- def test_producer_repr(self):
- d = py.log.Producer("default")
- assert repr(d).find('default') != -1
-
- def test_produce_one_keyword(self):
- l = []
- py.log.setconsumer('s1', l.append)
- py.log.Producer('s1')("hello world")
- assert len(l) == 1
- msg = l[0]
- assert msg.content().startswith('hello world')
- assert msg.prefix() == '[s1] '
- assert str(msg) == "[s1] hello world"
-
- def test_producer_class(self):
- p = py.log.Producer('x1')
- l = []
- py.log.setconsumer(p._keywords, l.append)
- p("hello")
- assert len(l) == 1
- assert len(l[0].keywords) == 1
- assert 'x1' == l[0].keywords[0]
-
- def test_producer_caching(self):
- p = py.log.Producer('x1')
- x2 = p.x2
- assert x2 is p.x2
-
-class TestLogConsumer:
- def setup_method(self, meth):
- default_keywordmapper.setstate(_oldstate)
- def test_log_none(self):
- log = py.log.Producer("XXX")
- l = []
- py.log.setconsumer('XXX', l.append)
- log("1")
- assert l
- l[:] = []
- py.log.setconsumer('XXX', None)
- log("2")
- assert not l
-
- def test_log_default_stderr(self):
- res, out, err = callcapture(py.log.Producer("default"), "hello")
- assert err.strip() == "[default] hello"
-
- def test_simple_consumer_match(self):
- l = []
- py.log.setconsumer("x1", l.append)
- p = py.log.Producer("x1 x2")
- p("hello")
- assert l
- assert l[0].content() == "hello"
-
- def test_simple_consumer_match_2(self):
- l = []
- p = py.log.Producer("x1 x2")
- py.log.setconsumer(p._keywords, l.append)
- p("42")
- assert l
- assert l[0].content() == "42"
-
- def test_no_auto_producer(self):
- p = py.log.Producer('x')
- py.test.raises(AttributeError, "p._x")
- py.test.raises(AttributeError, "p.x_y")
-
- def test_setconsumer_with_producer(self):
- l = []
- p = py.log.Producer("hello")
- py.log.setconsumer(p, l.append)
- p("world")
- assert str(l[0]) == "[hello] world"
-
- def test_multi_consumer(self):
- l = []
- py.log.setconsumer("x1", l.append)
- py.log.setconsumer("x1 x2", None)
- p = py.log.Producer("x1 x2")
- p("hello")
- assert not l
- py.log.Producer("x1")("hello")
- assert l
- assert l[0].content() == "hello"
-
- def test_log_stderr(self):
- py.log.setconsumer("xyz", py.log.STDOUT)
- res, out, err = callcapture(py.log.Producer("xyz"), "hello")
- assert not err
- assert out.strip() == '[xyz] hello'
-
- def test_log_file(self, tmpdir):
- customlog = tmpdir.join('log.out')
- py.log.setconsumer("default", open(str(customlog), 'w', 1))
- py.log.Producer("default")("hello world #1")
- assert customlog.readlines() == ['[default] hello world #1\n']
-
- py.log.setconsumer("default", py.log.Path(customlog, buffering=False))
- py.log.Producer("default")("hello world #2")
- res = customlog.readlines()
- assert res == ['[default] hello world #2\n'] # no append by default!
-
- def test_log_file_append_mode(self, tmpdir):
- logfilefn = tmpdir.join('log_append.out')
-
- # The append mode is on by default, so we don't need to specify it for File
- py.log.setconsumer("default", py.log.Path(logfilefn, append=True,
- buffering=0))
- assert logfilefn.check()
- py.log.Producer("default")("hello world #1")
- lines = logfilefn.readlines()
- assert lines == ['[default] hello world #1\n']
- py.log.setconsumer("default", py.log.Path(logfilefn, append=True,
- buffering=0))
- py.log.Producer("default")("hello world #1")
- lines = logfilefn.readlines()
- assert lines == ['[default] hello world #1\n',
- '[default] hello world #1\n']
-
- def test_log_file_delayed_create(self, tmpdir):
- logfilefn = tmpdir.join('log_create.out')
-
- py.log.setconsumer("default", py.log.Path(logfilefn,
- delayed_create=True, buffering=0))
- assert not logfilefn.check()
- py.log.Producer("default")("hello world #1")
- lines = logfilefn.readlines()
- assert lines == ['[default] hello world #1\n']
-
- def test_keyword_based_log_files(self, tmpdir):
- logfiles = []
- keywords = 'k1 k2 k3'.split()
- for key in keywords:
- path = tmpdir.join(key)
- py.log.setconsumer(key, py.log.Path(path, buffering=0))
-
- py.log.Producer('k1')('1')
- py.log.Producer('k2')('2')
- py.log.Producer('k3')('3')
-
- for key in keywords:
- path = tmpdir.join(key)
- assert path.read().strip() == '[%s] %s' % (key, key[-1])
-
- # disabled for now; the syslog log file can usually be read only by root
- # I manually inspected /var/log/messages and the entries were there
- def no_test_log_syslog(self):
- py.log.setconsumer("default", py.log.Syslog())
- py.log.default("hello world #1")
-
- # disabled for now until I figure out how to read entries in the
- # Event Logs on Windows
- # I manually inspected the Application Log and the entries were there
- def no_test_log_winevent(self):
- py.log.setconsumer("default", py.log.WinEvent())
- py.log.default("hello world #1")
-
- # disabled for now until I figure out how to properly pass the parameters
- def no_test_log_email(self):
- py.log.setconsumer("default", py.log.Email(mailhost="gheorghiu.net",
- fromaddr="grig",
- toaddrs="grig",
- subject = "py.log email"))
- py.log.default("hello world #1")
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/log/test_warning.py b/tests/wpt/web-platform-tests/tools/py/testing/log/test_warning.py
deleted file mode 100644
index 8c89cf8adbb..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/log/test_warning.py
+++ /dev/null
@@ -1,76 +0,0 @@
-import pytest
-import py
-
-mypath = py.path.local(__file__).new(ext=".py")
-
-@pytest.mark.xfail
-def test_forwarding_to_warnings_module():
- pytest.deprecated_call(py.log._apiwarn, "1.3", "..")
-
-def test_apiwarn_functional(recwarn):
- capture = py.io.StdCapture()
- py.log._apiwarn("x.y.z", "something", stacklevel=1)
- out, err = capture.reset()
- py.builtin.print_("out", out)
- py.builtin.print_("err", err)
- assert err.find("x.y.z") != -1
- lno = py.code.getrawcode(test_apiwarn_functional).co_firstlineno + 2
- exp = "%s:%s" % (mypath, lno)
- assert err.find(exp) != -1
-
-def test_stacklevel(recwarn):
- def f():
- py.log._apiwarn("x", "some", stacklevel=2)
- # 3
- # 4
- capture = py.io.StdCapture()
- f()
- out, err = capture.reset()
- lno = py.code.getrawcode(test_stacklevel).co_firstlineno + 6
- warning = str(err)
- assert warning.find(":%s" % lno) != -1
-
-def test_stacklevel_initpkg_with_resolve(testdir, recwarn):
- testdir.makepyfile(modabc="""
- import py
- def f():
- py.log._apiwarn("x", "some", stacklevel="apipkg123")
- """)
- testdir.makepyfile(apipkg123="""
- def __getattr__():
- import modabc
- modabc.f()
- """)
- p = testdir.makepyfile("""
- import apipkg123
- apipkg123.__getattr__()
- """)
- capture = py.io.StdCapture()
- p.pyimport()
- out, err = capture.reset()
- warning = str(err)
- loc = 'test_stacklevel_initpkg_with_resolve.py:2'
- assert warning.find(loc) != -1
-
-def test_stacklevel_initpkg_no_resolve(recwarn):
- def f():
- py.log._apiwarn("x", "some", stacklevel="apipkg")
- capture = py.io.StdCapture()
- f()
- out, err = capture.reset()
- lno = py.code.getrawcode(test_stacklevel_initpkg_no_resolve).co_firstlineno + 2
- warning = str(err)
- assert warning.find(":%s" % lno) != -1
-
-
-def test_function(recwarn):
- capture = py.io.StdCapture()
- py.log._apiwarn("x.y.z", "something", function=test_function)
- out, err = capture.reset()
- py.builtin.print_("out", out)
- py.builtin.print_("err", err)
- assert err.find("x.y.z") != -1
- lno = py.code.getrawcode(test_function).co_firstlineno
- exp = "%s:%s" % (mypath, lno)
- assert err.find(exp) != -1
-
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/path/common.py b/tests/wpt/web-platform-tests/tools/py/testing/path/common.py
deleted file mode 100644
index 4834fba12d0..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/path/common.py
+++ /dev/null
@@ -1,470 +0,0 @@
-import py
-import sys
-
-class CommonFSTests(object):
- def test_constructor_equality(self, path1):
- p = path1.__class__(path1)
- assert p == path1
-
- def test_eq_nonstring(self, path1):
- p1 = path1.join('sampledir')
- p2 = path1.join('sampledir')
- assert p1 == p2
-
- def test_new_identical(self, path1):
- assert path1 == path1.new()
-
- def test_join(self, path1):
- p = path1.join('sampledir')
- strp = str(p)
- assert strp.endswith('sampledir')
- assert strp.startswith(str(path1))
-
- def test_join_normalized(self, path1):
- newpath = path1.join(path1.sep+'sampledir')
- strp = str(newpath)
- assert strp.endswith('sampledir')
- assert strp.startswith(str(path1))
- newpath = path1.join((path1.sep*2) + 'sampledir')
- strp = str(newpath)
- assert strp.endswith('sampledir')
- assert strp.startswith(str(path1))
-
- def test_join_noargs(self, path1):
- newpath = path1.join()
- assert path1 == newpath
-
- def test_add_something(self, path1):
- p = path1.join('sample')
- p = p + 'dir'
- assert p.check()
- assert p.exists()
- assert p.isdir()
- assert not p.isfile()
-
- def test_parts(self, path1):
- newpath = path1.join('sampledir', 'otherfile')
- par = newpath.parts()[-3:]
- assert par == [path1, path1.join('sampledir'), newpath]
-
- revpar = newpath.parts(reverse=True)[:3]
- assert revpar == [newpath, path1.join('sampledir'), path1]
-
- def test_common(self, path1):
- other = path1.join('sampledir')
- x = other.common(path1)
- assert x == path1
-
- #def test_parents_nonexisting_file(self, path1):
- # newpath = path1 / 'dirnoexist' / 'nonexisting file'
- # par = list(newpath.parents())
- # assert par[:2] == [path1 / 'dirnoexist', path1]
-
- def test_basename_checks(self, path1):
- newpath = path1.join('sampledir')
- assert newpath.check(basename='sampledir')
- assert newpath.check(notbasename='xyz')
- assert newpath.basename == 'sampledir'
-
- def test_basename(self, path1):
- newpath = path1.join('sampledir')
- assert newpath.check(basename='sampledir')
- assert newpath.basename, 'sampledir'
-
- def test_dirname(self, path1):
- newpath = path1.join('sampledir')
- assert newpath.dirname == str(path1)
-
- def test_dirpath(self, path1):
- newpath = path1.join('sampledir')
- assert newpath.dirpath() == path1
-
- def test_dirpath_with_args(self, path1):
- newpath = path1.join('sampledir')
- assert newpath.dirpath('x') == path1.join('x')
-
- def test_newbasename(self, path1):
- newpath = path1.join('samplefile')
- newbase = newpath.new(basename="samplefile2")
- assert newbase.basename == "samplefile2"
- assert newbase.dirpath() == newpath.dirpath()
-
- def test_not_exists(self, path1):
- assert not path1.join('does_not_exist').check()
- assert path1.join('does_not_exist').check(exists=0)
-
- def test_exists(self, path1):
- assert path1.join("samplefile").check()
- assert path1.join("samplefile").check(exists=1)
- assert path1.join("samplefile").exists()
- assert path1.join("samplefile").isfile()
- assert not path1.join("samplefile").isdir()
-
- def test_dir(self, path1):
- #print repr(path1.join("sampledir"))
- assert path1.join("sampledir").check(dir=1)
- assert path1.join('samplefile').check(notdir=1)
- assert not path1.join("samplefile").check(dir=1)
- assert path1.join("samplefile").exists()
- assert not path1.join("samplefile").isdir()
- assert path1.join("samplefile").isfile()
-
- def test_fnmatch_file(self, path1):
- assert path1.join("samplefile").check(fnmatch='s*e')
- assert path1.join("samplefile").fnmatch('s*e')
- assert not path1.join("samplefile").fnmatch('s*x')
- assert not path1.join("samplefile").check(fnmatch='s*x')
-
- #def test_fnmatch_dir(self, path1):
-
- # pattern = path1.sep.join(['s*file'])
- # sfile = path1.join("samplefile")
- # assert sfile.check(fnmatch=pattern)
-
- def test_relto(self, path1):
- l=path1.join("sampledir", "otherfile")
- assert l.relto(path1) == l.sep.join(["sampledir", "otherfile"])
- assert l.check(relto=path1)
- assert path1.check(notrelto=l)
- assert not path1.check(relto=l)
-
- def test_bestrelpath(self, path1):
- curdir = path1
- sep = curdir.sep
- s = curdir.bestrelpath(curdir)
- assert s == "."
- s = curdir.bestrelpath(curdir.join("hello", "world"))
- assert s == "hello" + sep + "world"
-
- s = curdir.bestrelpath(curdir.dirpath().join("sister"))
- assert s == ".." + sep + "sister"
- assert curdir.bestrelpath(curdir.dirpath()) == ".."
-
- assert curdir.bestrelpath("hello") == "hello"
-
- def test_relto_not_relative(self, path1):
- l1=path1.join("bcde")
- l2=path1.join("b")
- assert not l1.relto(l2)
- assert not l2.relto(l1)
-
- @py.test.mark.xfail("sys.platform.startswith('java')")
- def test_listdir(self, path1):
- l = path1.listdir()
- assert path1.join('sampledir') in l
- assert path1.join('samplefile') in l
- py.test.raises(py.error.ENOTDIR,
- "path1.join('samplefile').listdir()")
-
- def test_listdir_fnmatchstring(self, path1):
- l = path1.listdir('s*dir')
- assert len(l)
- assert l[0], path1.join('sampledir')
-
- def test_listdir_filter(self, path1):
- l = path1.listdir(lambda x: x.check(dir=1))
- assert path1.join('sampledir') in l
- assert not path1.join('samplefile') in l
-
- def test_listdir_sorted(self, path1):
- l = path1.listdir(lambda x: x.check(basestarts="sample"), sort=True)
- assert path1.join('sampledir') == l[0]
- assert path1.join('samplefile') == l[1]
- assert path1.join('samplepickle') == l[2]
-
- def test_visit_nofilter(self, path1):
- l = []
- for i in path1.visit():
- l.append(i.relto(path1))
- assert "sampledir" in l
- assert path1.sep.join(["sampledir", "otherfile"]) in l
-
- def test_visit_norecurse(self, path1):
- l = []
- for i in path1.visit(None, lambda x: x.basename != "sampledir"):
- l.append(i.relto(path1))
- assert "sampledir" in l
- assert not path1.sep.join(["sampledir", "otherfile"]) in l
-
- def test_visit_filterfunc_is_string(self, path1):
- l = []
- for i in path1.visit('*dir'):
- l.append(i.relto(path1))
- assert len(l), 2
- assert "sampledir" in l
- assert "otherdir" in l
-
- @py.test.mark.xfail("sys.platform.startswith('java')")
- def test_visit_ignore(self, path1):
- p = path1.join('nonexisting')
- assert list(p.visit(ignore=py.error.ENOENT)) == []
-
- def test_visit_endswith(self, path1):
- l = []
- for i in path1.visit(lambda x: x.check(endswith="file")):
- l.append(i.relto(path1))
- assert path1.sep.join(["sampledir", "otherfile"]) in l
- assert "samplefile" in l
-
- def test_endswith(self, path1):
- assert path1.check(notendswith='.py')
- x = path1.join('samplefile')
- assert x.check(endswith='file')
-
- def test_cmp(self, path1):
- path1 = path1.join('samplefile')
- path2 = path1.join('samplefile2')
- assert (path1 < path2) == ('samplefile' < 'samplefile2')
- assert not (path1 < path1)
-
- def test_simple_read(self, path1):
- x = path1.join('samplefile').read('r')
- assert x == 'samplefile\n'
-
- def test_join_div_operator(self, path1):
- newpath = path1 / '/sampledir' / '/test//'
- newpath2 = path1.join('sampledir', 'test')
- assert newpath == newpath2
-
- def test_ext(self, path1):
- newpath = path1.join('sampledir.ext')
- assert newpath.ext == '.ext'
- newpath = path1.join('sampledir')
- assert not newpath.ext
-
- def test_purebasename(self, path1):
- newpath = path1.join('samplefile.py')
- assert newpath.purebasename == 'samplefile'
-
- def test_multiple_parts(self, path1):
- newpath = path1.join('samplefile.py')
- dirname, purebasename, basename, ext = newpath._getbyspec(
- 'dirname,purebasename,basename,ext')
- assert str(path1).endswith(dirname) # be careful with win32 'drive'
- assert purebasename == 'samplefile'
- assert basename == 'samplefile.py'
- assert ext == '.py'
-
- def test_dotted_name_ext(self, path1):
- newpath = path1.join('a.b.c')
- ext = newpath.ext
- assert ext == '.c'
- assert newpath.ext == '.c'
-
- def test_newext(self, path1):
- newpath = path1.join('samplefile.py')
- newext = newpath.new(ext='.txt')
- assert newext.basename == "samplefile.txt"
- assert newext.purebasename == "samplefile"
-
- def test_readlines(self, path1):
- fn = path1.join('samplefile')
- contents = fn.readlines()
- assert contents == ['samplefile\n']
-
- def test_readlines_nocr(self, path1):
- fn = path1.join('samplefile')
- contents = fn.readlines(cr=0)
- assert contents == ['samplefile', '']
-
- def test_file(self, path1):
- assert path1.join('samplefile').check(file=1)
-
- def test_not_file(self, path1):
- assert not path1.join("sampledir").check(file=1)
- assert path1.join("sampledir").check(file=0)
-
- def test_non_existent(self, path1):
- assert path1.join("sampledir.nothere").check(dir=0)
- assert path1.join("sampledir.nothere").check(file=0)
- assert path1.join("sampledir.nothere").check(notfile=1)
- assert path1.join("sampledir.nothere").check(notdir=1)
- assert path1.join("sampledir.nothere").check(notexists=1)
- assert not path1.join("sampledir.nothere").check(notfile=0)
-
- # pattern = path1.sep.join(['s*file'])
- # sfile = path1.join("samplefile")
- # assert sfile.check(fnmatch=pattern)
-
- def test_size(self, path1):
- url = path1.join("samplefile")
- assert url.size() > len("samplefile")
-
- def test_mtime(self, path1):
- url = path1.join("samplefile")
- assert url.mtime() > 0
-
- def test_relto_wrong_type(self, path1):
- py.test.raises(TypeError, "path1.relto(42)")
-
- def test_load(self, path1):
- p = path1.join('samplepickle')
- obj = p.load()
- assert type(obj) is dict
- assert obj.get('answer',None) == 42
-
- def test_visit_filesonly(self, path1):
- l = []
- for i in path1.visit(lambda x: x.check(file=1)):
- l.append(i.relto(path1))
- assert not "sampledir" in l
- assert path1.sep.join(["sampledir", "otherfile"]) in l
-
- def test_visit_nodotfiles(self, path1):
- l = []
- for i in path1.visit(lambda x: x.check(dotfile=0)):
- l.append(i.relto(path1))
- assert "sampledir" in l
- assert path1.sep.join(["sampledir", "otherfile"]) in l
- assert not ".dotfile" in l
-
- def test_visit_breadthfirst(self, path1):
- l = []
- for i in path1.visit(bf=True):
- l.append(i.relto(path1))
- for i, p in enumerate(l):
- if path1.sep in p:
- for j in range(i, len(l)):
- assert path1.sep in l[j]
- break
- else:
- py.test.fail("huh")
-
- def test_visit_sort(self, path1):
- l = []
- for i in path1.visit(bf=True, sort=True):
- l.append(i.relto(path1))
- for i, p in enumerate(l):
- if path1.sep in p:
- break
- assert l[:i] == sorted(l[:i])
- assert l[i:] == sorted(l[i:])
-
- def test_endswith(self, path1):
- def chk(p):
- return p.check(endswith="pickle")
- assert not chk(path1)
- assert not chk(path1.join('samplefile'))
- assert chk(path1.join('somepickle'))
-
- def test_copy_file(self, path1):
- otherdir = path1.join('otherdir')
- initpy = otherdir.join('__init__.py')
- copied = otherdir.join('copied')
- initpy.copy(copied)
- try:
- assert copied.check()
- s1 = initpy.read()
- s2 = copied.read()
- assert s1 == s2
- finally:
- if copied.check():
- copied.remove()
-
- def test_copy_dir(self, path1):
- otherdir = path1.join('otherdir')
- copied = path1.join('newdir')
- try:
- otherdir.copy(copied)
- assert copied.check(dir=1)
- assert copied.join('__init__.py').check(file=1)
- s1 = otherdir.join('__init__.py').read()
- s2 = copied.join('__init__.py').read()
- assert s1 == s2
- finally:
- if copied.check(dir=1):
- copied.remove(rec=1)
-
- def test_remove_file(self, path1):
- d = path1.ensure('todeleted')
- assert d.check()
- d.remove()
- assert not d.check()
-
- def test_remove_dir_recursive_by_default(self, path1):
- d = path1.ensure('to', 'be', 'deleted')
- assert d.check()
- p = path1.join('to')
- p.remove()
- assert not p.check()
-
- def test_ensure_dir(self, path1):
- b = path1.ensure_dir("001", "002")
- assert b.basename == "002"
- assert b.isdir()
-
- def test_mkdir_and_remove(self, path1):
- tmpdir = path1
- py.test.raises(py.error.EEXIST, tmpdir.mkdir, 'sampledir')
- new = tmpdir.join('mktest1')
- new.mkdir()
- assert new.check(dir=1)
- new.remove()
-
- new = tmpdir.mkdir('mktest')
- assert new.check(dir=1)
- new.remove()
- assert tmpdir.join('mktest') == new
-
- def test_move_file(self, path1):
- p = path1.join('samplefile')
- newp = p.dirpath('moved_samplefile')
- p.move(newp)
- try:
- assert newp.check(file=1)
- assert not p.check()
- finally:
- dp = newp.dirpath()
- if hasattr(dp, 'revert'):
- dp.revert()
- else:
- newp.move(p)
- assert p.check()
-
- def test_move_dir(self, path1):
- source = path1.join('sampledir')
- dest = path1.join('moveddir')
- source.move(dest)
- assert dest.check(dir=1)
- assert dest.join('otherfile').check(file=1)
- assert not source.join('sampledir').check()
-
-def setuptestfs(path):
- if path.join('samplefile').check():
- return
- #print "setting up test fs for", repr(path)
- samplefile = path.ensure('samplefile')
- samplefile.write('samplefile\n')
-
- execfile = path.ensure('execfile')
- execfile.write('x=42')
-
- execfilepy = path.ensure('execfile.py')
- execfilepy.write('x=42')
-
- d = {1:2, 'hello': 'world', 'answer': 42}
- path.ensure('samplepickle').dump(d)
-
- sampledir = path.ensure('sampledir', dir=1)
- sampledir.ensure('otherfile')
-
- otherdir = path.ensure('otherdir', dir=1)
- otherdir.ensure('__init__.py')
-
- module_a = otherdir.ensure('a.py')
- if sys.version_info >= (2,6):
- module_a.write('from .b import stuff as result\n')
- else:
- module_a.write('from b import stuff as result\n')
- module_b = otherdir.ensure('b.py')
- module_b.write('stuff="got it"\n')
- module_c = otherdir.ensure('c.py')
- module_c.write('''import py;
-import otherdir.a
-value = otherdir.a.result
-''')
- module_d = otherdir.ensure('d.py')
- module_d.write('''import py;
-from otherdir import a
-value2 = a.result
-''')
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/path/conftest.py b/tests/wpt/web-platform-tests/tools/py/testing/path/conftest.py
deleted file mode 100644
index a9711b2ce30..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/path/conftest.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import py
-import sys
-from py._path import svnwc as svncommon
-
-svnbin = py.path.local.sysfind('svn')
-repodump = py.path.local(__file__).dirpath('repotest.dump')
-from py.builtin import print_
-
-def pytest_funcarg__repowc1(request):
- if svnbin is None:
- py.test.skip("svn binary not found")
-
- tmpdir = request.getfuncargvalue("tmpdir")
- repo, repourl, wc = request.cached_setup(
- setup=lambda: getrepowc(tmpdir, "path1repo", "path1wc"),
- scope="module",
- )
- for x in ('test_remove', 'test_move', 'test_status_deleted'):
- if request.function.__name__.startswith(x):
- #print >>sys.stderr, ("saving repo", repo, "for", request.function)
- _savedrepowc = save_repowc(repo, wc)
- request.addfinalizer(lambda: restore_repowc(_savedrepowc))
- return repo, repourl, wc
-
-def pytest_funcarg__repowc2(request):
- tmpdir = request.getfuncargvalue("tmpdir")
- name = request.function.__name__
- repo, url, wc = getrepowc(tmpdir, "%s-repo-2" % name, "%s-wc-2" % name)
- return repo, url, wc
-
-def getsvnbin():
- if svnbin is None:
- py.test.skip("svn binary not found")
- return svnbin
-
-# make a wc directory out of a given root url
-# cache previously obtained wcs!
-#
-def getrepowc(tmpdir, reponame='basetestrepo', wcname='wc'):
- repo = tmpdir.mkdir(reponame)
- wcdir = tmpdir.mkdir(wcname)
- repo.ensure(dir=1)
- py.process.cmdexec('svnadmin create "%s"' %
- svncommon._escape_helper(repo))
- py.process.cmdexec('svnadmin load -q "%s" <"%s"' %
- (svncommon._escape_helper(repo), repodump))
- print_("created svn repository", repo)
- wcdir.ensure(dir=1)
- wc = py.path.svnwc(wcdir)
- if py.std.sys.platform == 'win32':
- repourl = "file://" + '/' + str(repo).replace('\\', '/')
- else:
- repourl = "file://%s" % repo
- wc.checkout(repourl)
- print_("checked out new repo into", wc)
- return (repo, repourl, wc)
-
-
-def save_repowc(repo, wc):
- assert not str(repo).startswith("file://"), repo
- assert repo.check()
- savedrepo = repo.dirpath(repo.basename+".1")
- savedwc = wc.dirpath(wc.basename+".1")
- repo.copy(savedrepo)
- wc.localpath.copy(savedwc.localpath)
- return savedrepo, savedwc
-
-def restore_repowc(obj):
- savedrepo, savedwc = obj
- #print >>sys.stderr, ("restoring", savedrepo)
- repo = savedrepo.new(basename=savedrepo.basename[:-2])
- assert repo.check()
- wc = savedwc.new(basename=savedwc.basename[:-2])
- assert wc.check()
- wc.localpath.remove()
- repo.remove()
- savedrepo.move(repo)
- savedwc.localpath.move(wc.localpath)
- py.path.svnurl._lsnorevcache.clear()
- py.path.svnurl._lsrevcache.clear()
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/path/test_cacheutil.py b/tests/wpt/web-platform-tests/tools/py/testing/path/test_cacheutil.py
deleted file mode 100644
index 0b5cd31332f..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/path/test_cacheutil.py
+++ /dev/null
@@ -1,84 +0,0 @@
-import py
-from py._path import cacheutil
-
-class BasicCacheAPITest:
- cache = None
- def test_getorbuild(self):
- val = self.cache.getorbuild(-42, lambda: 42)
- assert val == 42
- val = self.cache.getorbuild(-42, lambda: 23)
- assert val == 42
-
- def test_cache_get_key_error(self):
- py.test.raises(KeyError, "self.cache._getentry(-23)")
-
- def test_delentry_non_raising(self):
- val = self.cache.getorbuild(100, lambda: 100)
- self.cache.delentry(100)
- py.test.raises(KeyError, "self.cache._getentry(100)")
-
- def test_delentry_raising(self):
- val = self.cache.getorbuild(100, lambda: 100)
- self.cache.delentry(100)
- py.test.raises(KeyError, "self.cache.delentry(100, raising=True)")
-
- def test_clear(self):
- self.cache.clear()
-
-class TestBuildcostAccess(BasicCacheAPITest):
- cache = cacheutil.BuildcostAccessCache(maxentries=128)
-
- def test_cache_works_somewhat_simple(self, monkeypatch):
- cache = cacheutil.BuildcostAccessCache()
- # the default gettime
- # BuildcostAccessCache.build can
- # result into time()-time() == 0 which makes the below
- # test fail randomly. Let's rather use incrementing
- # numbers instead.
- l = [0]
- def counter():
- l[0] = l[0] + 1
- return l[0]
- monkeypatch.setattr(cacheutil, 'gettime', counter)
- for x in range(cache.maxentries):
- y = cache.getorbuild(x, lambda: x)
- assert x == y
- for x in range(cache.maxentries):
- assert cache.getorbuild(x, None) == x
- halfentries = int(cache.maxentries / 2)
- for x in range(halfentries):
- assert cache.getorbuild(x, None) == x
- assert cache.getorbuild(x, None) == x
- # evict one entry
- val = cache.getorbuild(-1, lambda: 42)
- assert val == 42
- # check that recently used ones are still there
- # and are not build again
- for x in range(halfentries):
- assert cache.getorbuild(x, None) == x
- assert cache.getorbuild(-1, None) == 42
-
-
-class TestAging(BasicCacheAPITest):
- maxsecs = 0.10
- cache = cacheutil.AgingCache(maxentries=128, maxseconds=maxsecs)
-
- def test_cache_eviction(self):
- self.cache.getorbuild(17, lambda: 17)
- endtime = py.std.time.time() + self.maxsecs * 10
- while py.std.time.time() < endtime:
- try:
- self.cache._getentry(17)
- except KeyError:
- break
- py.std.time.sleep(self.maxsecs*0.3)
- else:
- py.test.fail("waiting for cache eviction failed")
-
-def test_prune_lowestweight():
- maxsecs = 0.05
- cache = cacheutil.AgingCache(maxentries=10, maxseconds=maxsecs)
- for x in range(cache.maxentries):
- cache.getorbuild(x, lambda: x)
- py.std.time.sleep(maxsecs*1.1)
- cache.getorbuild(cache.maxentries+1, lambda: 42)
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/path/test_local.py b/tests/wpt/web-platform-tests/tools/py/testing/path/test_local.py
deleted file mode 100644
index bcf131fd2b7..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/path/test_local.py
+++ /dev/null
@@ -1,860 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-import py
-import pytest
-import os, sys
-from py.path import local
-import common
-
-failsonjython = py.test.mark.xfail("sys.platform.startswith('java')")
-failsonjywin32 = py.test.mark.xfail("sys.platform.startswith('java') "
- "and getattr(os, '_name', None) == 'nt'")
-win32only = py.test.mark.skipif(
- "not (sys.platform == 'win32' or getattr(os, '_name', None) == 'nt')")
-skiponwin32 = py.test.mark.skipif(
- "sys.platform == 'win32' or getattr(os, '_name', None) == 'nt'")
-
-
-def pytest_funcarg__path1(request):
- def setup():
- path1 = request.getfuncargvalue("tmpdir")
- common.setuptestfs(path1)
- return path1
- def teardown(path1):
- # post check
- assert path1.join("samplefile").check()
- return request.cached_setup(setup, teardown, scope="session")
-
-class TestLocalPath(common.CommonFSTests):
- def test_join_normpath(self, tmpdir):
- assert tmpdir.join(".") == tmpdir
- p = tmpdir.join("../%s" % tmpdir.basename)
- assert p == tmpdir
- p = tmpdir.join("..//%s/" % tmpdir.basename)
- assert p == tmpdir
-
- @skiponwin32
- def test_dirpath_abs_no_abs(self, tmpdir):
- p = tmpdir.join('foo')
- assert p.dirpath('/bar') == tmpdir.join('bar')
- assert tmpdir.dirpath('/bar', abs=True) == py.path.local('/bar')
-
- def test_gethash(self, tmpdir):
- md5 = py.builtin._tryimport('md5', 'hashlib').md5
- lib = py.builtin._tryimport('sha', 'hashlib')
- sha = getattr(lib, 'sha1', getattr(lib, 'sha', None))
- fn = tmpdir.join("testhashfile")
- data = 'hello'.encode('ascii')
- fn.write(data, mode="wb")
- assert fn.computehash("md5") == md5(data).hexdigest()
- assert fn.computehash("sha1") == sha(data).hexdigest()
- py.test.raises(ValueError, fn.computehash, "asdasd")
-
- def test_remove_removes_readonly_file(self, tmpdir):
- readonly_file = tmpdir.join('readonly').ensure()
- readonly_file.chmod(0)
- readonly_file.remove()
- assert not readonly_file.check(exists=1)
-
- def test_remove_removes_readonly_dir(self, tmpdir):
- readonly_dir = tmpdir.join('readonlydir').ensure(dir=1)
- readonly_dir.chmod(int("500", 8))
- readonly_dir.remove()
- assert not readonly_dir.check(exists=1)
-
- def test_remove_removes_dir_and_readonly_file(self, tmpdir):
- readonly_dir = tmpdir.join('readonlydir').ensure(dir=1)
- readonly_file = readonly_dir.join('readonlyfile').ensure()
- readonly_file.chmod(0)
- readonly_dir.remove()
- assert not readonly_dir.check(exists=1)
-
- def test_remove_routes_ignore_errors(self, tmpdir, monkeypatch):
- l = []
- monkeypatch.setattr(py.std.shutil, 'rmtree',
- lambda *args, **kwargs: l.append(kwargs))
- tmpdir.remove()
- assert not l[0]['ignore_errors']
- for val in (True, False):
- l[:] = []
- tmpdir.remove(ignore_errors=val)
- assert l[0]['ignore_errors'] == val
-
- def test_initialize_curdir(self):
- assert str(local()) == py.std.os.getcwd()
-
- @skiponwin32
- def test_chdir_gone(self, path1):
- p = path1.ensure("dir_to_be_removed", dir=1)
- p.chdir()
- p.remove()
- pytest.raises(py.error.ENOENT, py.path.local)
- assert path1.chdir() is None
- assert os.getcwd() == str(path1)
-
- def test_as_cwd(self, path1):
- dir = path1.ensure("subdir", dir=1)
- old = py.path.local()
- with dir.as_cwd() as x:
- assert x == old
- assert py.path.local() == dir
- assert os.getcwd() == str(old)
-
- def test_as_cwd_exception(self, path1):
- old = py.path.local()
- dir = path1.ensure("subdir", dir=1)
- with pytest.raises(ValueError):
- with dir.as_cwd():
- raise ValueError()
- assert old == py.path.local()
-
- def test_initialize_reldir(self, path1):
- with path1.as_cwd():
- p = local('samplefile')
- assert p.check()
-
- @pytest.mark.xfail("sys.version_info < (2,6) and sys.platform == 'win32'")
- def test_tilde_expansion(self, monkeypatch, tmpdir):
- monkeypatch.setenv("HOME", str(tmpdir))
- p = py.path.local("~", expanduser=True)
- assert p == os.path.expanduser("~")
-
- def test_eq_with_strings(self, path1):
- path1 = path1.join('sampledir')
- path2 = str(path1)
- assert path1 == path2
- assert path2 == path1
- path3 = path1.join('samplefile')
- assert path3 != path2
- assert path2 != path3
-
- def test_eq_with_none(self, path1):
- assert path1 != None
-
- def test_gt_with_strings(self, path1):
- path2 = path1.join('sampledir')
- path3 = str(path1.join("ttt"))
- assert path3 > path2
- assert path2 < path3
- assert path2 < "ttt"
- assert "ttt" > path2
- path4 = path1.join("aaa")
- l = [path2, path4,path3]
- assert sorted(l) == [path4, path2, path3]
-
- def test_open_and_ensure(self, path1):
- p = path1.join("sub1", "sub2", "file")
- with p.open("w", ensure=1) as f:
- f.write("hello")
- assert p.read() == "hello"
-
- def test_write_and_ensure(self, path1):
- p = path1.join("sub1", "sub2", "file")
- p.write("hello", ensure=1)
- assert p.read() == "hello"
-
- @py.test.mark.multi(bin=(False, True))
- def test_dump(self, tmpdir, bin):
- path = tmpdir.join("dumpfile%s" % int(bin))
- try:
- d = {'answer' : 42}
- path.dump(d, bin=bin)
- f = path.open('rb+')
- dnew = py.std.pickle.load(f)
- assert d == dnew
- finally:
- f.close()
-
- @failsonjywin32
- def test_setmtime(self):
- import tempfile
- import time
- try:
- fd, name = tempfile.mkstemp()
- py.std.os.close(fd)
- except AttributeError:
- name = tempfile.mktemp()
- open(name, 'w').close()
- try:
- mtime = int(time.time())-100
- path = local(name)
- assert path.mtime() != mtime
- path.setmtime(mtime)
- assert path.mtime() == mtime
- path.setmtime()
- assert path.mtime() != mtime
- finally:
- py.std.os.remove(name)
-
- def test_normpath(self, path1):
- new1 = path1.join("/otherdir")
- new2 = path1.join("otherdir")
- assert str(new1) == str(new2)
-
- def test_mkdtemp_creation(self):
- d = local.mkdtemp()
- try:
- assert d.check(dir=1)
- finally:
- d.remove(rec=1)
-
- def test_tmproot(self):
- d = local.mkdtemp()
- tmproot = local.get_temproot()
- try:
- assert d.check(dir=1)
- assert d.dirpath() == tmproot
- finally:
- d.remove(rec=1)
-
- def test_chdir(self, tmpdir):
- old = local()
- try:
- res = tmpdir.chdir()
- assert str(res) == str(old)
- assert py.std.os.getcwd() == str(tmpdir)
- finally:
- old.chdir()
-
- def test_ensure_filepath_withdir(self, tmpdir):
- newfile = tmpdir.join('test1','test')
- newfile.ensure()
- assert newfile.check(file=1)
- newfile.write("42")
- newfile.ensure()
- s = newfile.read()
- assert s == "42"
-
- def test_ensure_filepath_withoutdir(self, tmpdir):
- newfile = tmpdir.join('test1file')
- t = newfile.ensure()
- assert t == newfile
- assert newfile.check(file=1)
-
- def test_ensure_dirpath(self, tmpdir):
- newfile = tmpdir.join('test1','testfile')
- t = newfile.ensure(dir=1)
- assert t == newfile
- assert newfile.check(dir=1)
-
- def test_init_from_path(self, tmpdir):
- l = local()
- l2 = local(l)
- assert l2 == l
-
- wc = py.path.svnwc('.')
- l3 = local(wc)
- assert l3 is not wc
- assert l3.strpath == wc.strpath
- assert not hasattr(l3, 'commit')
-
- @py.test.mark.xfail(run=False, reason="unreliable est for long filenames")
- def test_long_filenames(self, tmpdir):
- if sys.platform == "win32":
- py.test.skip("win32: work around needed for path length limit")
- # see http://codespeak.net/pipermail/py-dev/2008q2/000922.html
-
- # testing paths > 260 chars (which is Windows' limitation, but
- # depending on how the paths are used), but > 4096 (which is the
- # Linux' limitation) - the behaviour of paths with names > 4096 chars
- # is undetermined
- newfilename = '/test' * 60
- l = tmpdir.join(newfilename)
- l.ensure(file=True)
- l.write('foo')
- l2 = tmpdir.join(newfilename)
- assert l2.read() == 'foo'
-
- def test_visit_depth_first(self, tmpdir):
- p1 = tmpdir.ensure("a","1")
- p2 = tmpdir.ensure("b","2")
- p3 = tmpdir.ensure("breadth")
- l = list(tmpdir.visit(lambda x: x.check(file=1)))
- assert len(l) == 3
- # check that breadth comes last
- assert l[2] == p3
-
- def test_visit_rec_fnmatch(self, tmpdir):
- p1 = tmpdir.ensure("a","123")
- p2 = tmpdir.ensure(".b","345")
- l = list(tmpdir.visit("???", rec="[!.]*"))
- assert len(l) == 1
- # check that breadth comes last
- assert l[0] == p1
-
- def test_fnmatch_file_abspath(self, tmpdir):
- b = tmpdir.join("a", "b")
- assert b.fnmatch(os.sep.join("ab"))
- pattern = os.sep.join([str(tmpdir), "*", "b"])
- assert b.fnmatch(pattern)
-
- def test_sysfind(self):
- name = sys.platform == "win32" and "cmd" or "test"
- x = py.path.local.sysfind(name)
- assert x.check(file=1)
- assert py.path.local.sysfind('jaksdkasldqwe') is None
- assert py.path.local.sysfind(name, paths=[]) is None
- x2 = py.path.local.sysfind(name, paths=[x.dirpath()])
- assert x2 == x
-
-
-class TestExecutionOnWindows:
- pytestmark = win32only
-
- def test_sysfind_bat_exe_before(self, tmpdir, monkeypatch):
- monkeypatch.setenv("PATH", str(tmpdir), prepend=os.pathsep)
- tmpdir.ensure("hello")
- h = tmpdir.ensure("hello.bat")
- x = py.path.local.sysfind("hello")
- assert x == h
-
-
-class TestExecution:
- pytestmark = skiponwin32
-
- def test_sysfind_no_permisson_ignored(self, monkeypatch, tmpdir):
- noperm = tmpdir.ensure('noperm', dir=True)
- monkeypatch.setenv("PATH", noperm, prepend=":")
- noperm.chmod(0)
- assert py.path.local.sysfind('jaksdkasldqwe') is None
-
- def test_sysfind_absolute(self):
- x = py.path.local.sysfind('test')
- assert x.check(file=1)
- y = py.path.local.sysfind(str(x))
- assert y.check(file=1)
- assert y == x
-
- def test_sysfind_multiple(self, tmpdir, monkeypatch):
- monkeypatch.setenv('PATH',
- "%s:%s" % (tmpdir.ensure('a'),
- tmpdir.join('b')),
- prepend=":")
- tmpdir.ensure('b', 'a')
- checker = lambda x: x.dirpath().basename == 'b'
- x = py.path.local.sysfind('a', checker=checker)
- assert x.basename == 'a'
- assert x.dirpath().basename == 'b'
- checker = lambda x: None
- assert py.path.local.sysfind('a', checker=checker) is None
-
- def test_sysexec(self):
- x = py.path.local.sysfind('ls')
- out = x.sysexec('-a')
- for x in py.path.local().listdir():
- assert out.find(x.basename) != -1
-
- def test_sysexec_failing(self):
- x = py.path.local.sysfind('false')
- py.test.raises(py.process.cmdexec.Error, """
- x.sysexec('aksjdkasjd')
- """)
-
- def test_make_numbered_dir(self, tmpdir):
- tmpdir.ensure('base.not_an_int', dir=1)
- for i in range(10):
- numdir = local.make_numbered_dir(prefix='base.', rootdir=tmpdir,
- keep=2, lock_timeout=0)
- assert numdir.check()
- assert numdir.basename == 'base.%d' %i
- if i>=1:
- assert numdir.new(ext=str(i-1)).check()
- if i>=2:
- assert numdir.new(ext=str(i-2)).check()
- if i>=3:
- assert not numdir.new(ext=str(i-3)).check()
-
- def test_make_numbered_dir_NotImplemented_Error(self, tmpdir, monkeypatch):
- def notimpl(x, y):
- raise NotImplementedError(42)
- monkeypatch.setattr(py.std.os, 'symlink', notimpl)
- x = tmpdir.make_numbered_dir(rootdir=tmpdir, lock_timeout=0)
- assert x.relto(tmpdir)
- assert x.check()
-
- def test_locked_make_numbered_dir(self, tmpdir):
- for i in range(10):
- numdir = local.make_numbered_dir(prefix='base2.', rootdir=tmpdir,
- keep=2)
- assert numdir.check()
- assert numdir.basename == 'base2.%d' %i
- for j in range(i):
- assert numdir.new(ext=str(j)).check()
-
- def test_error_preservation(self, path1):
- py.test.raises (EnvironmentError, path1.join('qwoeqiwe').mtime)
- py.test.raises (EnvironmentError, path1.join('qwoeqiwe').read)
-
- #def test_parentdirmatch(self):
- # local.parentdirmatch('std', startmodule=__name__)
- #
-
-
-class TestImport:
- def test_pyimport(self, path1):
- obj = path1.join('execfile.py').pyimport()
- assert obj.x == 42
- assert obj.__name__ == 'execfile'
-
- def test_pyimport_renamed_dir_creates_mismatch(self, tmpdir):
- p = tmpdir.ensure("a", "test_x123.py")
- p.pyimport()
- tmpdir.join("a").move(tmpdir.join("b"))
- pytest.raises(tmpdir.ImportMismatchError,
- lambda: tmpdir.join("b", "test_x123.py").pyimport())
-
- def test_pyimport_messy_name(self, tmpdir):
- # http://bitbucket.org/hpk42/py-trunk/issue/129
- path = tmpdir.ensure('foo__init__.py')
- obj = path.pyimport()
-
- def test_pyimport_dir(self, tmpdir):
- p = tmpdir.join("hello_123")
- p_init = p.ensure("__init__.py")
- m = p.pyimport()
- assert m.__name__ == "hello_123"
- m = p_init.pyimport()
- assert m.__name__ == "hello_123"
-
- def test_pyimport_execfile_different_name(self, path1):
- obj = path1.join('execfile.py').pyimport(modname="0x.y.z")
- assert obj.x == 42
- assert obj.__name__ == '0x.y.z'
-
- def test_pyimport_a(self, path1):
- otherdir = path1.join('otherdir')
- mod = otherdir.join('a.py').pyimport()
- assert mod.result == "got it"
- assert mod.__name__ == 'otherdir.a'
-
- def test_pyimport_b(self, path1):
- otherdir = path1.join('otherdir')
- mod = otherdir.join('b.py').pyimport()
- assert mod.stuff == "got it"
- assert mod.__name__ == 'otherdir.b'
-
- def test_pyimport_c(self, path1):
- otherdir = path1.join('otherdir')
- mod = otherdir.join('c.py').pyimport()
- assert mod.value == "got it"
-
- def test_pyimport_d(self, path1):
- otherdir = path1.join('otherdir')
- mod = otherdir.join('d.py').pyimport()
- assert mod.value2 == "got it"
-
- def test_pyimport_and_import(self, tmpdir):
- tmpdir.ensure('xxxpackage', '__init__.py')
- mod1path = tmpdir.ensure('xxxpackage', 'module1.py')
- mod1 = mod1path.pyimport()
- assert mod1.__name__ == 'xxxpackage.module1'
- from xxxpackage import module1
- assert module1 is mod1
-
- def test_pyimport_check_filepath_consistency(self, monkeypatch, tmpdir):
- name = 'pointsback123'
- ModuleType = type(py.std.os)
- p = tmpdir.ensure(name + '.py')
- for ending in ('.pyc', '$py.class', '.pyo'):
- mod = ModuleType(name)
- pseudopath = tmpdir.ensure(name+ending)
- mod.__file__ = str(pseudopath)
- monkeypatch.setitem(sys.modules, name, mod)
- newmod = p.pyimport()
- assert mod == newmod
- monkeypatch.undo()
- mod = ModuleType(name)
- pseudopath = tmpdir.ensure(name+"123.py")
- mod.__file__ = str(pseudopath)
- monkeypatch.setitem(sys.modules, name, mod)
- excinfo = py.test.raises(pseudopath.ImportMismatchError,
- "p.pyimport()")
- modname, modfile, orig = excinfo.value.args
- assert modname == name
- assert modfile == pseudopath
- assert orig == p
- assert issubclass(pseudopath.ImportMismatchError, ImportError)
-
- def test_issue131_pyimport_on__init__(self, tmpdir):
- # __init__.py files may be namespace packages, and thus the
- # __file__ of an imported module may not be ourselves
- # see issue
- p1 = tmpdir.ensure("proja", "__init__.py")
- p2 = tmpdir.ensure("sub", "proja", "__init__.py")
- m1 = p1.pyimport()
- m2 = p2.pyimport()
- assert m1 == m2
-
- def test_ensuresyspath_append(self, tmpdir):
- root1 = tmpdir.mkdir("root1")
- file1 = root1.ensure("x123.py")
- assert str(root1) not in sys.path
- file1.pyimport(ensuresyspath="append")
- assert str(root1) == sys.path[-1]
- assert str(root1) not in sys.path[:-1]
-
-
-def test_pypkgdir(tmpdir):
- pkg = tmpdir.ensure('pkg1', dir=1)
- pkg.ensure("__init__.py")
- pkg.ensure("subdir/__init__.py")
- assert pkg.pypkgpath() == pkg
- assert pkg.join('subdir', '__init__.py').pypkgpath() == pkg
-
-def test_pypkgdir_unimportable(tmpdir):
- pkg = tmpdir.ensure('pkg1-1', dir=1) # unimportable
- pkg.ensure("__init__.py")
- subdir = pkg.ensure("subdir/__init__.py").dirpath()
- assert subdir.pypkgpath() == subdir
- assert subdir.ensure("xyz.py").pypkgpath() == subdir
- assert not pkg.pypkgpath()
-
-def test_isimportable():
- from py._path.local import isimportable
- assert not isimportable("")
- assert isimportable("x")
- assert isimportable("x1")
- assert isimportable("x_1")
- assert isimportable("_")
- assert isimportable("_1")
- assert not isimportable("x-1")
- assert not isimportable("x:1")
-
-def test_homedir_from_HOME(monkeypatch):
- path = os.getcwd()
- monkeypatch.setenv("HOME", path)
- assert py.path.local._gethomedir() == py.path.local(path)
-
-def test_homedir_not_exists(monkeypatch):
- monkeypatch.delenv("HOME", raising=False)
- monkeypatch.delenv("HOMEDRIVE", raising=False)
- homedir = py.path.local._gethomedir()
- assert homedir is None
-
-def test_samefile(tmpdir):
- assert tmpdir.samefile(tmpdir)
- p = tmpdir.ensure("hello")
- assert p.samefile(p)
- with p.dirpath().as_cwd():
- assert p.samefile(p.basename)
- if sys.platform == "win32":
- p1 = p.__class__(str(p).lower())
- p2 = p.__class__(str(p).upper())
- assert p1.samefile(p2)
-
-def test_listdir_single_arg(tmpdir):
- tmpdir.ensure("hello")
- assert tmpdir.listdir("hello")[0].basename == "hello"
-
-def test_mkdtemp_rootdir(tmpdir):
- dtmp = local.mkdtemp(rootdir=tmpdir)
- assert tmpdir.listdir() == [dtmp]
-
-class TestWINLocalPath:
- pytestmark = win32only
-
- def test_owner_group_not_implemented(self, path1):
- py.test.raises(NotImplementedError, "path1.stat().owner")
- py.test.raises(NotImplementedError, "path1.stat().group")
-
- def test_chmod_simple_int(self, path1):
- py.builtin.print_("path1 is", path1)
- mode = path1.stat().mode
- # Ensure that we actually change the mode to something different.
- path1.chmod(mode == 0 and 1 or 0)
- try:
- print(path1.stat().mode)
- print(mode)
- assert path1.stat().mode != mode
- finally:
- path1.chmod(mode)
- assert path1.stat().mode == mode
-
- def test_path_comparison_lowercase_mixed(self, path1):
- t1 = path1.join("a_path")
- t2 = path1.join("A_path")
- assert t1 == t1
- assert t1 == t2
-
- def test_relto_with_mixed_case(self, path1):
- t1 = path1.join("a_path", "fiLe")
- t2 = path1.join("A_path")
- assert t1.relto(t2) == "fiLe"
-
- def test_allow_unix_style_paths(self, path1):
- t1 = path1.join('a_path')
- assert t1 == str(path1) + '\\a_path'
- t1 = path1.join('a_path/')
- assert t1 == str(path1) + '\\a_path'
- t1 = path1.join('dir/a_path')
- assert t1 == str(path1) + '\\dir\\a_path'
-
- def test_sysfind_in_currentdir(self, path1):
- cmd = py.path.local.sysfind('cmd')
- root = cmd.new(dirname='', basename='') # c:\ in most installations
- with root.as_cwd():
- x = py.path.local.sysfind(cmd.relto(root))
- assert x.check(file=1)
-
- def test_fnmatch_file_abspath_posix_pattern_on_win32(self, tmpdir):
- # path-matching patterns might contain a posix path separator '/'
- # Test that we can match that pattern on windows.
- import posixpath
- b = tmpdir.join("a", "b")
- assert b.fnmatch(posixpath.sep.join("ab"))
- pattern = posixpath.sep.join([str(tmpdir), "*", "b"])
- assert b.fnmatch(pattern)
-
-class TestPOSIXLocalPath:
- pytestmark = skiponwin32
-
- def test_hardlink(self, tmpdir):
- linkpath = tmpdir.join('test')
- filepath = tmpdir.join('file')
- filepath.write("Hello")
- nlink = filepath.stat().nlink
- linkpath.mklinkto(filepath)
- assert filepath.stat().nlink == nlink + 1
-
- def test_symlink_are_identical(self, tmpdir):
- filepath = tmpdir.join('file')
- filepath.write("Hello")
- linkpath = tmpdir.join('test')
- linkpath.mksymlinkto(filepath)
- assert linkpath.readlink() == str(filepath)
-
- def test_symlink_isfile(self, tmpdir):
- linkpath = tmpdir.join('test')
- filepath = tmpdir.join('file')
- filepath.write("")
- linkpath.mksymlinkto(filepath)
- assert linkpath.check(file=1)
- assert not linkpath.check(link=0, file=1)
- assert linkpath.islink()
-
- def test_symlink_relative(self, tmpdir):
- linkpath = tmpdir.join('test')
- filepath = tmpdir.join('file')
- filepath.write("Hello")
- linkpath.mksymlinkto(filepath, absolute=False)
- assert linkpath.readlink() == "file"
- assert filepath.read() == linkpath.read()
-
- def test_symlink_not_existing(self, tmpdir):
- linkpath = tmpdir.join('testnotexisting')
- assert not linkpath.check(link=1)
- assert linkpath.check(link=0)
-
- def test_relto_with_root(self, path1, tmpdir):
- y = path1.join('x').relto(py.path.local('/'))
- assert y[0] == str(path1)[1]
-
- def test_visit_recursive_symlink(self, tmpdir):
- linkpath = tmpdir.join('test')
- linkpath.mksymlinkto(tmpdir)
- visitor = tmpdir.visit(None, lambda x: x.check(link=0))
- assert list(visitor) == [linkpath]
-
- def test_symlink_isdir(self, tmpdir):
- linkpath = tmpdir.join('test')
- linkpath.mksymlinkto(tmpdir)
- assert linkpath.check(dir=1)
- assert not linkpath.check(link=0, dir=1)
-
- def test_symlink_remove(self, tmpdir):
- linkpath = tmpdir.join('test')
- linkpath.mksymlinkto(linkpath) # point to itself
- assert linkpath.check(link=1)
- linkpath.remove()
- assert not linkpath.check()
-
- def test_realpath_file(self, tmpdir):
- linkpath = tmpdir.join('test')
- filepath = tmpdir.join('file')
- filepath.write("")
- linkpath.mksymlinkto(filepath)
- realpath = linkpath.realpath()
- assert realpath.basename == 'file'
-
- def test_owner(self, path1, tmpdir):
- from pwd import getpwuid
- from grp import getgrgid
- stat = path1.stat()
- assert stat.path == path1
-
- uid = stat.uid
- gid = stat.gid
- owner = getpwuid(uid)[0]
- group = getgrgid(gid)[0]
-
- assert uid == stat.uid
- assert owner == stat.owner
- assert gid == stat.gid
- assert group == stat.group
-
- def test_stat_helpers(self, tmpdir, monkeypatch):
- path1 = tmpdir.ensure("file")
- stat1 = path1.stat()
- stat2 = tmpdir.stat()
- assert stat1.isfile()
- assert stat2.isdir()
- assert not stat1.islink()
- assert not stat2.islink()
-
- def test_stat_non_raising(self, tmpdir):
- path1 = tmpdir.join("file")
- pytest.raises(py.error.ENOENT, lambda: path1.stat())
- res = path1.stat(raising=False)
- assert res is None
-
- def test_atime(self, tmpdir):
- import time
- path = tmpdir.ensure('samplefile')
- now = time.time()
- atime1 = path.atime()
- # we could wait here but timer resolution is very
- # system dependent
- path.read()
- time.sleep(0.01)
- atime2 = path.atime()
- time.sleep(0.01)
- duration = time.time() - now
- assert (atime2-atime1) <= duration
-
- def test_commondir(self, path1):
- # XXX This is here in local until we find a way to implement this
- # using the subversion command line api.
- p1 = path1.join('something')
- p2 = path1.join('otherthing')
- assert p1.common(p2) == path1
- assert p2.common(p1) == path1
-
- def test_commondir_nocommon(self, path1):
- # XXX This is here in local until we find a way to implement this
- # using the subversion command line api.
- p1 = path1.join('something')
- p2 = py.path.local(path1.sep+'blabla')
- assert p1.common(p2) == '/'
-
- def test_join_to_root(self, path1):
- root = path1.parts()[0]
- assert len(str(root)) == 1
- assert str(root.join('a')) == '//a' # posix allows two slashes
-
- def test_join_root_to_root_with_no_abs(self, path1):
- nroot = path1.join('/')
- assert str(path1) == str(nroot)
- assert path1 == nroot
-
- def test_chmod_simple_int(self, path1):
- mode = path1.stat().mode
- path1.chmod(int(mode/2))
- try:
- assert path1.stat().mode != mode
- finally:
- path1.chmod(mode)
- assert path1.stat().mode == mode
-
- def test_chmod_rec_int(self, path1):
- # XXX fragile test
- recfilter = lambda x: x.check(dotfile=0, link=0)
- oldmodes = {}
- for x in path1.visit(rec=recfilter):
- oldmodes[x] = x.stat().mode
- path1.chmod(int("772", 8), rec=recfilter)
- try:
- for x in path1.visit(rec=recfilter):
- assert x.stat().mode & int("777", 8) == int("772", 8)
- finally:
- for x,y in oldmodes.items():
- x.chmod(y)
-
- def test_copy_archiving(self, tmpdir):
- unicode_fn = u"something-\342\200\223.txt"
- f = tmpdir.ensure("a", unicode_fn)
- a = f.dirpath()
- oldmode = f.stat().mode
- newmode = oldmode ^ 1
- f.chmod(newmode)
- b = tmpdir.join("b")
- a.copy(b, mode=True)
- assert b.join(f.basename).stat().mode == newmode
-
- @failsonjython
- def test_chown_identity(self, path1):
- owner = path1.stat().owner
- group = path1.stat().group
- path1.chown(owner, group)
-
- @failsonjython
- def test_chown_dangling_link(self, path1):
- owner = path1.stat().owner
- group = path1.stat().group
- x = path1.join('hello')
- x.mksymlinkto('qlwkejqwlek')
- try:
- path1.chown(owner, group, rec=1)
- finally:
- x.remove(rec=0)
-
- @failsonjython
- def test_chown_identity_rec_mayfail(self, path1):
- owner = path1.stat().owner
- group = path1.stat().group
- path1.chown(owner, group)
-
-
-class TestUnicodePy2Py3:
- def test_join_ensure(self, tmpdir, monkeypatch):
- if sys.version_info >= (3,0) and "LANG" not in os.environ:
- pytest.skip("cannot run test without locale")
- x = py.path.local(tmpdir.strpath)
- part = "hällo"
- y = x.ensure(part)
- assert x.join(part) == y
-
- def test_listdir(self, tmpdir):
- if sys.version_info >= (3,0) and "LANG" not in os.environ:
- pytest.skip("cannot run test without locale")
- x = py.path.local(tmpdir.strpath)
- part = "hällo"
- y = x.ensure(part)
- assert x.listdir(part)[0] == y
-
- @pytest.mark.xfail(reason="changing read/write might break existing usages")
- def test_read_write(self, tmpdir):
- x = tmpdir.join("hello")
- part = py.builtin._totext("hällo", "utf8")
- x.write(part)
- assert x.read() == part
- x.write(part.encode(sys.getdefaultencoding()))
- assert x.read() == part.encode(sys.getdefaultencoding())
-
-class TestBinaryAndTextMethods:
- def test_read_binwrite(self, tmpdir):
- x = tmpdir.join("hello")
- part = py.builtin._totext("hällo", "utf8")
- part_utf8 = part.encode("utf8")
- x.write_binary(part_utf8)
- assert x.read_binary() == part_utf8
- s = x.read_text(encoding="utf8")
- assert s == part
- assert py.builtin._istext(s)
-
- def test_read_textwrite(self, tmpdir):
- x = tmpdir.join("hello")
- part = py.builtin._totext("hällo", "utf8")
- part_utf8 = part.encode("utf8")
- x.write_text(part, encoding="utf8")
- assert x.read_binary() == part_utf8
- assert x.read_text(encoding="utf8") == part
-
- def test_default_encoding(self, tmpdir):
- x = tmpdir.join("hello")
- # Can't use UTF8 as the default encoding (ASCII) doesn't support it
- part = py.builtin._totext("hello", "ascii")
- x.write_text(part, "ascii")
- s = x.read_text("ascii")
- assert s == part
- assert type(s) == type(part)
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/path/test_svnauth.py b/tests/wpt/web-platform-tests/tools/py/testing/path/test_svnauth.py
deleted file mode 100644
index b3f36656168..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/path/test_svnauth.py
+++ /dev/null
@@ -1,454 +0,0 @@
-import py
-import svntestbase
-from py.path import SvnAuth
-import time
-import sys
-
-svnbin = py.path.local.sysfind('svn')
-
-def make_repo_auth(repo, userdata):
- """ write config to repo
-
- user information in userdata is used for auth
- userdata has user names as keys, and a tuple (password, readwrite) as
- values, where 'readwrite' is either 'r' or 'rw'
- """
- confdir = py.path.local(repo).join('conf')
- confdir.join('svnserve.conf').write('''\
-[general]
-anon-access = none
-password-db = passwd
-authz-db = authz
-realm = TestRepo
-''')
- authzdata = '[/]\n'
- passwddata = '[users]\n'
- for user in userdata:
- authzdata += '%s = %s\n' % (user, userdata[user][1])
- passwddata += '%s = %s\n' % (user, userdata[user][0])
- confdir.join('authz').write(authzdata)
- confdir.join('passwd').write(passwddata)
-
-def serve_bg(repopath):
- pidfile = py.path.local(repopath).join('pid')
- port = 10000
- e = None
- while port < 10010:
- cmd = 'svnserve -d -T --listen-port=%d --pid-file=%s -r %s' % (
- port, pidfile, repopath)
- print(cmd)
- try:
- py.process.cmdexec(cmd)
- except py.process.cmdexec.Error:
- e = sys.exc_info()[1]
- else:
- # XXX we assume here that the pid file gets written somewhere, I
- # guess this should be relatively safe... (I hope, at least?)
- counter = pid = 0
- while counter < 10:
- counter += 1
- try:
- pid = pidfile.read()
- except py.error.ENOENT:
- pass
- if pid:
- break
- time.sleep(0.2)
- return port, int(pid)
- port += 1
- raise IOError('could not start svnserve: %s' % (e,))
-
-class TestSvnAuth(object):
- def test_basic(self):
- auth = SvnAuth('foo', 'bar')
- assert auth.username == 'foo'
- assert auth.password == 'bar'
- assert str(auth)
-
- def test_makecmdoptions_uname_pw_makestr(self):
- auth = SvnAuth('foo', 'bar')
- assert auth.makecmdoptions() == '--username="foo" --password="bar"'
-
- def test_makecmdoptions_quote_escape(self):
- auth = SvnAuth('fo"o', '"ba\'r"')
- assert auth.makecmdoptions() == '--username="fo\\"o" --password="\\"ba\'r\\""'
-
- def test_makecmdoptions_no_cache_auth(self):
- auth = SvnAuth('foo', 'bar', cache_auth=False)
- assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
- '--no-auth-cache')
-
- def test_makecmdoptions_no_interactive(self):
- auth = SvnAuth('foo', 'bar', interactive=False)
- assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
- '--non-interactive')
-
- def test_makecmdoptions_no_interactive_no_cache_auth(self):
- auth = SvnAuth('foo', 'bar', cache_auth=False,
- interactive=False)
- assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
- '--no-auth-cache --non-interactive')
-
-class svnwc_no_svn(py.path.svnwc):
- def __new__(cls, *args, **kwargs):
- self = super(svnwc_no_svn, cls).__new__(cls, *args, **kwargs)
- self.commands = []
- return self
-
- def _svn(self, *args):
- self.commands.append(args)
-
-class TestSvnWCAuth(object):
- def setup_method(self, meth):
- if not svnbin:
- py.test.skip("svn binary required")
- self.auth = SvnAuth('user', 'pass', cache_auth=False)
-
- def test_checkout(self):
- wc = svnwc_no_svn('foo', auth=self.auth)
- wc.checkout('url')
- assert wc.commands[0][-1] == ('--username="user" --password="pass" '
- '--no-auth-cache')
-
- def test_commit(self):
- wc = svnwc_no_svn('foo', auth=self.auth)
- wc.commit('msg')
- assert wc.commands[0][-1] == ('--username="user" --password="pass" '
- '--no-auth-cache')
-
- def test_checkout_no_cache_auth(self):
- wc = svnwc_no_svn('foo', auth=self.auth)
- wc.checkout('url')
- assert wc.commands[0][-1] == ('--username="user" --password="pass" '
- '--no-auth-cache')
-
- def test_checkout_auth_from_constructor(self):
- wc = svnwc_no_svn('foo', auth=self.auth)
- wc.checkout('url')
- assert wc.commands[0][-1] == ('--username="user" --password="pass" '
- '--no-auth-cache')
-
-class svnurl_no_svn(py.path.svnurl):
- cmdexec_output = 'test'
- popen_output = 'test'
- def __new__(cls, *args, **kwargs):
- self = super(svnurl_no_svn, cls).__new__(cls, *args, **kwargs)
- self.commands = []
- return self
-
- def _cmdexec(self, cmd):
- self.commands.append(cmd)
- return self.cmdexec_output
-
- def _popen(self, cmd):
- self.commands.append(cmd)
- return self.popen_output
-
-class TestSvnURLAuth(object):
- def setup_method(self, meth):
- self.auth = SvnAuth('foo', 'bar')
-
- def test_init(self):
- u = svnurl_no_svn('http://foo.bar/svn')
- assert u.auth is None
-
- u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
- assert u.auth is self.auth
-
- def test_new(self):
- u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
- new = u.new(basename='bar')
- assert new.auth is self.auth
- assert new.url == 'http://foo.bar/svn/bar'
-
- def test_join(self):
- u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
- new = u.join('foo')
- assert new.auth is self.auth
- assert new.url == 'http://foo.bar/svn/foo'
-
- def test_listdir(self):
- u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
- u.cmdexec_output = '''\
- 1717 johnny 1529 Nov 04 14:32 LICENSE.txt
- 1716 johnny 5352 Nov 04 14:28 README.txt
-'''
- paths = u.listdir()
- assert paths[0].auth is self.auth
- assert paths[1].auth is self.auth
- assert paths[0].basename == 'LICENSE.txt'
-
- def test_info(self):
- u = svnurl_no_svn('http://foo.bar/svn/LICENSE.txt', auth=self.auth)
- def dirpath(self):
- return self
- u.cmdexec_output = '''\
- 1717 johnny 1529 Nov 04 14:32 LICENSE.txt
- 1716 johnny 5352 Nov 04 14:28 README.txt
-'''
- org_dp = u.__class__.dirpath
- u.__class__.dirpath = dirpath
- try:
- info = u.info()
- finally:
- u.dirpath = org_dp
- assert info.size == 1529
-
- def test_open(self):
- u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
- foo = u.join('foo')
- foo.check = lambda *args, **kwargs: True
- ret = foo.open()
- assert ret == 'test'
- assert '--username="foo" --password="bar"' in foo.commands[0]
-
- def test_dirpath(self):
- u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
- parent = u.dirpath()
- assert parent.auth is self.auth
-
- def test_mkdir(self):
- u = svnurl_no_svn('http://foo.bar/svn/qweqwe', auth=self.auth)
- assert not u.commands
- u.mkdir(msg='created dir foo')
- assert u.commands
- assert '--username="foo" --password="bar"' in u.commands[0]
-
- def test_copy(self):
- u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
- u2 = svnurl_no_svn('http://foo.bar/svn2')
- u.copy(u2, 'copied dir')
- assert '--username="foo" --password="bar"' in u.commands[0]
-
- def test_rename(self):
- u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
- u.rename('http://foo.bar/svn/bar', 'moved foo to bar')
- assert '--username="foo" --password="bar"' in u.commands[0]
-
- def test_remove(self):
- u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
- u.remove(msg='removing foo')
- assert '--username="foo" --password="bar"' in u.commands[0]
-
- def test_export(self):
- u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
- target = py.path.local('/foo')
- u.export(target)
- assert '--username="foo" --password="bar"' in u.commands[0]
-
- def test_log(self):
- u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
- u.popen_output = py.io.TextIO(py.builtin._totext('''\
-<?xml version="1.0"?>
-<log>
-<logentry revision="51381">
-<author>guido</author>
-<date>2008-02-11T12:12:18.476481Z</date>
-<msg>Creating branch to work on auth support for py.path.svn*.
-</msg>
-</logentry>
-</log>
-''', 'ascii'))
- u.check = lambda *args, **kwargs: True
- ret = u.log(10, 20, verbose=True)
- assert '--username="foo" --password="bar"' in u.commands[0]
- assert len(ret) == 1
- assert int(ret[0].rev) == 51381
- assert ret[0].author == 'guido'
-
- def test_propget(self):
- u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
- u.propget('foo')
- assert '--username="foo" --password="bar"' in u.commands[0]
-
-def pytest_funcarg__setup(request):
- return Setup(request)
-
-class Setup:
- def __init__(self, request):
- if not svnbin:
- py.test.skip("svn binary required")
- if not request.config.option.runslowtests:
- py.test.skip('use --runslowtests to run these tests')
-
- tmpdir = request.getfuncargvalue("tmpdir")
- repodir = tmpdir.join("repo")
- py.process.cmdexec('svnadmin create %s' % repodir)
- if sys.platform == 'win32':
- repodir = '/' + str(repodir).replace('\\', '/')
- self.repo = py.path.svnurl("file://%s" % repodir)
- if py.std.sys.platform == 'win32':
- # remove trailing slash...
- repodir = repodir[1:]
- self.repopath = py.path.local(repodir)
- self.temppath = tmpdir.mkdir("temppath")
- self.auth = SvnAuth('johnny', 'foo', cache_auth=False,
- interactive=False)
- make_repo_auth(self.repopath, {'johnny': ('foo', 'rw')})
- self.port, self.pid = serve_bg(self.repopath.dirpath())
- # XXX caching is too global
- py.path.svnurl._lsnorevcache._dict.clear()
- request.addfinalizer(lambda: py.process.kill(self.pid))
-
-class TestSvnWCAuthFunctional:
- def test_checkout_constructor_arg(self, setup):
- wc = py.path.svnwc(setup.temppath, auth=setup.auth)
- wc.checkout(
- 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
- assert wc.join('.svn').check()
-
- def test_checkout_function_arg(self, setup):
- wc = py.path.svnwc(setup.temppath, auth=setup.auth)
- wc.checkout(
- 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
- assert wc.join('.svn').check()
-
- def test_checkout_failing_non_interactive(self, setup):
- auth = SvnAuth('johnny', 'bar', cache_auth=False,
- interactive=False)
- wc = py.path.svnwc(setup.temppath, auth)
- py.test.raises(Exception,
- ("wc.checkout('svn://localhost:%(port)s/%(repopath)s')" %
- setup.__dict__))
-
- def test_log(self, setup):
- wc = py.path.svnwc(setup.temppath, setup.auth)
- wc.checkout(
- 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
- foo = wc.ensure('foo.txt')
- wc.commit('added foo.txt')
- log = foo.log()
- assert len(log) == 1
- assert log[0].msg == 'added foo.txt'
-
- def test_switch(self, setup):
- wc = py.path.svnwc(setup.temppath, auth=setup.auth)
- svnurl = 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename)
- wc.checkout(svnurl)
- wc.ensure('foo', dir=True).ensure('foo.txt').write('foo')
- wc.commit('added foo dir with foo.txt file')
- wc.ensure('bar', dir=True)
- wc.commit('added bar dir')
- bar = wc.join('bar')
- bar.switch(svnurl + '/foo')
- assert bar.join('foo.txt')
-
- def test_update(self, setup):
- wc1 = py.path.svnwc(setup.temppath.ensure('wc1', dir=True),
- auth=setup.auth)
- wc2 = py.path.svnwc(setup.temppath.ensure('wc2', dir=True),
- auth=setup.auth)
- wc1.checkout(
- 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
- wc2.checkout(
- 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
- wc1.ensure('foo', dir=True)
- wc1.commit('added foo dir')
- wc2.update()
- assert wc2.join('foo').check()
-
- auth = SvnAuth('unknown', 'unknown', interactive=False)
- wc2.auth = auth
- py.test.raises(Exception, 'wc2.update()')
-
- def test_lock_unlock_status(self, setup):
- port = setup.port
- wc = py.path.svnwc(setup.temppath, auth=setup.auth)
- wc.checkout(
- 'svn://localhost:%s/%s' % (port, setup.repopath.basename,))
- wc.ensure('foo', file=True)
- wc.commit('added foo file')
- foo = wc.join('foo')
- foo.lock()
- status = foo.status()
- assert status.locked
- foo.unlock()
- status = foo.status()
- assert not status.locked
-
- auth = SvnAuth('unknown', 'unknown', interactive=False)
- foo.auth = auth
- py.test.raises(Exception, 'foo.lock()')
- py.test.raises(Exception, 'foo.unlock()')
-
- def test_diff(self, setup):
- port = setup.port
- wc = py.path.svnwc(setup.temppath, auth=setup.auth)
- wc.checkout(
- 'svn://localhost:%s/%s' % (port, setup.repopath.basename,))
- wc.ensure('foo', file=True)
- wc.commit('added foo file')
- wc.update()
- rev = int(wc.status().rev)
- foo = wc.join('foo')
- foo.write('bar')
- diff = foo.diff()
- assert '\n+bar\n' in diff
- foo.commit('added some content')
- diff = foo.diff()
- assert not diff
- diff = foo.diff(rev=rev)
- assert '\n+bar\n' in diff
-
- auth = SvnAuth('unknown', 'unknown', interactive=False)
- foo.auth = auth
- py.test.raises(Exception, 'foo.diff(rev=rev)')
-
-class TestSvnURLAuthFunctional:
- def test_listdir(self, setup):
- port = setup.port
- u = py.path.svnurl(
- 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
- auth=setup.auth)
- u.ensure('foo')
- paths = u.listdir()
- assert len(paths) == 1
- assert paths[0].auth is setup.auth
-
- auth = SvnAuth('foo', 'bar', interactive=False)
- u = py.path.svnurl(
- 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
- auth=auth)
- py.test.raises(Exception, 'u.listdir()')
-
- def test_copy(self, setup):
- port = setup.port
- u = py.path.svnurl(
- 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
- auth=setup.auth)
- foo = u.mkdir('foo')
- assert foo.check()
- bar = u.join('bar')
- foo.copy(bar)
- assert bar.check()
- assert bar.auth is setup.auth
-
- auth = SvnAuth('foo', 'bar', interactive=False)
- u = py.path.svnurl(
- 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
- auth=auth)
- foo = u.join('foo')
- bar = u.join('bar')
- py.test.raises(Exception, 'foo.copy(bar)')
-
- def test_write_read(self, setup):
- port = setup.port
- u = py.path.svnurl(
- 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
- auth=setup.auth)
- foo = u.ensure('foo')
- fp = foo.open()
- try:
- data = fp.read()
- finally:
- fp.close()
- assert data == ''
-
- auth = SvnAuth('foo', 'bar', interactive=False)
- u = py.path.svnurl(
- 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
- auth=auth)
- foo = u.join('foo')
- py.test.raises(Exception, 'foo.open()')
-
- # XXX rinse, repeat... :|
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/path/test_svnwc.py b/tests/wpt/web-platform-tests/tools/py/testing/path/test_svnwc.py
deleted file mode 100644
index 9e6b524acab..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/path/test_svnwc.py
+++ /dev/null
@@ -1,549 +0,0 @@
-import py
-import os, sys
-import pytest
-from py._path.svnwc import InfoSvnWCCommand, XMLWCStatus, parse_wcinfotime
-from py._path import svnwc as svncommon
-from svntestbase import CommonSvnTests
-
-def test_make_repo(path1, tmpdir):
- repo = tmpdir.join("repo")
- py.process.cmdexec('svnadmin create %s' % repo)
- if sys.platform == 'win32':
- repo = '/' + str(repo).replace('\\', '/')
- repo = py.path.svnurl("file://%s" % repo)
- wc = py.path.svnwc(tmpdir.join("wc"))
- wc.checkout(repo)
- assert wc.rev == 0
- assert len(wc.listdir()) == 0
- p = wc.join("a_file")
- p.write("test file")
- p.add()
- rev = wc.commit("some test")
- assert p.info().rev == 1
- assert rev == 1
- rev = wc.commit()
- assert rev is None
-
-def pytest_funcarg__path1(request):
- repo, repourl, wc = request.getfuncargvalue("repowc1")
- return wc
-
-class TestWCSvnCommandPath(CommonSvnTests):
- def test_status_attributes_simple(self, path1):
- def assert_nochange(p):
- s = p.status()
- assert not s.modified
- assert not s.prop_modified
- assert not s.added
- assert not s.deleted
- assert not s.replaced
-
- dpath = path1.join('sampledir')
- assert_nochange(path1.join('sampledir'))
- assert_nochange(path1.join('samplefile'))
-
- def test_status_added(self, path1):
- nf = path1.join('newfile')
- nf.write('hello')
- nf.add()
- try:
- s = nf.status()
- assert s.added
- assert not s.modified
- assert not s.prop_modified
- assert not s.replaced
- finally:
- nf.revert()
-
- def test_status_change(self, path1):
- nf = path1.join('samplefile')
- try:
- nf.write(nf.read() + 'change')
- s = nf.status()
- assert not s.added
- assert s.modified
- assert not s.prop_modified
- assert not s.replaced
- finally:
- nf.revert()
-
- def test_status_added_ondirectory(self, path1):
- sampledir = path1.join('sampledir')
- try:
- t2 = sampledir.mkdir('t2')
- t1 = t2.join('t1')
- t1.write('test')
- t1.add()
- s = sampledir.status(rec=1)
- # Comparing just the file names, because paths are unpredictable
- # on Windows. (long vs. 8.3 paths)
- assert t1.basename in [item.basename for item in s.added]
- assert t2.basename in [item.basename for item in s.added]
- finally:
- t2.revert(rec=1)
- t2.localpath.remove(rec=1)
-
- def test_status_unknown(self, path1):
- t1 = path1.join('un1')
- try:
- t1.write('test')
- s = path1.status()
- # Comparing just the file names, because paths are unpredictable
- # on Windows. (long vs. 8.3 paths)
- assert t1.basename in [item.basename for item in s.unknown]
- finally:
- t1.localpath.remove()
-
- def test_status_unchanged(self, path1):
- r = path1
- s = path1.status(rec=1)
- # Comparing just the file names, because paths are unpredictable
- # on Windows. (long vs. 8.3 paths)
- assert r.join('samplefile').basename in [item.basename
- for item in s.unchanged]
- assert r.join('sampledir').basename in [item.basename
- for item in s.unchanged]
- assert r.join('sampledir/otherfile').basename in [item.basename
- for item in s.unchanged]
-
- @pytest.mark.xfail(reason="svn-1.7 has buggy 'status --xml' output")
- def test_status_update(self, path1):
- r = path1
- try:
- r.update(rev=1)
- s = r.status(updates=1, rec=1)
- # Comparing just the file names, because paths are unpredictable
- # on Windows. (long vs. 8.3 paths)
- py.std.pprint.pprint(s.allpath())
- assert r.join('anotherfile').basename in [item.basename for
- item in s.update_available]
- #assert len(s.update_available) == 1
- finally:
- r.update()
-
- def test_status_replaced(self, path1):
- p = path1.join("samplefile")
- p.remove()
- p.ensure(dir=0)
- try:
- s = path1.status()
- assert p.basename in [item.basename for item in s.replaced]
- finally:
- path1.revert(rec=1)
-
- def test_status_ignored(self, path1):
- try:
- d = path1.join('sampledir')
- p = py.path.local(d).join('ignoredfile')
- p.ensure(file=True)
- s = d.status()
- assert [x.basename for x in s.unknown] == ['ignoredfile']
- assert [x.basename for x in s.ignored] == []
- d.propset('svn:ignore', 'ignoredfile')
- s = d.status()
- assert [x.basename for x in s.unknown] == []
- assert [x.basename for x in s.ignored] == ['ignoredfile']
- finally:
- path1.revert(rec=1)
-
- def test_status_conflict(self, path1, tmpdir):
- wc = path1
- wccopy = py.path.svnwc(tmpdir.join("conflict_copy"))
- wccopy.checkout(wc.url)
- p = wc.ensure('conflictsamplefile', file=1)
- p.write('foo')
- wc.commit('added conflictsamplefile')
- wccopy.update()
- assert wccopy.join('conflictsamplefile').check()
- p.write('bar')
- wc.commit('wrote some data')
- wccopy.join('conflictsamplefile').write('baz')
- wccopy.update(interactive=False)
- s = wccopy.status()
- assert [x.basename for x in s.conflict] == ['conflictsamplefile']
-
- def test_status_external(self, path1, repowc2):
- otherrepo, otherrepourl, otherwc = repowc2
- d = path1.ensure('sampledir', dir=1)
- try:
- d.update()
- d.propset('svn:externals', 'otherwc %s' % (otherwc.url,))
- d.update()
- s = d.status()
- assert [x.basename for x in s.external] == ['otherwc']
- assert 'otherwc' not in [x.basename for x in s.unchanged]
- s = d.status(rec=1)
- assert [x.basename for x in s.external] == ['otherwc']
- assert 'otherwc' in [x.basename for x in s.unchanged]
- finally:
- path1.revert(rec=1)
-
- def test_status_deleted(self, path1):
- d = path1.ensure('sampledir', dir=1)
- d.remove()
- d.ensure(dir=1)
- path1.commit()
- d.ensure('deletefile', dir=0)
- d.commit()
- s = d.status()
- assert 'deletefile' in [x.basename for x in s.unchanged]
- assert not s.deleted
- p = d.join('deletefile')
- p.remove()
- s = d.status()
- assert 'deletefile' not in s.unchanged
- assert [x.basename for x in s.deleted] == ['deletefile']
-
- def test_status_noauthor(self, path1):
- # testing for XML without author - this used to raise an exception
- xml = '''\
- <entry path="/tmp/pytest-23/wc">
- <wc-status item="normal" props="none" revision="0">
- <commit revision="0">
- <date>2008-08-19T16:50:53.400198Z</date>
- </commit>
- </wc-status>
- </entry>
- '''
- XMLWCStatus.fromstring(xml, path1)
-
- def test_status_wrong_xml(self, path1):
- # testing for XML without author - this used to raise an exception
- xml = '<entry path="/home/jean/zope/venv/projectdb/parts/development-products/DataGridField">\n<wc-status item="incomplete" props="none" revision="784">\n</wc-status>\n</entry>'
- st = XMLWCStatus.fromstring(xml, path1)
- assert len(st.incomplete) == 1
-
- def test_diff(self, path1):
- p = path1 / 'anotherfile'
- out = p.diff(rev=2)
- assert out.find('hello') != -1
-
- def test_blame(self, path1):
- p = path1.join('samplepickle')
- lines = p.blame()
- assert sum([l[0] for l in lines]) == len(lines)
- for l1, l2 in zip(p.readlines(), [l[2] for l in lines]):
- assert l1 == l2
- assert [l[1] for l in lines] == ['hpk'] * len(lines)
- p = path1.join('samplefile')
- lines = p.blame()
- assert sum([l[0] for l in lines]) == len(lines)
- for l1, l2 in zip(p.readlines(), [l[2] for l in lines]):
- assert l1 == l2
- assert [l[1] for l in lines] == ['hpk'] * len(lines)
-
- def test_join_abs(self, path1):
- s = str(path1.localpath)
- n = path1.join(s, abs=1)
- assert path1 == n
-
- def test_join_abs2(self, path1):
- assert path1.join('samplefile', abs=1) == path1.join('samplefile')
-
- def test_str_gives_localpath(self, path1):
- assert str(path1) == str(path1.localpath)
-
- def test_versioned(self, path1):
- assert path1.check(versioned=1)
- # TODO: Why does my copy of svn think .svn is versioned?
- #assert path1.join('.svn').check(versioned=0)
- assert path1.join('samplefile').check(versioned=1)
- assert not path1.join('notexisting').check(versioned=1)
- notexisting = path1.join('hello').localpath
- try:
- notexisting.write("")
- assert path1.join('hello').check(versioned=0)
- finally:
- notexisting.remove()
-
- def test_listdir_versioned(self, path1):
- assert path1.check(versioned=1)
- p = path1.localpath.ensure("not_a_versioned_file")
- l = [x.localpath
- for x in path1.listdir(lambda x: x.check(versioned=True))]
- assert p not in l
-
- def test_nonversioned_remove(self, path1):
- assert path1.check(versioned=1)
- somefile = path1.join('nonversioned/somefile')
- nonwc = py.path.local(somefile)
- nonwc.ensure()
- assert somefile.check()
- assert not somefile.check(versioned=True)
- somefile.remove() # this used to fail because it tried to 'svn rm'
-
- def test_properties(self, path1):
- try:
- path1.propset('gaga', 'this')
- assert path1.propget('gaga') == 'this'
- # Comparing just the file names, because paths are unpredictable
- # on Windows. (long vs. 8.3 paths)
- assert path1.basename in [item.basename for item in
- path1.status().prop_modified]
- assert 'gaga' in path1.proplist()
- assert path1.proplist()['gaga'] == 'this'
-
- finally:
- path1.propdel('gaga')
-
- def test_proplist_recursive(self, path1):
- s = path1.join('samplefile')
- s.propset('gugu', 'that')
- try:
- p = path1.proplist(rec=1)
- # Comparing just the file names, because paths are unpredictable
- # on Windows. (long vs. 8.3 paths)
- assert (path1 / 'samplefile').basename in [item.basename
- for item in p]
- finally:
- s.propdel('gugu')
-
- def test_long_properties(self, path1):
- value = """
- vadm:posix : root root 0100755
- Properties on 'chroot/dns/var/bind/db.net.xots':
- """
- try:
- path1.propset('gaga', value)
- backvalue = path1.propget('gaga')
- assert backvalue == value
- #assert len(backvalue.split('\n')) == 1
- finally:
- path1.propdel('gaga')
-
-
- def test_ensure(self, path1):
- newpath = path1.ensure('a', 'b', 'c')
- try:
- assert newpath.check(exists=1, versioned=1)
- newpath.write("hello")
- newpath.ensure()
- assert newpath.read() == "hello"
- finally:
- path1.join('a').remove(force=1)
-
- def test_not_versioned(self, path1):
- p = path1.localpath.mkdir('whatever')
- f = path1.localpath.ensure('testcreatedfile')
- try:
- assert path1.join('whatever').check(versioned=0)
- assert path1.join('testcreatedfile').check(versioned=0)
- assert not path1.join('testcreatedfile').check(versioned=1)
- finally:
- p.remove(rec=1)
- f.remove()
-
- def test_lock_unlock(self, path1):
- root = path1
- somefile = root.join('somefile')
- somefile.ensure(file=True)
- # not yet added to repo
- py.test.raises(Exception, 'somefile.lock()')
- somefile.write('foo')
- somefile.commit('test')
- assert somefile.check(versioned=True)
- somefile.lock()
- try:
- locked = root.status().locked
- assert len(locked) == 1
- assert locked[0].basename == somefile.basename
- assert locked[0].dirpath().basename == somefile.dirpath().basename
- #assert somefile.locked()
- py.test.raises(Exception, 'somefile.lock()')
- finally:
- somefile.unlock()
- #assert not somefile.locked()
- locked = root.status().locked
- assert locked == []
- py.test.raises(Exception, 'somefile,unlock()')
- somefile.remove()
-
- def test_commit_nonrecursive(self, path1):
- somedir = path1.join('sampledir')
- somedir.mkdir("subsubdir")
- somedir.propset('foo', 'bar')
- status = somedir.status()
- assert len(status.prop_modified) == 1
- assert len(status.added) == 1
-
- somedir.commit('non-recursive commit', rec=0)
- status = somedir.status()
- assert len(status.prop_modified) == 0
- assert len(status.added) == 1
-
- somedir.commit('recursive commit')
- status = somedir.status()
- assert len(status.prop_modified) == 0
- assert len(status.added) == 0
-
- def test_commit_return_value(self, path1):
- testfile = path1.join('test.txt').ensure(file=True)
- testfile.write('test')
- rev = path1.commit('testing')
- assert type(rev) == int
-
- anotherfile = path1.join('another.txt').ensure(file=True)
- anotherfile.write('test')
- rev2 = path1.commit('testing more')
- assert type(rev2) == int
- assert rev2 == rev + 1
-
- #def test_log(self, path1):
- # l = path1.log()
- # assert len(l) == 3 # might need to be upped if more tests are added
-
-class XTestWCSvnCommandPathSpecial:
-
- rooturl = 'http://codespeak.net/svn/py.path/trunk/dist/py.path/test/data'
- #def test_update_none_rev(self, path1):
- # path = tmpdir.join('checkouttest')
- # wcpath = newpath(xsvnwc=str(path), url=path1url)
- # try:
- # wcpath.checkout(rev=2100)
- # wcpath.update()
- # assert wcpath.info().rev > 2100
- # finally:
- # wcpath.localpath.remove(rec=1)
-
-def test_parse_wcinfotime():
- assert (parse_wcinfotime('2006-05-30 20:45:26 +0200 (Tue, 30 May 2006)') ==
- 1149021926)
- assert (parse_wcinfotime('2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003)') ==
- 1067287394)
-
-class TestInfoSvnWCCommand:
-
- def test_svn_1_2(self, path1):
- output = """
- Path: test_svnwc.py
- Name: test_svnwc.py
- URL: http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py
- Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada
- Revision: 28137
- Node Kind: file
- Schedule: normal
- Last Changed Author: jan
- Last Changed Rev: 27939
- Last Changed Date: 2006-05-30 20:45:26 +0200 (Tue, 30 May 2006)
- Text Last Updated: 2006-06-01 00:42:53 +0200 (Thu, 01 Jun 2006)
- Properties Last Updated: 2006-05-23 11:54:59 +0200 (Tue, 23 May 2006)
- Checksum: 357e44880e5d80157cc5fbc3ce9822e3
- """
- path = py.path.local(__file__).dirpath().chdir()
- try:
- info = InfoSvnWCCommand(output)
- finally:
- path.chdir()
- assert info.last_author == 'jan'
- assert info.kind == 'file'
- assert info.mtime == 1149021926.0
- assert info.url == 'http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py'
- assert info.time == 1149021926000000.0
- assert info.rev == 28137
-
-
- def test_svn_1_3(self, path1):
- output = """
- Path: test_svnwc.py
- Name: test_svnwc.py
- URL: http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py
- Repository Root: http://codespeak.net/svn
- Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada
- Revision: 28124
- Node Kind: file
- Schedule: normal
- Last Changed Author: jan
- Last Changed Rev: 27939
- Last Changed Date: 2006-05-30 20:45:26 +0200 (Tue, 30 May 2006)
- Text Last Updated: 2006-06-02 23:46:11 +0200 (Fri, 02 Jun 2006)
- Properties Last Updated: 2006-06-02 23:45:28 +0200 (Fri, 02 Jun 2006)
- Checksum: 357e44880e5d80157cc5fbc3ce9822e3
- """
- path = py.path.local(__file__).dirpath().chdir()
- try:
- info = InfoSvnWCCommand(output)
- finally:
- path.chdir()
- assert info.last_author == 'jan'
- assert info.kind == 'file'
- assert info.mtime == 1149021926.0
- assert info.url == 'http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py'
- assert info.rev == 28124
- assert info.time == 1149021926000000.0
-
-
-def test_characters_at():
- py.test.raises(ValueError, "py.path.svnwc('/tmp/@@@:')")
-
-def test_characters_tilde():
- py.path.svnwc('/tmp/test~')
-
-
-class TestRepo:
- def test_trailing_slash_is_stripped(self, path1):
- # XXX we need to test more normalizing properties
- url = path1.join("/")
- assert path1 == url
-
- #def test_different_revs_compare_unequal(self, path1):
- # newpath = path1.new(rev=1199)
- # assert newpath != path1
-
- def test_exists_svn_root(self, path1):
- assert path1.check()
-
- #def test_not_exists_rev(self, path1):
- # url = path1.__class__(path1url, rev=500)
- # assert url.check(exists=0)
-
- #def test_nonexisting_listdir_rev(self, path1):
- # url = path1.__class__(path1url, rev=500)
- # raises(py.error.ENOENT, url.listdir)
-
- #def test_newrev(self, path1):
- # url = path1.new(rev=None)
- # assert url.rev == None
- # assert url.strpath == path1.strpath
- # url = path1.new(rev=10)
- # assert url.rev == 10
-
- #def test_info_rev(self, path1):
- # url = path1.__class__(path1url, rev=1155)
- # url = url.join("samplefile")
- # res = url.info()
- # assert res.size > len("samplefile") and res.created_rev == 1155
-
- # the following tests are easier if we have a path class
- def test_repocache_simple(self, path1):
- repocache = svncommon.RepoCache()
- repocache.put(path1.strpath, 42)
- url, rev = repocache.get(path1.join('test').strpath)
- assert rev == 42
- assert url == path1.strpath
-
- def test_repocache_notimeout(self, path1):
- repocache = svncommon.RepoCache()
- repocache.timeout = 0
- repocache.put(path1.strpath, path1.rev)
- url, rev = repocache.get(path1.strpath)
- assert rev == -1
- assert url == path1.strpath
-
- def test_repocache_outdated(self, path1):
- repocache = svncommon.RepoCache()
- repocache.put(path1.strpath, 42, timestamp=0)
- url, rev = repocache.get(path1.join('test').strpath)
- assert rev == -1
- assert url == path1.strpath
-
- def _test_getreporev(self):
- """ this test runs so slow it's usually disabled """
- old = svncommon.repositories.repos
- try:
- _repocache.clear()
- root = path1.new(rev=-1)
- url, rev = cache.repocache.get(root.strpath)
- assert rev>=0
- assert url == svnrepourl
- finally:
- repositories.repos = old
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/process/test_cmdexec.py b/tests/wpt/web-platform-tests/tools/py/testing/process/test_cmdexec.py
deleted file mode 100644
index b539e0af381..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/process/test_cmdexec.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import py
-from py.process import cmdexec
-
-def exvalue():
- return py.std.sys.exc_info()[1]
-
-class Test_exec_cmd:
- def test_simple(self):
- out = cmdexec('echo hallo')
- assert out.strip() == 'hallo'
- assert py.builtin._istext(out)
-
- def test_simple_newline(self):
- import sys
- out = cmdexec(r"""%s -c "print ('hello')" """ % sys.executable)
- assert out == 'hello\n'
- assert py.builtin._istext(out)
-
- def test_simple_error(self):
- py.test.raises (cmdexec.Error, cmdexec, 'exit 1')
-
- def test_simple_error_exact_status(self):
- try:
- cmdexec('exit 1')
- except cmdexec.Error:
- e = exvalue()
- assert e.status == 1
- assert py.builtin._istext(e.out)
- assert py.builtin._istext(e.err)
-
- def test_err(self):
- try:
- cmdexec('echoqweqwe123 hallo')
- raise AssertionError("command succeeded but shouldn't")
- except cmdexec.Error:
- e = exvalue()
- assert hasattr(e, 'err')
- assert hasattr(e, 'out')
- assert e.err or e.out
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/process/test_forkedfunc.py b/tests/wpt/web-platform-tests/tools/py/testing/process/test_forkedfunc.py
deleted file mode 100644
index d4f9f985e0f..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/process/test_forkedfunc.py
+++ /dev/null
@@ -1,177 +0,0 @@
-import pytest
-import py, sys, os
-
-pytestmark = py.test.mark.skipif("not hasattr(os, 'fork')")
-
-
-def test_waitfinish_removes_tempdir():
- ff = py.process.ForkedFunc(boxf1)
- assert ff.tempdir.check()
- ff.waitfinish()
- assert not ff.tempdir.check()
-
-def test_tempdir_gets_gc_collected(monkeypatch):
- monkeypatch.setattr(os, 'fork', lambda: os.getpid())
- ff = py.process.ForkedFunc(boxf1)
- assert ff.tempdir.check()
- ff.__del__()
- assert not ff.tempdir.check()
-
-def test_basic_forkedfunc():
- result = py.process.ForkedFunc(boxf1).waitfinish()
- assert result.out == "some out\n"
- assert result.err == "some err\n"
- assert result.exitstatus == 0
- assert result.signal == 0
- assert result.retval == 1
-
-def test_exitstatus():
- def func():
- os._exit(4)
- result = py.process.ForkedFunc(func).waitfinish()
- assert result.exitstatus == 4
- assert result.signal == 0
- assert not result.out
- assert not result.err
-
-def test_execption_in_func():
- def fun():
- raise ValueError(42)
- ff = py.process.ForkedFunc(fun)
- result = ff.waitfinish()
- assert result.exitstatus == ff.EXITSTATUS_EXCEPTION
- assert result.err.find("ValueError: 42") != -1
- assert result.signal == 0
- assert not result.retval
-
-def test_forkedfunc_on_fds():
- result = py.process.ForkedFunc(boxf2).waitfinish()
- assert result.out == "someout"
- assert result.err == "someerr"
- assert result.exitstatus == 0
- assert result.signal == 0
- assert result.retval == 2
-
-def test_forkedfunc_on_fds_output():
- result = py.process.ForkedFunc(boxf3).waitfinish()
- assert result.signal == 11
- assert result.out == "s"
-
-
-def test_forkedfunc_on_stdout():
- def boxf3():
- import sys
- sys.stdout.write("hello\n")
- os.kill(os.getpid(), 11)
- result = py.process.ForkedFunc(boxf3).waitfinish()
- assert result.signal == 11
- assert result.out == "hello\n"
-
-def test_forkedfunc_signal():
- result = py.process.ForkedFunc(boxseg).waitfinish()
- assert result.retval is None
- if sys.version_info < (2,4):
- py.test.skip("signal detection does not work with python prior 2.4")
- assert result.signal == 11
-
-def test_forkedfunc_huge_data():
- result = py.process.ForkedFunc(boxhuge).waitfinish()
- assert result.out
- assert result.exitstatus == 0
- assert result.signal == 0
- assert result.retval == 3
-
-def test_box_seq():
- # we run many boxes with huge data, just one after another
- for i in range(50):
- result = py.process.ForkedFunc(boxhuge).waitfinish()
- assert result.out
- assert result.exitstatus == 0
- assert result.signal == 0
- assert result.retval == 3
-
-def test_box_in_a_box():
- def boxfun():
- result = py.process.ForkedFunc(boxf2).waitfinish()
- print (result.out)
- sys.stderr.write(result.err + "\n")
- return result.retval
-
- result = py.process.ForkedFunc(boxfun).waitfinish()
- assert result.out == "someout\n"
- assert result.err == "someerr\n"
- assert result.exitstatus == 0
- assert result.signal == 0
- assert result.retval == 2
-
-def test_kill_func_forked():
- class A:
- pass
- info = A()
- import time
-
- def box_fun():
- time.sleep(10) # we don't want to last forever here
-
- ff = py.process.ForkedFunc(box_fun)
- os.kill(ff.pid, 15)
- result = ff.waitfinish()
- if py.std.sys.version_info < (2,4):
- py.test.skip("signal detection does not work with python prior 2.4")
- assert result.signal == 15
-
-
-def test_hooks(monkeypatch):
- def _boxed():
- return 1
-
- def _on_start():
- sys.stdout.write("some out\n")
- sys.stdout.flush()
-
- def _on_exit():
- sys.stderr.write("some err\n")
- sys.stderr.flush()
-
- result = py.process.ForkedFunc(_boxed, child_on_start=_on_start,
- child_on_exit=_on_exit).waitfinish()
- assert result.out == "some out\n"
- assert result.err == "some err\n"
- assert result.exitstatus == 0
- assert result.signal == 0
- assert result.retval == 1
-
-
-# ======================================================================
-# examples
-# ======================================================================
-#
-
-def boxf1():
- sys.stdout.write("some out\n")
- sys.stderr.write("some err\n")
- return 1
-
-def boxf2():
- os.write(1, "someout".encode('ascii'))
- os.write(2, "someerr".encode('ascii'))
- return 2
-
-def boxf3():
- os.write(1, "s".encode('ascii'))
- os.kill(os.getpid(), 11)
-
-def boxseg():
- os.kill(os.getpid(), 11)
-
-def boxhuge():
- s = " ".encode('ascii')
- os.write(1, s * 10000)
- os.write(2, s * 10000)
- os.write(1, s * 10000)
-
- os.write(1, s * 10000)
- os.write(2, s * 10000)
- os.write(2, s * 10000)
- os.write(1, s * 10000)
- return 3
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/process/test_killproc.py b/tests/wpt/web-platform-tests/tools/py/testing/process/test_killproc.py
deleted file mode 100644
index 57088e1db8c..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/process/test_killproc.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-import py, sys
-
-@py.test.mark.skipif("sys.platform.startswith('java')")
-def test_kill(tmpdir):
- subprocess = py.test.importorskip("subprocess")
- t = tmpdir.join("t.py")
- t.write("import time ; time.sleep(100)")
- proc = py.std.subprocess.Popen([sys.executable, str(t)])
- assert proc.poll() is None # no return value yet
- py.process.kill(proc.pid)
- ret = proc.wait()
- if sys.platform == "win32" and ret == 0:
- py.test.skip("XXX on win32, subprocess.Popen().wait() on a killed "
- "process does not yield return value != 0")
- assert ret != 0
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/root/test_error.py b/tests/wpt/web-platform-tests/tools/py/testing/root/test_error.py
deleted file mode 100644
index a34e0068d92..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/root/test_error.py
+++ /dev/null
@@ -1,37 +0,0 @@
-
-import py
-
-import errno
-
-def test_error_classes():
- for name in errno.errorcode.values():
- x = getattr(py.error, name)
- assert issubclass(x, py.error.Error)
- assert issubclass(x, EnvironmentError)
-
-def test_picklability_issue1():
- e1 = py.error.ENOENT()
- s = py.std.pickle.dumps(e1)
- e2 = py.std.pickle.loads(s)
- assert isinstance(e2, py.error.ENOENT)
-
-def test_unknown_error():
- num = 3999
- cls = py.error._geterrnoclass(num)
- assert cls.__name__ == 'UnknownErrno%d' % (num,)
- assert issubclass(cls, py.error.Error)
- assert issubclass(cls, EnvironmentError)
- cls2 = py.error._geterrnoclass(num)
- assert cls is cls2
-
-def test_error_conversion_ENOTDIR(testdir):
- p = testdir.makepyfile("")
- excinfo = py.test.raises(py.error.Error, py.error.checked_call, p.listdir)
- assert isinstance(excinfo.value, EnvironmentError)
- assert isinstance(excinfo.value, py.error.Error)
- assert "ENOTDIR" in repr(excinfo.value)
-
-
-def test_checked_call_supports_kwargs(tmpdir):
- import tempfile
- py.error.checked_call(tempfile.mkdtemp, dir=str(tmpdir))
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/root/test_py_imports.py b/tests/wpt/web-platform-tests/tools/py/testing/root/test_py_imports.py
deleted file mode 100644
index 5f5954e9943..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/root/test_py_imports.py
+++ /dev/null
@@ -1,68 +0,0 @@
-import py
-import types
-import sys
-
-def checksubpackage(name):
- obj = getattr(py, name)
- if hasattr(obj, '__map__'): # isinstance(obj, Module):
- keys = dir(obj)
- assert len(keys) > 0
- print (obj.__map__)
- for name in list(obj.__map__):
- assert hasattr(obj, name), (obj, name)
-
-def test_dir():
- for name in dir(py):
- if not name.startswith('_'):
- yield checksubpackage, name
-
-def test_virtual_module_identity():
- from py import path as path1
- from py import path as path2
- assert path1 is path2
- from py.path import local as local1
- from py.path import local as local2
- assert local1 is local2
-
-def test_importall():
- base = py._pydir
- nodirs = [
- ]
- if sys.version_info >= (3,0):
- nodirs.append(base.join('_code', '_assertionold.py'))
- else:
- nodirs.append(base.join('_code', '_assertionnew.py'))
-
- def recurse(p):
- return p.check(dotfile=0) and p.basename != "attic"
-
- for p in base.visit('*.py', recurse):
- if p.basename == '__init__.py':
- continue
- relpath = p.new(ext='').relto(base)
- if base.sep in relpath: # not py/*.py itself
- for x in nodirs:
- if p == x or p.relto(x):
- break
- else:
- relpath = relpath.replace(base.sep, '.')
- modpath = 'py.%s' % relpath
- try:
- check_import(modpath)
- except py.test.skip.Exception:
- pass
-
-def check_import(modpath):
- py.builtin.print_("checking import", modpath)
- assert __import__(modpath)
-
-def test_all_resolves():
- seen = py.builtin.set([py])
- lastlength = None
- while len(seen) != lastlength:
- lastlength = len(seen)
- for item in py.builtin.frozenset(seen):
- for value in item.__dict__.values():
- if isinstance(value, type(py.test)):
- seen.add(value)
-
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/root/test_xmlgen.py b/tests/wpt/web-platform-tests/tools/py/testing/root/test_xmlgen.py
deleted file mode 100644
index 704d1492cb3..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/root/test_xmlgen.py
+++ /dev/null
@@ -1,145 +0,0 @@
-
-import py
-from py._xmlgen import unicode, html, raw
-
-class ns(py.xml.Namespace):
- pass
-
-def test_escape():
- uvalue = py.builtin._totext('\xc4\x85\xc4\x87\n\xe2\x82\xac\n', 'utf-8')
- class A:
- def __unicode__(self):
- return uvalue
- def __str__(self):
- x = self.__unicode__()
- if py.std.sys.version_info[0] < 3:
- return x.encode('utf-8')
- return x
- y = py.xml.escape(uvalue)
- assert y == uvalue
- x = py.xml.escape(A())
- assert x == uvalue
- if py.std.sys.version_info[0] < 3:
- assert isinstance(x, unicode)
- assert isinstance(y, unicode)
- y = py.xml.escape(uvalue.encode('utf-8'))
- assert y == uvalue
-
-
-def test_tag_with_text():
- x = ns.hello("world")
- u = unicode(x)
- assert u == "<hello>world</hello>"
-
-def test_class_identity():
- assert ns.hello is ns.hello
-
-def test_tag_with_text_and_attributes():
- x = ns.some(name="hello", value="world")
- assert x.attr.name == 'hello'
- assert x.attr.value == 'world'
- u = unicode(x)
- assert u == '<some name="hello" value="world"/>'
-
-def test_tag_with_subclassed_attr_simple():
- class my(ns.hello):
- class Attr(ns.hello.Attr):
- hello="world"
- x = my()
- assert x.attr.hello == 'world'
- assert unicode(x) == '<my hello="world"/>'
-
-def test_tag_with_raw_attr():
- x = html.object(data=raw('&'))
- assert unicode(x) == '<object data="&"></object>'
-
-def test_tag_nested():
- x = ns.hello(ns.world())
- unicode(x) # triggers parentifying
- assert x[0].parent is x
- u = unicode(x)
- assert u == '<hello><world/></hello>'
-
-def test_list_nested():
- x = ns.hello([ns.world()]) #pass in a list here
- u = unicode(x)
- assert u == '<hello><world/></hello>'
-
-def test_tag_xmlname():
- class my(ns.hello):
- xmlname = 'world'
- u = unicode(my())
- assert u == '<world/>'
-
-def test_tag_with_text_entity():
- x = ns.hello('world & rest')
- u = unicode(x)
- assert u == "<hello>world &amp; rest</hello>"
-
-def test_tag_with_text_and_attributes_entity():
- x = ns.some(name="hello & world")
- assert x.attr.name == "hello & world"
- u = unicode(x)
- assert u == '<some name="hello &amp; world"/>'
-
-def test_raw():
- x = ns.some(py.xml.raw("<p>literal</p>"))
- u = unicode(x)
- assert u == "<some><p>literal</p></some>"
-
-
-def test_html_name_stickyness():
- class my(html.p):
- pass
- x = my("hello")
- assert unicode(x) == '<p>hello</p>'
-
-def test_stylenames():
- class my:
- class body(html.body):
- style = html.Style(font_size = "12pt")
- u = unicode(my.body())
- assert u == '<body style="font-size: 12pt"></body>'
-
-def test_class_None():
- t = html.body(class_=None)
- u = unicode(t)
- assert u == '<body></body>'
-
-def test_alternating_style():
- alternating = (
- html.Style(background="white"),
- html.Style(background="grey"),
- )
- class my(html):
- class li(html.li):
- def style(self):
- i = self.parent.index(self)
- return alternating[i%2]
- style = property(style)
-
- x = my.ul(
- my.li("hello"),
- my.li("world"),
- my.li("42"))
- u = unicode(x)
- assert u == ('<ul><li style="background: white">hello</li>'
- '<li style="background: grey">world</li>'
- '<li style="background: white">42</li>'
- '</ul>')
-
-def test_singleton():
- h = html.head(html.link(href="foo"))
- assert unicode(h) == '<head><link href="foo"/></head>'
-
- h = html.head(html.script(src="foo"))
- assert unicode(h) == '<head><script src="foo"></script></head>'
-
-def test_inline():
- h = html.div(html.span('foo'), html.span('bar'))
- assert (h.unicode(indent=2) ==
- '<div><span>foo</span><span>bar</span></div>')
-
-def test_object_tags():
- o = html.object(html.object())
- assert o.unicode(indent=0) == '<object><object></object></object>'
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/test_iniconfig.py b/tests/wpt/web-platform-tests/tools/py/testing/test_iniconfig.py
deleted file mode 100644
index 9a7f72c11b3..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/testing/test_iniconfig.py
+++ /dev/null
@@ -1,299 +0,0 @@
-import py
-import pytest
-from py._iniconfig import IniConfig, ParseError, __all__ as ALL
-from py._iniconfig import iscommentline
-from textwrap import dedent
-
-def pytest_generate_tests(metafunc):
- if 'input' in metafunc.funcargnames:
- for name, (input, expected) in check_tokens.items():
- metafunc.addcall(id=name, funcargs={
- 'input': input,
- 'expected': expected,
- })
- elif hasattr(metafunc.function, 'multi'):
- kwargs = metafunc.function.multi.kwargs
- names, values = zip(*kwargs.items())
- values = cartesian_product(*values)
- for p in values:
- metafunc.addcall(funcargs=dict(zip(names, p)))
-
-def cartesian_product(L,*lists):
- # copied from http://bit.ly/cyIXjn
- if not lists:
- for x in L:
- yield (x,)
- else:
- for x in L:
- for y in cartesian_product(lists[0],*lists[1:]):
- yield (x,)+y
-
-check_tokens = {
- 'section': (
- '[section]',
- [(0, 'section', None, None)]
- ),
- 'value': (
- 'value = 1',
- [(0, None, 'value', '1')]
- ),
- 'value in section': (
- '[section]\nvalue=1',
- [(0, 'section', None, None), (1, 'section', 'value', '1')]
- ),
- 'value with continuation': (
- 'names =\n Alice\n Bob',
- [(0, None, 'names', 'Alice\nBob')]
- ),
- 'value with aligned continuation': (
- 'names = Alice\n'
- ' Bob',
- [(0, None, 'names', 'Alice\nBob')]
- ),
- 'blank line':(
- '[section]\n\nvalue=1',
- [(0, 'section', None, None), (2, 'section', 'value', '1')]
- ),
- 'comment': (
- '# comment',
- []
- ),
- 'comment on value': (
- 'value = 1',
- [(0, None, 'value', '1')]
- ),
-
- 'comment on section': (
- '[section] #comment',
- [(0, 'section', None, None)]
- ),
- 'comment2': (
- '; comment',
- []
- ),
-
- 'comment2 on section': (
- '[section] ;comment',
- [(0, 'section', None, None)]
- ),
- 'pseudo section syntax in value': (
- 'name = value []',
- [(0, None, 'name', 'value []')]
- ),
- 'assignment in value': (
- 'value = x = 3',
- [(0, None, 'value', 'x = 3')]
- ),
- 'use of colon for name-values': (
- 'name: y',
- [(0, None, 'name', 'y')]
- ),
- 'use of colon without space': (
- 'value:y=5',
- [(0, None, 'value', 'y=5')]
- ),
- 'equality gets precedence': (
- 'value=xyz:5',
- [(0, None, 'value', 'xyz:5')]
- ),
-
-}
-
-def parse(input):
- # only for testing purposes - _parse() does not use state except path
- ini = object.__new__(IniConfig)
- ini.path = "sample"
- return ini._parse(input.splitlines(True))
-
-def parse_a_error(input):
- return py.test.raises(ParseError, parse, input)
-
-def test_tokenize(input, expected):
- parsed = parse(input)
- assert parsed == expected
-
-def test_parse_empty():
- parsed = parse("")
- assert not parsed
- ini = IniConfig("sample", "")
- assert not ini.sections
-
-def test_ParseError():
- e = ParseError("filename", 0, "hello")
- assert str(e) == "filename:1: hello"
-
-def test_continuation_needs_perceeding_token():
- excinfo = parse_a_error(' Foo')
- assert excinfo.value.lineno == 0
-
-def test_continuation_cant_be_after_section():
- excinfo = parse_a_error('[section]\n Foo')
- assert excinfo.value.lineno == 1
-
-def test_section_cant_be_empty():
- excinfo = parse_a_error('[]')
-
-@py.test.mark.multi(line=[
- '!!',
- ])
-def test_error_on_weird_lines(line):
- parse_a_error(line)
-
-def test_iniconfig_from_file(tmpdir):
- path = tmpdir/'test.txt'
- path.write('[metadata]\nname=1')
-
- config = IniConfig(path=path)
- assert list(config.sections) == ['metadata']
- config = IniConfig(path, "[diff]")
- assert list(config.sections) == ['diff']
- py.test.raises(TypeError, "IniConfig(data=path.read())")
-
-def test_iniconfig_section_first(tmpdir):
- excinfo = py.test.raises(ParseError, """
- IniConfig("x", data='name=1')
- """)
- assert excinfo.value.msg == "no section header defined"
-
-def test_iniconig_section_duplicate_fails():
- excinfo = py.test.raises(ParseError, r"""
- IniConfig("x", data='[section]\n[section]')
- """)
- assert 'duplicate section' in str(excinfo.value)
-
-def test_iniconfig_duplicate_key_fails():
- excinfo = py.test.raises(ParseError, r"""
- IniConfig("x", data='[section]\nname = Alice\nname = bob')
- """)
-
- assert 'duplicate name' in str(excinfo.value)
-
-def test_iniconfig_lineof():
- config = IniConfig("x.ini", data=
- '[section]\n'
- 'value = 1\n'
- '[section2]\n'
- '# comment\n'
- 'value =2'
- )
-
- assert config.lineof('missing') is None
- assert config.lineof('section') == 1
- assert config.lineof('section2') == 3
- assert config.lineof('section', 'value') == 2
- assert config.lineof('section2','value') == 5
-
- assert config['section'].lineof('value') == 2
- assert config['section2'].lineof('value') == 5
-
-def test_iniconfig_get_convert():
- config= IniConfig("x", data='[section]\nint = 1\nfloat = 1.1')
- assert config.get('section', 'int') == '1'
- assert config.get('section', 'int', convert=int) == 1
-
-def test_iniconfig_get_missing():
- config= IniConfig("x", data='[section]\nint = 1\nfloat = 1.1')
- assert config.get('section', 'missing', default=1) == 1
- assert config.get('section', 'missing') is None
-
-def test_section_get():
- config = IniConfig("x", data='[section]\nvalue=1')
- section = config['section']
- assert section.get('value', convert=int) == 1
- assert section.get('value', 1) == "1"
- assert section.get('missing', 2) == 2
-
-def test_missing_section():
- config = IniConfig("x", data='[section]\nvalue=1')
- py.test.raises(KeyError,'config["other"]')
-
-def test_section_getitem():
- config = IniConfig("x", data='[section]\nvalue=1')
- assert config['section']['value'] == '1'
- assert config['section']['value'] == '1'
-
-def test_section_iter():
- config = IniConfig("x", data='[section]\nvalue=1')
- names = list(config['section'])
- assert names == ['value']
- items = list(config['section'].items())
- assert items==[('value', '1')]
-
-def test_config_iter():
- config = IniConfig("x.ini", data=dedent('''
- [section1]
- value=1
- [section2]
- value=2
- '''))
- l = list(config)
- assert len(l) == 2
- assert l[0].name == 'section1'
- assert l[0]['value'] == '1'
- assert l[1].name == 'section2'
- assert l[1]['value'] == '2'
-
-def test_config_contains():
- config = IniConfig("x.ini", data=dedent('''
- [section1]
- value=1
- [section2]
- value=2
- '''))
- assert 'xyz' not in config
- assert 'section1' in config
- assert 'section2' in config
-
-def test_iter_file_order():
- config = IniConfig("x.ini", data="""
-[section2] #cpython dict ordered before section
-value = 1
-value2 = 2 # dict ordered before value
-[section]
-a = 1
-b = 2
-""")
- l = list(config)
- secnames = [x.name for x in l]
- assert secnames == ['section2', 'section']
- assert list(config['section2']) == ['value', 'value2']
- assert list(config['section']) == ['a', 'b']
-
-def test_example_pypirc():
- config = IniConfig("pypirc", data=dedent('''
- [distutils]
- index-servers =
- pypi
- other
-
- [pypi]
- repository: <repository-url>
- username: <username>
- password: <password>
-
- [other]
- repository: http://example.com/pypi
- username: <username>
- password: <password>
- '''))
- distutils, pypi, other = list(config)
- assert distutils["index-servers"] == "pypi\nother"
- assert pypi['repository'] == '<repository-url>'
- assert pypi['username'] == '<username>'
- assert pypi['password'] == '<password>'
- assert ['repository', 'username', 'password'] == list(other)
-
-
-def test_api_import():
- assert ALL == ['IniConfig', 'ParseError']
-
-@pytest.mark.parametrize("line", [
- "#qwe",
- " #qwe",
- ";qwe",
- " ;qwe",
-])
-def test_iscommentline_true(line):
- assert iscommentline(line)
-
-
diff --git a/tests/wpt/web-platform-tests/tools/py/tox.ini b/tests/wpt/web-platform-tests/tools/py/tox.ini
deleted file mode 100644
index 8c0c79d6960..00000000000
--- a/tests/wpt/web-platform-tests/tools/py/tox.ini
+++ /dev/null
@@ -1,39 +0,0 @@
-[tox]
-envlist=py26,py27,py33,py34,external
-# py27-xdist causes problems with svn, py25 requires virtualenv==1.9.1
-#indexserver=
-# default=http://pypi.testrun.org
-
-[testenv]
-changedir=testing
-commands=py.test --confcutdir=.. -rfsxX --junitxml={envlogdir}/junit-{envname}.xml []
-deps=pytest
-
-[testenv:py27-xdist]
-basepython=python2.7
-deps=
- pytest
- pytest-xdist
-commands=
- py.test -n3 -rfsxX --confcutdir=.. --runslowtests \
- --junitxml={envlogdir}/junit-{envname}.xml []
-
-[testenv:jython]
-changedir=testing
-commands=
- {envpython} -m pytest --confcutdir=.. -rfsxX --junitxml={envlogdir}/junit-{envname}0.xml {posargs:io_ code}
-
-[testenv:py25]
-setenv = PIP_INSECURE=1
-
-[testenv:external]
-deps=
- pytest
- jinja2
- decorator
-commands=
- py.test --confcutdir=.. -rfsxX --junitxml={envlogdir}/junit-{envname}.xml {posargs:code}
-
-[pytest]
-rsyncdirs = conftest.py py doc testing
-addopts = -rxXf
diff --git a/tests/wpt/web-platform-tests/tools/pytest.ini b/tests/wpt/web-platform-tests/tools/pytest.ini
index e1ad451c8b3..ecef9b7173d 100644
--- a/tests/wpt/web-platform-tests/tools/pytest.ini
+++ b/tests/wpt/web-platform-tests/tools/pytest.ini
@@ -1,2 +1,2 @@
[pytest]
-norecursedirs = .* {arch} *.egg html5lib py pytest pywebsocket six wpt wptrunner
+norecursedirs = .* {arch} *.egg html5lib third_party pywebsocket six wpt wptrunner
diff --git a/tests/wpt/web-platform-tests/tools/pytest/.coveragerc b/tests/wpt/web-platform-tests/tools/pytest/.coveragerc
deleted file mode 100644
index 27db64e09c1..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/.coveragerc
+++ /dev/null
@@ -1,7 +0,0 @@
-[run]
-omit =
- # standlonetemplate is read dynamically and tested by test_genscript
- *standalonetemplate.py
- # oldinterpret could be removed, as it is no longer used in py26+
- *oldinterpret.py
- vendored_packages
diff --git a/tests/wpt/web-platform-tests/tools/pytest/.github/ISSUE_TEMPLATE.md b/tests/wpt/web-platform-tests/tools/pytest/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index bc62e8a3f7d..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Thanks for submitting an issue!
-
-Here's a quick checklist in what to include:
-
-- [ ] Include a detailed description of the bug or suggestion
-- [ ] `pip list` of the virtual environment you are using
-- [ ] py.test and operating system versions
-- [ ] Minimal example if possible
diff --git a/tests/wpt/web-platform-tests/tools/pytest/.github/PULL_REQUEST_TEMPLATE.md b/tests/wpt/web-platform-tests/tools/pytest/.github/PULL_REQUEST_TEMPLATE.md
deleted file mode 100644
index d09edce43d3..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/.github/PULL_REQUEST_TEMPLATE.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Thanks for submitting a PR, your contribution is really appreciated!
-
-Here's a quick checklist that should be present in PRs:
-
-- [ ] Target: for bug or doc fixes, target `master`; for new features, target `features`
-- [ ] Make sure to include one or more tests for your change
-- [ ] Add yourself to `AUTHORS`
-- [ ] Add a new entry to the `CHANGELOG` (choose any open position to avoid merge conflicts with other PRs)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/.gitignore b/tests/wpt/web-platform-tests/tools/pytest/.gitignore
deleted file mode 100644
index e4355b859cc..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/.gitignore
+++ /dev/null
@@ -1,34 +0,0 @@
-# Automatically generated by `hgimportsvn`
-.svn
-.hgsvn
-
-# Ignore local virtualenvs
-lib/
-bin/
-include/
-.Python/
-
-# These lines are suggested according to the svn:ignore property
-# Feel free to enable them by uncommenting them
-*.pyc
-*.pyo
-*.swp
-*.class
-*.orig
-*~
-
-.eggs/
-
-doc/*/_build
-build/
-dist/
-*.egg-info
-issue/
-env/
-.env/
-3rdparty/
-.tox
-.cache
-.coverage
-.ropeproject
-.idea
diff --git a/tests/wpt/web-platform-tests/tools/pytest/.travis.yml b/tests/wpt/web-platform-tests/tools/pytest/.travis.yml
deleted file mode 100644
index 3a8f36e95f8..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/.travis.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-sudo: false
-language: python
-python:
- - '3.5'
-# command to install dependencies
-install: "pip install -U tox"
-# # command to run tests
-env:
- matrix:
- # coveralls is not listed in tox's envlist, but should run in travis
- - TESTENV=coveralls
- # note: please use "tox --listenvs" to populate the build matrix below
- - TESTENV=linting
- - TESTENV=py26
- - TESTENV=py27
- - TESTENV=py33
- - TESTENV=py34
- - TESTENV=py35
- - TESTENV=pypy
- - TESTENV=py27-pexpect
- - TESTENV=py27-xdist
- - TESTENV=py27-trial
- - TESTENV=py35-pexpect
- - TESTENV=py35-xdist
- - TESTENV=py35-trial
- - TESTENV=py27-nobyte
- - TESTENV=doctesting
- - TESTENV=py27-cxfreeze
-
-script: tox --recreate -e $TESTENV
-
-notifications:
- irc:
- channels:
- - "chat.freenode.net#pytest"
- on_success: change
- on_failure: change
- skip_join: true
- email:
- - pytest-commit@python.org
diff --git a/tests/wpt/web-platform-tests/tools/pytest/AUTHORS b/tests/wpt/web-platform-tests/tools/pytest/AUTHORS
deleted file mode 100644
index dfc0a542ec5..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/AUTHORS
+++ /dev/null
@@ -1,87 +0,0 @@
-Holger Krekel, holger at merlinux eu
-merlinux GmbH, Germany, office at merlinux eu
-
-Contributors include::
-
-Abhijeet Kasurde
-Anatoly Bubenkoff
-Andreas Zeidler
-Andy Freeland
-Anthon van der Neut
-Armin Rigo
-Aron Curzon
-Aviv Palivoda
-Benjamin Peterson
-Bob Ippolito
-Brian Dorsey
-Brian Okken
-Brianna Laugher
-Bruno Oliveira
-Carl Friedrich Bolz
-Charles Cloud
-Chris Lamb
-Christian Theunert
-Christian Tismer
-Christopher Gilling
-Daniel Grana
-Daniel Hahler
-Daniel Nuri
-Dave Hunt
-David Mohr
-David Vierra
-Edison Gustavo Muenz
-Eduardo Schettino
-Endre Galaczi
-Elizaveta Shashkova
-Eric Hunsberger
-Eric Siegerman
-Erik M. Bray
-Florian Bruhin
-Floris Bruynooghe
-Gabriel Reis
-Georgy Dyuldin
-Graham Horler
-Grig Gheorghiu
-Guido Wesdorp
-Harald Armin Massa
-Ian Bicking
-Jaap Broekhuizen
-Jan Balster
-Janne Vanhala
-Jason R. Coombs
-Joshua Bronson
-Jurko Gospodnetić
-Katarzyna Jachim
-Kevin Cox
-Lee Kamentsky
-Lukas Bednar
-Maciek Fijalkowski
-Maho
-Marc Schlaich
-Mark Abramowitz
-Markus Unterwaditzer
-Martijn Faassen
-Matt Bachmann
-Michael Aquilina
-Michael Birtwell
-Michael Droettboom
-Nicolas Delaby
-Pieter Mulder
-Piotr Banaszkiewicz
-Punyashloka Biswal
-Ralf Schmitt
-Raphael Pierzina
-Ronny Pfannschmidt
-Ross Lawley
-Ryan Wooden
-Samuele Pedroni
-Tom Viner
-Trevor Bekolay
-Wouter van Ackooy
-David Díaz-Barquero
-Eric Hunsberger
-Simon Gomizelj
-Russel Winder
-Ben Webb
-Alexei Kozlenok
-Cal Leeming
diff --git a/tests/wpt/web-platform-tests/tools/pytest/CHANGELOG.rst b/tests/wpt/web-platform-tests/tools/pytest/CHANGELOG.rst
deleted file mode 100644
index f18f646f008..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/CHANGELOG.rst
+++ /dev/null
@@ -1,2586 +0,0 @@
-2.9.1
-=====
-
-**Bug Fixes**
-
-* Improve error message when a plugin fails to load.
- Thanks `@nicoddemus`_ for the PR.
-
-* Fix (`#1178 <https://github.com/pytest-dev/pytest/issues/1178>`_):
- ``pytest.fail`` with non-ascii characters raises an internal pytest error.
- Thanks `@nicoddemus`_ for the PR.
-
-* Fix (`#469`_): junit parses report.nodeid incorrectly, when params IDs
- contain ``::``. Thanks `@tomviner`_ for the PR (`#1431`_).
-
-* Fix (`#578 <https://github.com/pytest-dev/pytest/issues/578>`_): SyntaxErrors
- containing non-ascii lines at the point of failure generated an internal
- py.test error.
- Thanks `@asottile`_ for the report and `@nicoddemus`_ for the PR.
-
-* Fix (`#1437`_): When passing in a bytestring regex pattern to parameterize
- attempt to decode it as utf-8 ignoring errors.
-
-* Fix (`#649`_): parametrized test nodes cannot be specified to run on the command line.
-
-
-.. _#1437: https://github.com/pytest-dev/pytest/issues/1437
-.. _#469: https://github.com/pytest-dev/pytest/issues/469
-.. _#1431: https://github.com/pytest-dev/pytest/pull/1431
-.. _#649: https://github.com/pytest-dev/pytest/issues/649
-
-.. _@asottile: https://github.com/asottile
-
-
-2.9.0
-=====
-
-**New Features**
-
-* New ``pytest.mark.skip`` mark, which unconditionally skips marked tests.
- Thanks `@MichaelAquilina`_ for the complete PR (`#1040`_).
-
-* ``--doctest-glob`` may now be passed multiple times in the command-line.
- Thanks `@jab`_ and `@nicoddemus`_ for the PR.
-
-* New ``-rp`` and ``-rP`` reporting options give the summary and full output
- of passing tests, respectively. Thanks to `@codewarrior0`_ for the PR.
-
-* ``pytest.mark.xfail`` now has a ``strict`` option, which makes ``XPASS``
- tests to fail the test suite (defaulting to ``False``). There's also a
- ``xfail_strict`` ini option that can be used to configure it project-wise.
- Thanks `@rabbbit`_ for the request and `@nicoddemus`_ for the PR (`#1355`_).
-
-* ``Parser.addini`` now supports options of type ``bool``.
- Thanks `@nicoddemus`_ for the PR.
-
-* New ``ALLOW_BYTES`` doctest option. This strips ``b`` prefixes from byte strings
- in doctest output (similar to ``ALLOW_UNICODE``).
- Thanks `@jaraco`_ for the request and `@nicoddemus`_ for the PR (`#1287`_).
-
-* Give a hint on ``KeyboardInterrupt`` to use the ``--fulltrace`` option to show the errors.
- Fixes `#1366`_.
- Thanks to `@hpk42`_ for the report and `@RonnyPfannschmidt`_ for the PR.
-
-* Catch ``IndexError`` exceptions when getting exception source location.
- Fixes a pytest internal error for dynamically generated code (fixtures and tests)
- where source lines are fake by intention.
-
-**Changes**
-
-* **Important**: `py.code <http://pylib.readthedocs.org/en/latest/code.html>`_ has been
- merged into the ``pytest`` repository as ``pytest._code``. This decision
- was made because ``py.code`` had very few uses outside ``pytest`` and the
- fact that it was in a different repository made it difficult to fix bugs on
- its code in a timely manner. The team hopes with this to be able to better
- refactor out and improve that code.
- This change shouldn't affect users, but it is useful to let users aware
- if they encounter any strange behavior.
-
- Keep in mind that the code for ``pytest._code`` is **private** and
- **experimental**, so you definitely should not import it explicitly!
-
- Please note that the original ``py.code`` is still available in
- `pylib <http://pylib.readthedocs.org>`_.
-
-* ``pytest_enter_pdb`` now optionally receives the pytest config object.
- Thanks `@nicoddemus`_ for the PR.
-
-* Removed code and documentation for Python 2.5 or lower versions,
- including removal of the obsolete ``_pytest.assertion.oldinterpret`` module.
- Thanks `@nicoddemus`_ for the PR (`#1226`_).
-
-* Comparisons now always show up in full when ``CI`` or ``BUILD_NUMBER`` is
- found in the environment, even when ``-vv`` isn't used.
- Thanks `@The-Compiler`_ for the PR.
-
-* ``--lf`` and ``--ff`` now support long names: ``--last-failed`` and
- ``--failed-first`` respectively.
- Thanks `@MichaelAquilina`_ for the PR.
-
-* Added expected exceptions to ``pytest.raises`` fail message.
-
-* Collection only displays progress ("collecting X items") when in a terminal.
- This avoids cluttering the output when using ``--color=yes`` to obtain
- colors in CI integrations systems (`#1397`_).
-
-**Bug Fixes**
-
-* The ``-s`` and ``-c`` options should now work under ``xdist``;
- ``Config.fromdictargs`` now represents its input much more faithfully.
- Thanks to `@bukzor`_ for the complete PR (`#680`_).
-
-* Fix (`#1290`_): support Python 3.5's ``@`` operator in assertion rewriting.
- Thanks `@Shinkenjoe`_ for report with test case and `@tomviner`_ for the PR.
-
-* Fix formatting utf-8 explanation messages (`#1379`_).
- Thanks `@biern`_ for the PR.
-
-* Fix `traceback style docs`_ to describe all of the available options
- (auto/long/short/line/native/no), with `auto` being the default since v2.6.
- Thanks `@hackebrot`_ for the PR.
-
-* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records
- with same name.
-
-.. _`traceback style docs`: https://pytest.org/latest/usage.html#modifying-python-traceback-printing
-
-.. _#1422: https://github.com/pytest-dev/pytest/issues/1422
-.. _#1379: https://github.com/pytest-dev/pytest/issues/1379
-.. _#1366: https://github.com/pytest-dev/pytest/issues/1366
-.. _#1040: https://github.com/pytest-dev/pytest/pull/1040
-.. _#680: https://github.com/pytest-dev/pytest/issues/680
-.. _#1287: https://github.com/pytest-dev/pytest/pull/1287
-.. _#1226: https://github.com/pytest-dev/pytest/pull/1226
-.. _#1290: https://github.com/pytest-dev/pytest/pull/1290
-.. _#1355: https://github.com/pytest-dev/pytest/pull/1355
-.. _#1397: https://github.com/pytest-dev/pytest/issues/1397
-.. _@biern: https://github.com/biern
-.. _@MichaelAquilina: https://github.com/MichaelAquilina
-.. _@bukzor: https://github.com/bukzor
-.. _@hpk42: https://github.com/hpk42
-.. _@nicoddemus: https://github.com/nicoddemus
-.. _@jab: https://github.com/jab
-.. _@codewarrior0: https://github.com/codewarrior0
-.. _@jaraco: https://github.com/jaraco
-.. _@The-Compiler: https://github.com/The-Compiler
-.. _@Shinkenjoe: https://github.com/Shinkenjoe
-.. _@tomviner: https://github.com/tomviner
-.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
-.. _@rabbbit: https://github.com/rabbbit
-.. _@hackebrot: https://github.com/hackebrot
-
-2.8.7
-=====
-
-- fix #1338: use predictable object resolution for monkeypatch
-
-2.8.6
-=====
-
-- fix #1259: allow for double nodeids in junitxml,
- this was a regression failing plugins combinations
- like pytest-pep8 + pytest-flakes
-
-- Workaround for exception that occurs in pyreadline when using
- ``--pdb`` with standard I/O capture enabled.
- Thanks Erik M. Bray for the PR.
-
-- fix #900: Better error message in case the target of a ``monkeypatch`` call
- raises an ``ImportError``.
-
-- fix #1292: monkeypatch calls (setattr, setenv, etc.) are now O(1).
- Thanks David R. MacIver for the report and Bruno Oliveira for the PR.
-
-- fix #1223: captured stdout and stderr are now properly displayed before
- entering pdb when ``--pdb`` is used instead of being thrown away.
- Thanks Cal Leeming for the PR.
-
-- fix #1305: pytest warnings emitted during ``pytest_terminal_summary`` are now
- properly displayed.
- Thanks Ionel Maries Cristian for the report and Bruno Oliveira for the PR.
-
-- fix #628: fixed internal UnicodeDecodeError when doctests contain unicode.
- Thanks Jason R. Coombs for the report and Bruno Oliveira for the PR.
-
-- fix #1334: Add captured stdout to jUnit XML report on setup error.
- Thanks Georgy Dyuldin for the PR.
-
-
-2.8.5
-=====
-
-- fix #1243: fixed issue where class attributes injected during collection could break pytest.
- PR by Alexei Kozlenok, thanks Ronny Pfannschmidt and Bruno Oliveira for the review and help.
-
-- fix #1074: precompute junitxml chunks instead of storing the whole tree in objects
- Thanks Bruno Oliveira for the report and Ronny Pfannschmidt for the PR
-
-- fix #1238: fix ``pytest.deprecated_call()`` receiving multiple arguments
- (Regression introduced in 2.8.4). Thanks Alex Gaynor for the report and
- Bruno Oliveira for the PR.
-
-
-2.8.4
-=====
-
-- fix #1190: ``deprecated_call()`` now works when the deprecated
- function has been already called by another test in the same
- module. Thanks Mikhail Chernykh for the report and Bruno Oliveira for the
- PR.
-
-- fix #1198: ``--pastebin`` option now works on Python 3. Thanks
- Mehdy Khoshnoody for the PR.
-
-- fix #1219: ``--pastebin`` now works correctly when captured output contains
- non-ascii characters. Thanks Bruno Oliveira for the PR.
-
-- fix #1204: another error when collecting with a nasty __getattr__().
- Thanks Florian Bruhin for the PR.
-
-- fix the summary printed when no tests did run.
- Thanks Florian Bruhin for the PR.
-- fix #1185 - ensure MANIFEST.in exactly matches what should go to a sdist
-
-- a number of documentation modernizations wrt good practices.
- Thanks Bruno Oliveira for the PR.
-
-2.8.3
-=====
-
-- fix #1169: add __name__ attribute to testcases in TestCaseFunction to
- support the @unittest.skip decorator on functions and methods.
- Thanks Lee Kamentsky for the PR.
-
-- fix #1035: collecting tests if test module level obj has __getattr__().
- Thanks Suor for the report and Bruno Oliveira / Tom Viner for the PR.
-
-- fix #331: don't collect tests if their failure cannot be reported correctly
- e.g. they are a callable instance of a class.
-
-- fix #1133: fixed internal error when filtering tracebacks where one entry
- belongs to a file which is no longer available.
- Thanks Bruno Oliveira for the PR.
-
-- enhancement made to highlight in red the name of the failing tests so
- they stand out in the output.
- Thanks Gabriel Reis for the PR.
-
-- add more talks to the documentation
-- extend documentation on the --ignore cli option
-- use pytest-runner for setuptools integration
-- minor fixes for interaction with OS X El Capitan
- system integrity protection (thanks Florian)
-
-
-2.8.2
-=====
-
-- fix #1085: proper handling of encoding errors when passing encoded byte
- strings to pytest.parametrize in Python 2.
- Thanks Themanwithoutaplan for the report and Bruno Oliveira for the PR.
-
-- fix #1087: handling SystemError when passing empty byte strings to
- pytest.parametrize in Python 3.
- Thanks Paul Kehrer for the report and Bruno Oliveira for the PR.
-
-- fix #995: fixed internal error when filtering tracebacks where one entry
- was generated by an exec() statement.
- Thanks Daniel Hahler, Ashley C Straw, Philippe Gauthier and Pavel Savchenko
- for contributing and Bruno Oliveira for the PR.
-
-- fix #1100 and #1057: errors when using autouse fixtures and doctest modules.
- Thanks Sergey B Kirpichev and Vital Kudzelka for contributing and Bruno
- Oliveira for the PR.
-
-2.8.1
-=====
-
-- fix #1034: Add missing nodeid on pytest_logwarning call in
- addhook. Thanks Simon Gomizelj for the PR.
-
-- 'deprecated_call' is now only satisfied with a DeprecationWarning or
- PendingDeprecationWarning. Before 2.8.0, it accepted any warning, and 2.8.0
- made it accept only DeprecationWarning (but not PendingDeprecationWarning).
- Thanks Alex Gaynor for the issue and Eric Hunsberger for the PR.
-
-- fix issue #1073: avoid calling __getattr__ on potential plugin objects.
- This fixes an incompatibility with pytest-django. Thanks Andreas Pelme,
- Bruno Oliveira and Ronny Pfannschmidt for contributing and Holger Krekel
- for the fix.
-
-- Fix issue #704: handle versionconflict during plugin loading more
- gracefully. Thanks Bruno Oliveira for the PR.
-
-- Fix issue #1064: ""--junitxml" regression when used with the
- "pytest-xdist" plugin, with test reports being assigned to the wrong tests.
- Thanks Daniel Grunwald for the report and Bruno Oliveira for the PR.
-
-- (experimental) adapt more SEMVER style versioning and change meaning of
- master branch in git repo: "master" branch now keeps the bugfixes, changes
- aimed for micro releases. "features" branch will only be be released
- with minor or major pytest releases.
-
-- Fix issue #766 by removing documentation references to distutils.
- Thanks Russel Winder.
-
-- Fix issue #1030: now byte-strings are escaped to produce item node ids
- to make them always serializable.
- Thanks Andy Freeland for the report and Bruno Oliveira for the PR.
-
-- Python 2: if unicode parametrized values are convertible to ascii, their
- ascii representation is used for the node id.
-
-- Fix issue #411: Add __eq__ method to assertion comparison example.
- Thanks Ben Webb.
-- Fix issue #653: deprecated_call can be used as context manager.
-
-- fix issue 877: properly handle assertion explanations with non-ascii repr
- Thanks Mathieu Agopian for the report and Ronny Pfannschmidt for the PR.
-
-- fix issue 1029: transform errors when writing cache values into pytest-warnings
-
-2.8.0
-=====
-
-- new ``--lf`` and ``-ff`` options to run only the last failing tests or
- "failing tests first" from the last run. This functionality is provided
- through porting the formerly external pytest-cache plugin into pytest core.
- BACKWARD INCOMPAT: if you used pytest-cache's functionality to persist
- data between test runs be aware that we don't serialize sets anymore.
- Thanks Ronny Pfannschmidt for most of the merging work.
-
-- "-r" option now accepts "a" to include all possible reports, similar
- to passing "fEsxXw" explicitly (isse960).
- Thanks Abhijeet Kasurde for the PR.
-
-- avoid python3.5 deprecation warnings by introducing version
- specific inspection helpers, thanks Michael Droettboom.
-
-- fix issue562: @nose.tools.istest now fully respected.
-
-- fix issue934: when string comparison fails and a diff is too large to display
- without passing -vv, still show a few lines of the diff.
- Thanks Florian Bruhin for the report and Bruno Oliveira for the PR.
-
-- fix issue736: Fix a bug where fixture params would be discarded when combined
- with parametrization markers.
- Thanks to Markus Unterwaditzer for the PR.
-
-- fix issue710: introduce ALLOW_UNICODE doctest option: when enabled, the
- ``u`` prefix is stripped from unicode strings in expected doctest output. This
- allows doctests which use unicode to run in Python 2 and 3 unchanged.
- Thanks Jason R. Coombs for the report and Bruno Oliveira for the PR.
-
-- parametrize now also generates meaningful test IDs for enum, regex and class
- objects (as opposed to class instances).
- Thanks to Florian Bruhin for the PR.
-
-- Add 'warns' to assert that warnings are thrown (like 'raises').
- Thanks to Eric Hunsberger for the PR.
-
-- Fix issue683: Do not apply an already applied mark. Thanks ojake for the PR.
-
-- Deal with capturing failures better so fewer exceptions get lost to
- /dev/null. Thanks David Szotten for the PR.
-
-- fix issue730: deprecate and warn about the --genscript option.
- Thanks Ronny Pfannschmidt for the report and Christian Pommranz for the PR.
-
-- fix issue751: multiple parametrize with ids bug if it parametrizes class with
- two or more test methods. Thanks Sergey Chipiga for reporting and Jan
- Bednarik for PR.
-
-- fix issue82: avoid loading conftest files from setup.cfg/pytest.ini/tox.ini
- files and upwards by default (--confcutdir can still be set to override this).
- Thanks Bruno Oliveira for the PR.
-
-- fix issue768: docstrings found in python modules were not setting up session
- fixtures. Thanks Jason R. Coombs for reporting and Bruno Oliveira for the PR.
-
-- added ``tmpdir_factory``, a session-scoped fixture that can be used to create
- directories under the base temporary directory. Previously this object was
- installed as a ``_tmpdirhandler`` attribute of the ``config`` object, but now it
- is part of the official API and using ``config._tmpdirhandler`` is
- deprecated.
- Thanks Bruno Oliveira for the PR.
-
-- fix issue808: pytest's internal assertion rewrite hook now implements the
- optional PEP302 get_data API so tests can access data files next to them.
- Thanks xmo-odoo for request and example and Bruno Oliveira for
- the PR.
-
-- rootdir and inifile are now displayed during usage errors to help
- users diagnose problems such as unexpected ini files which add
- unknown options being picked up by pytest. Thanks to Pavel Savchenko for
- bringing the problem to attention in #821 and Bruno Oliveira for the PR.
-
-- Summary bar now is colored yellow for warning
- situations such as: all tests either were skipped or xpass/xfailed,
- or no tests were run at all (this is a partial fix for issue500).
-
-- fix issue812: pytest now exits with status code 5 in situations where no
- tests were run at all, such as the directory given in the command line does
- not contain any tests or as result of a command line option filters
- all out all tests (-k for example).
- Thanks Eric Siegerman (issue812) and Bruno Oliveira for the PR.
-
-- Summary bar now is colored yellow for warning
- situations such as: all tests either were skipped or xpass/xfailed,
- or no tests were run at all (related to issue500).
- Thanks Eric Siegerman.
-
-- New ``testpaths`` ini option: list of directories to search for tests
- when executing pytest from the root directory. This can be used
- to speed up test collection when a project has well specified directories
- for tests, being usually more practical than configuring norecursedirs for
- all directories that do not contain tests.
- Thanks to Adrian for idea (#694) and Bruno Oliveira for the PR.
-
-- fix issue713: JUnit XML reports for doctest failures.
- Thanks Punyashloka Biswal.
-
-- fix issue970: internal pytest warnings now appear as "pytest-warnings" in
- the terminal instead of "warnings", so it is clear for users that those
- warnings are from pytest and not from the builtin "warnings" module.
- Thanks Bruno Oliveira.
-
-- Include setup and teardown in junitxml test durations.
- Thanks Janne Vanhala.
-
-- fix issue735: assertion failures on debug versions of Python 3.4+
-
-- new option ``--import-mode`` to allow to change test module importing
- behaviour to append to sys.path instead of prepending. This better allows
- to run test modules against installated versions of a package even if the
- package under test has the same import root. In this example::
-
- testing/__init__.py
- testing/test_pkg_under_test.py
- pkg_under_test/
-
- the tests will run against the installed version
- of pkg_under_test when ``--import-mode=append`` is used whereas
- by default they would always pick up the local version. Thanks Holger Krekel.
-
-- pytester: add method ``TmpTestdir.delete_loaded_modules()``, and call it
- from ``inline_run()`` to allow temporary modules to be reloaded.
- Thanks Eduardo Schettino.
-
-- internally refactor pluginmanager API and code so that there
- is a clear distinction between a pytest-agnostic rather simple
- pluginmanager and the PytestPluginManager which adds a lot of
- behaviour, among it handling of the local conftest files.
- In terms of documented methods this is a backward compatible
- change but it might still break 3rd party plugins which relied on
- details like especially the pluginmanager.add_shutdown() API.
- Thanks Holger Krekel.
-
-- pluginmanagement: introduce ``pytest.hookimpl`` and
- ``pytest.hookspec`` decorators for setting impl/spec
- specific parameters. This substitutes the previous
- now deprecated use of ``pytest.mark`` which is meant to
- contain markers for test functions only.
-
-- write/refine docs for "writing plugins" which now have their
- own page and are separate from the "using/installing plugins`` page.
-
-- fix issue732: properly unregister plugins from any hook calling
- sites allowing to have temporary plugins during test execution.
-
-- deprecate and warn about ``__multicall__`` argument in hook
- implementations. Use the ``hookwrapper`` mechanism instead already
- introduced with pytest-2.7.
-
-- speed up pytest's own test suite considerably by using inprocess
- tests by default (testrun can be modified with --runpytest=subprocess
- to create subprocesses in many places instead). The main
- APIs to run pytest in a test is "runpytest()" or "runpytest_subprocess"
- and "runpytest_inprocess" if you need a particular way of running
- the test. In all cases you get back a RunResult but the inprocess
- one will also have a "reprec" attribute with the recorded events/reports.
-
-- fix monkeypatch.setattr("x.y", raising=False) to actually not raise
- if "y" is not a pre-existing attribute. Thanks Florian Bruhin.
-
-- fix issue741: make running output from testdir.run copy/pasteable
- Thanks Bruno Oliveira.
-
-- add a new ``--noconftest`` argument which ignores all ``conftest.py`` files.
-
-- add ``file`` and ``line`` attributes to JUnit-XML output.
-
-- fix issue890: changed extension of all documentation files from ``txt`` to
- ``rst``. Thanks to Abhijeet for the PR.
-
-- fix issue714: add ability to apply indirect=True parameter on particular argnames.
- Thanks Elizaveta239.
-
-- fix issue890: changed extension of all documentation files from ``txt`` to
- ``rst``. Thanks to Abhijeet for the PR.
-
-- fix issue957: "# doctest: SKIP" option will now register doctests as SKIPPED
- rather than PASSED.
- Thanks Thomas Grainger for the report and Bruno Oliveira for the PR.
-
-- issue951: add new record_xml_property fixture, that supports logging
- additional information on xml output. Thanks David Diaz for the PR.
-
-- issue949: paths after normal options (for example ``-s``, ``-v``, etc) are now
- properly used to discover ``rootdir`` and ``ini`` files.
- Thanks Peter Lauri for the report and Bruno Oliveira for the PR.
-
-2.7.3 (compared to 2.7.2)
-=============================
-
-- Allow 'dev', 'rc', or other non-integer version strings in ``importorskip``.
- Thanks to Eric Hunsberger for the PR.
-
-- fix issue856: consider --color parameter in all outputs (for example
- --fixtures). Thanks Barney Gale for the report and Bruno Oliveira for the PR.
-
-- fix issue855: passing str objects as ``plugins`` argument to pytest.main
- is now interpreted as a module name to be imported and registered as a
- plugin, instead of silently having no effect.
- Thanks xmo-odoo for the report and Bruno Oliveira for the PR.
-
-- fix issue744: fix for ast.Call changes in Python 3.5+. Thanks
- Guido van Rossum, Matthias Bussonnier, Stefan Zimmermann and
- Thomas Kluyver.
-
-- fix issue842: applying markers in classes no longer propagate this markers
- to superclasses which also have markers.
- Thanks xmo-odoo for the report and Bruno Oliveira for the PR.
-
-- preserve warning functions after call to pytest.deprecated_call. Thanks
- Pieter Mulder for PR.
-
-- fix issue854: autouse yield_fixtures defined as class members of
- unittest.TestCase subclasses now work as expected.
- Thannks xmo-odoo for the report and Bruno Oliveira for the PR.
-
-- fix issue833: --fixtures now shows all fixtures of collected test files, instead of just the
- fixtures declared on the first one.
- Thanks Florian Bruhin for reporting and Bruno Oliveira for the PR.
-
-- fix issue863: skipped tests now report the correct reason when a skip/xfail
- condition is met when using multiple markers.
- Thanks Raphael Pierzina for reporting and Bruno Oliveira for the PR.
-
-- optimized tmpdir fixture initialization, which should make test sessions
- faster (specially when using pytest-xdist). The only visible effect
- is that now pytest uses a subdirectory in the $TEMP directory for all
- directories created by this fixture (defaults to $TEMP/pytest-$USER).
- Thanks Bruno Oliveira for the PR.
-
-2.7.2 (compared to 2.7.1)
-=============================
-
-- fix issue767: pytest.raises value attribute does not contain the exception
- instance on Python 2.6. Thanks Eric Siegerman for providing the test
- case and Bruno Oliveira for PR.
-
-- Automatically create directory for junitxml and results log.
- Thanks Aron Curzon.
-
-- fix issue713: JUnit XML reports for doctest failures.
- Thanks Punyashloka Biswal.
-
-- fix issue735: assertion failures on debug versions of Python 3.4+
- Thanks Benjamin Peterson.
-
-- fix issue114: skipif marker reports to internal skipping plugin;
- Thanks Floris Bruynooghe for reporting and Bruno Oliveira for the PR.
-
-- fix issue748: unittest.SkipTest reports to internal pytest unittest plugin.
- Thanks Thomas De Schampheleire for reporting and Bruno Oliveira for the PR.
-
-- fix issue718: failed to create representation of sets containing unsortable
- elements in python 2. Thanks Edison Gustavo Muenz.
-
-- fix issue756, fix issue752 (and similar issues): depend on py-1.4.29
- which has a refined algorithm for traceback generation.
-
-
-2.7.1 (compared to 2.7.0)
-=============================
-
-- fix issue731: do not get confused by the braces which may be present
- and unbalanced in an object's repr while collapsing False
- explanations. Thanks Carl Meyer for the report and test case.
-
-- fix issue553: properly handling inspect.getsourcelines failures in
- FixtureLookupError which would lead to to an internal error,
- obfuscating the original problem. Thanks talljosh for initial
- diagnose/patch and Bruno Oliveira for final patch.
-
-- fix issue660: properly report scope-mismatch-access errors
- independently from ordering of fixture arguments. Also
- avoid the pytest internal traceback which does not provide
- information to the user. Thanks Holger Krekel.
-
-- streamlined and documented release process. Also all versions
- (in setup.py and documentation generation) are now read
- from _pytest/__init__.py. Thanks Holger Krekel.
-
-- fixed docs to remove the notion that yield-fixtures are experimental.
- They are here to stay :) Thanks Bruno Oliveira.
-
-- Support building wheels by using environment markers for the
- requirements. Thanks Ionel Maries Cristian.
-
-- fixed regression to 2.6.4 which surfaced e.g. in lost stdout capture printing
- when tests raised SystemExit. Thanks Holger Krekel.
-
-- reintroduced _pytest fixture of the pytester plugin which is used
- at least by pytest-xdist.
-
-2.7.0 (compared to 2.6.4)
-=============================
-
-- fix issue435: make reload() work when assert rewriting is active.
- Thanks Daniel Hahler.
-
-- fix issue616: conftest.py files and their contained fixutres are now
- properly considered for visibility, independently from the exact
- current working directory and test arguments that are used.
- Many thanks to Eric Siegerman and his PR235 which contains
- systematic tests for conftest visibility and now passes.
- This change also introduces the concept of a ``rootdir`` which
- is printed as a new pytest header and documented in the pytest
- customize web page.
-
-- change reporting of "diverted" tests, i.e. tests that are collected
- in one file but actually come from another (e.g. when tests in a test class
- come from a base class in a different file). We now show the nodeid
- and indicate via a postfix the other file.
-
-- add ability to set command line options by environment variable PYTEST_ADDOPTS.
-
-- added documentation on the new pytest-dev teams on bitbucket and
- github. See https://pytest.org/latest/contributing.html .
- Thanks to Anatoly for pushing and initial work on this.
-
-- fix issue650: new option ``--docttest-ignore-import-errors`` which
- will turn import errors in doctests into skips. Thanks Charles Cloud
- for the complete PR.
-
-- fix issue655: work around different ways that cause python2/3
- to leak sys.exc_info into fixtures/tests causing failures in 3rd party code
-
-- fix issue615: assertion re-writing did not correctly escape % signs
- when formatting boolean operations, which tripped over mixing
- booleans with modulo operators. Thanks to Tom Viner for the report,
- triaging and fix.
-
-- implement issue351: add ability to specify parametrize ids as a callable
- to generate custom test ids. Thanks Brianna Laugher for the idea and
- implementation.
-
-- introduce and document new hookwrapper mechanism useful for plugins
- which want to wrap the execution of certain hooks for their purposes.
- This supersedes the undocumented ``__multicall__`` protocol which
- pytest itself and some external plugins use. Note that pytest-2.8
- is scheduled to drop supporting the old ``__multicall__``
- and only support the hookwrapper protocol.
-
-- majorly speed up invocation of plugin hooks
-
-- use hookwrapper mechanism in builtin pytest plugins.
-
-- add a doctest ini option for doctest flags, thanks Holger Peters.
-
-- add note to docs that if you want to mark a parameter and the
- parameter is a callable, you also need to pass in a reason to disambiguate
- it from the "decorator" case. Thanks Tom Viner.
-
-- "python_classes" and "python_functions" options now support glob-patterns
- for test discovery, as discussed in issue600. Thanks Ldiary Translations.
-
-- allow to override parametrized fixtures with non-parametrized ones and vice versa (bubenkoff).
-
-- fix issue463: raise specific error for 'parameterize' misspelling (pfctdayelise).
-
-- On failure, the ``sys.last_value``, ``sys.last_type`` and
- ``sys.last_traceback`` are set, so that a user can inspect the error
- via postmortem debugging (almarklein).
-
-2.6.4
-=====
-
-- Improve assertion failure reporting on iterables, by using ndiff and
- pprint.
-
-- removed outdated japanese docs from source tree.
-
-- docs for "pytest_addhooks" hook. Thanks Bruno Oliveira.
-
-- updated plugin index docs. Thanks Bruno Oliveira.
-
-- fix issue557: with "-k" we only allow the old style "-" for negation
- at the beginning of strings and even that is deprecated. Use "not" instead.
- This should allow to pick parametrized tests where "-" appeared in the parameter.
-
-- fix issue604: Escape % character in the assertion message.
-
-- fix issue620: add explanation in the --genscript target about what
- the binary blob means. Thanks Dinu Gherman.
-
-- fix issue614: fixed pastebin support.
-
-
-- fix issue620: add explanation in the --genscript target about what
- the binary blob means. Thanks Dinu Gherman.
-
-- fix issue614: fixed pastebin support.
-
-2.6.3
-=====
-
-- fix issue575: xunit-xml was reporting collection errors as failures
- instead of errors, thanks Oleg Sinyavskiy.
-
-- fix issue582: fix setuptools example, thanks Laszlo Papp and Ronny
- Pfannschmidt.
-
-- Fix infinite recursion bug when pickling capture.EncodedFile, thanks
- Uwe Schmitt.
-
-- fix issue589: fix bad interaction with numpy and others when showing
- exceptions. Check for precise "maximum recursion depth exceed" exception
- instead of presuming any RuntimeError is that one (implemented in py
- dep). Thanks Charles Cloud for analysing the issue.
-
-- fix conftest related fixture visibility issue: when running with a
- CWD outside a test package pytest would get fixture discovery wrong.
- Thanks to Wolfgang Schnerring for figuring out a reproducable example.
-
-- Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the
- timeout when interactively entering pdb). Thanks Wolfgang Schnerring.
-
-- check xfail/skip also with non-python function test items. Thanks
- Floris Bruynooghe.
-
-2.6.2
-=====
-
-- Added function pytest.freeze_includes(), which makes it easy to embed
- pytest into executables using tools like cx_freeze.
- See docs for examples and rationale. Thanks Bruno Oliveira.
-
-- Improve assertion rewriting cache invalidation precision.
-
-- fixed issue561: adapt autouse fixture example for python3.
-
-- fixed issue453: assertion rewriting issue with __repr__ containing
- "\n{", "\n}" and "\n~".
-
-- fix issue560: correctly display code if an "else:" or "finally:" is
- followed by statements on the same line.
-
-- Fix example in monkeypatch documentation, thanks t-8ch.
-
-- fix issue572: correct tmpdir doc example for python3.
-
-- Do not mark as universal wheel because Python 2.6 is different from
- other builds due to the extra argparse dependency. Fixes issue566.
- Thanks sontek.
-
-- Implement issue549: user-provided assertion messages now no longer
- replace the py.test introspection message but are shown in addition
- to them.
-
-2.6.1
-=====
-
-- No longer show line numbers in the --verbose output, the output is now
- purely the nodeid. The line number is still shown in failure reports.
- Thanks Floris Bruynooghe.
-
-- fix issue437 where assertion rewriting could cause pytest-xdist slaves
- to collect different tests. Thanks Bruno Oliveira.
-
-- fix issue555: add "errors" attribute to capture-streams to satisfy
- some distutils and possibly other code accessing sys.stdout.errors.
-
-- fix issue547 capsys/capfd also work when output capturing ("-s") is disabled.
-
-- address issue170: allow pytest.mark.xfail(...) to specify expected exceptions via
- an optional "raises=EXC" argument where EXC can be a single exception
- or a tuple of exception classes. Thanks David Mohr for the complete
- PR.
-
-- fix integration of pytest with unittest.mock.patch decorator when
- it uses the "new" argument. Thanks Nicolas Delaby for test and PR.
-
-- fix issue with detecting conftest files if the arguments contain
- "::" node id specifications (copy pasted from "-v" output)
-
-- fix issue544 by only removing "@NUM" at the end of "::" separated parts
- and if the part has an ".py" extension
-
-- don't use py.std import helper, rather import things directly.
- Thanks Bruno Oliveira.
-
-2.6
-===
-
-- Cache exceptions from fixtures according to their scope (issue 467).
-
-- fix issue537: Avoid importing old assertion reinterpretation code by default.
-
-- fix issue364: shorten and enhance tracebacks representation by default.
- The new "--tb=auto" option (default) will only display long tracebacks
- for the first and last entry. You can get the old behaviour of printing
- all entries as long entries with "--tb=long". Also short entries by
- default are now printed very similarly to "--tb=native" ones.
-
-- fix issue514: teach assertion reinterpretation about private class attributes
-
-- change -v output to include full node IDs of tests. Users can copy
- a node ID from a test run, including line number, and use it as a
- positional argument in order to run only a single test.
-
-- fix issue 475: fail early and comprehensible if calling
- pytest.raises with wrong exception type.
-
-- fix issue516: tell in getting-started about current dependencies.
-
-- cleanup setup.py a bit and specify supported versions. Thanks Jurko
- Gospodnetic for the PR.
-
-- change XPASS colour to yellow rather then red when tests are run
- with -v.
-
-- fix issue473: work around mock putting an unbound method into a class
- dict when double-patching.
-
-- fix issue498: if a fixture finalizer fails, make sure that
- the fixture is still invalidated.
-
-- fix issue453: the result of the pytest_assertrepr_compare hook now gets
- it's newlines escaped so that format_exception does not blow up.
-
-- internal new warning system: pytest will now produce warnings when
- it detects oddities in your test collection or execution.
- Warnings are ultimately sent to a new pytest_logwarning hook which is
- currently only implemented by the terminal plugin which displays
- warnings in the summary line and shows more details when -rw (report on
- warnings) is specified.
-
-- change skips into warnings for test classes with an __init__ and
- callables in test modules which look like a test but are not functions.
-
-- fix issue436: improved finding of initial conftest files from command
- line arguments by using the result of parse_known_args rather than
- the previous flaky heuristics. Thanks Marc Abramowitz for tests
- and initial fixing approaches in this area.
-
-- fix issue #479: properly handle nose/unittest(2) SkipTest exceptions
- during collection/loading of test modules. Thanks to Marc Schlaich
- for the complete PR.
-
-- fix issue490: include pytest_load_initial_conftests in documentation
- and improve docstring.
-
-- fix issue472: clarify that ``pytest.config.getvalue()`` cannot work
- if it's triggered ahead of command line parsing.
-
-- merge PR123: improved integration with mock.patch decorator on tests.
-
-- fix issue412: messing with stdout/stderr FD-level streams is now
- captured without crashes.
-
-- fix issue483: trial/py33 works now properly. Thanks Daniel Grana for PR.
-
-- improve example for pytest integration with "python setup.py test"
- which now has a generic "-a" or "--pytest-args" option where you
- can pass additional options as a quoted string. Thanks Trevor Bekolay.
-
-- simplified internal capturing mechanism and made it more robust
- against tests or setups changing FD1/FD2, also better integrated
- now with pytest.pdb() in single tests.
-
-- improvements to pytest's own test-suite leakage detection, courtesy of PRs
- from Marc Abramowitz
-
-- fix issue492: avoid leak in test_writeorg. Thanks Marc Abramowitz.
-
-- fix issue493: don't run tests in doc directory with ``python setup.py test``
- (use tox -e doctesting for that)
-
-- fix issue486: better reporting and handling of early conftest loading failures
-
-- some cleanup and simplification of internal conftest handling.
-
-- work a bit harder to break reference cycles when catching exceptions.
- Thanks Jurko Gospodnetic.
-
-- fix issue443: fix skip examples to use proper comparison. Thanks Alex
- Groenholm.
-
-- support nose-style ``__test__`` attribute on modules, classes and
- functions, including unittest-style Classes. If set to False, the
- test will not be collected.
-
-- fix issue512: show "<notset>" for arguments which might not be set
- in monkeypatch plugin. Improves output in documentation.
-
-
-2.5.2
-=====
-
-- fix issue409 -- better interoperate with cx_freeze by not
- trying to import from collections.abc which causes problems
- for py27/cx_freeze. Thanks Wolfgang L. for reporting and tracking it down.
-
-- fixed docs and code to use "pytest" instead of "py.test" almost everywhere.
- Thanks Jurko Gospodnetic for the complete PR.
-
-- fix issue425: mention at end of "py.test -h" that --markers
- and --fixtures work according to specified test path (or current dir)
-
-- fix issue413: exceptions with unicode attributes are now printed
- correctly also on python2 and with pytest-xdist runs. (the fix
- requires py-1.4.20)
-
-- copy, cleanup and integrate py.io capture
- from pylib 1.4.20.dev2 (rev 13d9af95547e)
-
-- address issue416: clarify docs as to conftest.py loading semantics
-
-- fix issue429: comparing byte strings with non-ascii chars in assert
- expressions now work better. Thanks Floris Bruynooghe.
-
-- make capfd/capsys.capture private, its unused and shouldnt be exposed
-
-
-2.5.1
-=====
-
-- merge new documentation styling PR from Tobias Bieniek.
-
-- fix issue403: allow parametrize of multiple same-name functions within
- a collection node. Thanks Andreas Kloeckner and Alex Gaynor for reporting
- and analysis.
-
-- Allow parameterized fixtures to specify the ID of the parameters by
- adding an ids argument to pytest.fixture() and pytest.yield_fixture().
- Thanks Floris Bruynooghe.
-
-- fix issue404 by always using the binary xml escape in the junitxml
- plugin. Thanks Ronny Pfannschmidt.
-
-- fix issue407: fix addoption docstring to point to argparse instead of
- optparse. Thanks Daniel D. Wright.
-
-
-
-2.5.0
-=====
-
-- dropped python2.5 from automated release testing of pytest itself
- which means it's probably going to break soon (but still works
- with this release we believe).
-
-- simplified and fixed implementation for calling finalizers when
- parametrized fixtures or function arguments are involved. finalization
- is now performed lazily at setup time instead of in the "teardown phase".
- While this might sound odd at first, it helps to ensure that we are
- correctly handling setup/teardown even in complex code. User-level code
- should not be affected unless it's implementing the pytest_runtest_teardown
- hook and expecting certain fixture instances are torn down within (very
- unlikely and would have been unreliable anyway).
-
-- PR90: add --color=yes|no|auto option to force terminal coloring
- mode ("auto" is default). Thanks Marc Abramowitz.
-
-- fix issue319 - correctly show unicode in assertion errors. Many
- thanks to Floris Bruynooghe for the complete PR. Also means
- we depend on py>=1.4.19 now.
-
-- fix issue396 - correctly sort and finalize class-scoped parametrized
- tests independently from number of methods on the class.
-
-- refix issue323 in a better way -- parametrization should now never
- cause Runtime Recursion errors because the underlying algorithm
- for re-ordering tests per-scope/per-fixture is not recursive
- anymore (it was tail-call recursive before which could lead
- to problems for more than >966 non-function scoped parameters).
-
-- fix issue290 - there is preliminary support now for parametrizing
- with repeated same values (sometimes useful to to test if calling
- a second time works as with the first time).
-
-- close issue240 - document precisely how pytest module importing
- works, discuss the two common test directory layouts, and how it
- interacts with PEP420-namespace packages.
-
-- fix issue246 fix finalizer order to be LIFO on independent fixtures
- depending on a parametrized higher-than-function scoped fixture.
- (was quite some effort so please bear with the complexity of this sentence :)
- Thanks Ralph Schmitt for the precise failure example.
-
-- fix issue244 by implementing special index for parameters to only use
- indices for paramentrized test ids
-
-- fix issue287 by running all finalizers but saving the exception
- from the first failing finalizer and re-raising it so teardown will
- still have failed. We reraise the first failing exception because
- it might be the cause for other finalizers to fail.
-
-- fix ordering when mock.patch or other standard decorator-wrappings
- are used with test methods. This fixues issue346 and should
- help with random "xdist" collection failures. Thanks to
- Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
-
-- fix issue357 - special case "-k" expressions to allow for
- filtering with simple strings that are not valid python expressions.
- Examples: "-k 1.3" matches all tests parametrized with 1.3.
- "-k None" filters all tests that have "None" in their name
- and conversely "-k 'not None'".
- Previously these examples would raise syntax errors.
-
-- fix issue384 by removing the trial support code
- since the unittest compat enhancements allow
- trial to handle it on its own
-
-- don't hide an ImportError when importing a plugin produces one.
- fixes issue375.
-
-- fix issue275 - allow usefixtures and autouse fixtures
- for running doctest text files.
-
-- fix issue380 by making --resultlog only rely on longrepr instead
- of the "reprcrash" attribute which only exists sometimes.
-
-- address issue122: allow @pytest.fixture(params=iterator) by exploding
- into a list early on.
-
-- fix pexpect-3.0 compatibility for pytest's own tests.
- (fixes issue386)
-
-- allow nested parametrize-value markers, thanks James Lan for the PR.
-
-- fix unicode handling with new monkeypatch.setattr(import_path, value)
- API. Thanks Rob Dennis. Fixes issue371.
-
-- fix unicode handling with junitxml, fixes issue368.
-
-- In assertion rewriting mode on Python 2, fix the detection of coding
- cookies. See issue #330.
-
-- make "--runxfail" turn imperative pytest.xfail calls into no ops
- (it already did neutralize pytest.mark.xfail markers)
-
-- refine pytest / pkg_resources interactions: The AssertionRewritingHook
- PEP302 compliant loader now registers itself with setuptools/pkg_resources
- properly so that the pkg_resources.resource_stream method works properly.
- Fixes issue366. Thanks for the investigations and full PR to Jason R. Coombs.
-
-- pytestconfig fixture is now session-scoped as it is the same object during the
- whole test run. Fixes issue370.
-
-- avoid one surprising case of marker malfunction/confusion::
-
- @pytest.mark.some(lambda arg: ...)
- def test_function():
-
- would not work correctly because pytest assumes @pytest.mark.some
- gets a function to be decorated already. We now at least detect if this
- arg is an lambda and thus the example will work. Thanks Alex Gaynor
- for bringing it up.
-
-- xfail a test on pypy that checks wrong encoding/ascii (pypy does
- not error out). fixes issue385.
-
-- internally make varnames() deal with classes's __init__,
- although it's not needed by pytest itself atm. Also
- fix caching. Fixes issue376.
-
-- fix issue221 - handle importing of namespace-package with no
- __init__.py properly.
-
-- refactor internal FixtureRequest handling to avoid monkeypatching.
- One of the positive user-facing effects is that the "request" object
- can now be used in closures.
-
-- fixed version comparison in pytest.importskip(modname, minverstring)
-
-- fix issue377 by clarifying in the nose-compat docs that pytest
- does not duplicate the unittest-API into the "plain" namespace.
-
-- fix verbose reporting for @mock'd test functions
-
-2.4.2
-=====
-
-- on Windows require colorama and a newer py lib so that py.io.TerminalWriter()
- now uses colorama instead of its own ctypes hacks. (fixes issue365)
- thanks Paul Moore for bringing it up.
-
-- fix "-k" matching of tests where "repr" and "attr" and other names would
- cause wrong matches because of an internal implementation quirk
- (don't ask) which is now properly implemented. fixes issue345.
-
-- avoid tmpdir fixture to create too long filenames especially
- when parametrization is used (issue354)
-
-- fix pytest-pep8 and pytest-flakes / pytest interactions
- (collection names in mark plugin was assuming an item always
- has a function which is not true for those plugins etc.)
- Thanks Andi Zeidler.
-
-- introduce node.get_marker/node.add_marker API for plugins
- like pytest-pep8 and pytest-flakes to avoid the messy
- details of the node.keywords pseudo-dicts. Adapted
- docs.
-
-- remove attempt to "dup" stdout at startup as it's icky.
- the normal capturing should catch enough possibilities
- of tests messing up standard FDs.
-
-- add pluginmanager.do_configure(config) as a link to
- config.do_configure() for plugin-compatibility
-
-2.4.1
-=====
-
-- When using parser.addoption() unicode arguments to the
- "type" keyword should also be converted to the respective types.
- thanks Floris Bruynooghe, @dnozay. (fixes issue360 and issue362)
-
-- fix dotted filename completion when using argcomplete
- thanks Anthon van der Neuth. (fixes issue361)
-
-- fix regression when a 1-tuple ("arg",) is used for specifying
- parametrization (the values of the parametrization were passed
- nested in a tuple). Thanks Donald Stufft.
-
-- merge doc typo fixes, thanks Andy Dirnberger
-
-2.4
-===
-
-known incompatibilities:
-
-- if calling --genscript from python2.7 or above, you only get a
- standalone script which works on python2.7 or above. Use Python2.6
- to also get a python2.5 compatible version.
-
-- all xunit-style teardown methods (nose-style, pytest-style,
- unittest-style) will not be called if the corresponding setup method failed,
- see issue322 below.
-
-- the pytest_plugin_unregister hook wasn't ever properly called
- and there is no known implementation of the hook - so it got removed.
-
-- pytest.fixture-decorated functions cannot be generators (i.e. use
- yield) anymore. This change might be reversed in 2.4.1 if it causes
- unforeseen real-life issues. However, you can always write and return
- an inner function/generator and change the fixture consumer to iterate
- over the returned generator. This change was done in lieu of the new
- ``pytest.yield_fixture`` decorator, see below.
-
-new features:
-
-- experimentally introduce a new ``pytest.yield_fixture`` decorator
- which accepts exactly the same parameters as pytest.fixture but
- mandates a ``yield`` statement instead of a ``return statement`` from
- fixture functions. This allows direct integration with "with-style"
- context managers in fixture functions and generally avoids registering
- of finalization callbacks in favour of treating the "after-yield" as
- teardown code. Thanks Andreas Pelme, Vladimir Keleshev, Floris
- Bruynooghe, Ronny Pfannschmidt and many others for discussions.
-
-- allow boolean expression directly with skipif/xfail
- if a "reason" is also specified. Rework skipping documentation
- to recommend "condition as booleans" because it prevents surprises
- when importing markers between modules. Specifying conditions
- as strings will remain fully supported.
-
-- reporting: color the last line red or green depending if
- failures/errors occurred or everything passed. thanks Christian
- Theunert.
-
-- make "import pdb ; pdb.set_trace()" work natively wrt capturing (no
- "-s" needed anymore), making ``pytest.set_trace()`` a mere shortcut.
-
-- fix issue181: --pdb now also works on collect errors (and
- on internal errors) . This was implemented by a slight internal
- refactoring and the introduction of a new hook
- ``pytest_exception_interact`` hook (see next item).
-
-- fix issue341: introduce new experimental hook for IDEs/terminals to
- intercept debugging: ``pytest_exception_interact(node, call, report)``.
-
-- new monkeypatch.setattr() variant to provide a shorter
- invocation for patching out classes/functions from modules:
-
- monkeypatch.setattr("requests.get", myfunc)
-
- will replace the "get" function of the "requests" module with ``myfunc``.
-
-- fix issue322: tearDownClass is not run if setUpClass failed. Thanks
- Mathieu Agopian for the initial fix. Also make all of pytest/nose
- finalizer mimick the same generic behaviour: if a setupX exists and
- fails, don't run teardownX. This internally introduces a new method
- "node.addfinalizer()" helper which can only be called during the setup
- phase of a node.
-
-- simplify pytest.mark.parametrize() signature: allow to pass a
- CSV-separated string to specify argnames. For example:
- ``pytest.mark.parametrize("input,expected", [(1,2), (2,3)])``
- works as well as the previous:
- ``pytest.mark.parametrize(("input", "expected"), ...)``.
-
-- add support for setUpModule/tearDownModule detection, thanks Brian Okken.
-
-- integrate tab-completion on options through use of "argcomplete".
- Thanks Anthon van der Neut for the PR.
-
-- change option names to be hyphen-separated long options but keep the
- old spelling backward compatible. py.test -h will only show the
- hyphenated version, for example "--collect-only" but "--collectonly"
- will remain valid as well (for backward-compat reasons). Many thanks to
- Anthon van der Neut for the implementation and to Hynek Schlawack for
- pushing us.
-
-- fix issue 308 - allow to mark/xfail/skip individual parameter sets
- when parametrizing. Thanks Brianna Laugher.
-
-- call new experimental pytest_load_initial_conftests hook to allow
- 3rd party plugins to do something before a conftest is loaded.
-
-Bug fixes:
-
-- fix issue358 - capturing options are now parsed more properly
- by using a new parser.parse_known_args method.
-
-- pytest now uses argparse instead of optparse (thanks Anthon) which
- means that "argparse" is added as a dependency if installing into python2.6
- environments or below.
-
-- fix issue333: fix a case of bad unittest/pytest hook interaction.
-
-- PR27: correctly handle nose.SkipTest during collection. Thanks
- Antonio Cuni, Ronny Pfannschmidt.
-
-- fix issue355: junitxml puts name="pytest" attribute to testsuite tag.
-
-- fix issue336: autouse fixture in plugins should work again.
-
-- fix issue279: improve object comparisons on assertion failure
- for standard datatypes and recognise collections.abc. Thanks to
- Brianna Laugher and Mathieu Agopian.
-
-- fix issue317: assertion rewriter support for the is_package method
-
-- fix issue335: document py.code.ExceptionInfo() object returned
- from pytest.raises(), thanks Mathieu Agopian.
-
-- remove implicit distribute_setup support from setup.py.
-
-- fix issue305: ignore any problems when writing pyc files.
-
-- SO-17664702: call fixture finalizers even if the fixture function
- partially failed (finalizers would not always be called before)
-
-- fix issue320 - fix class scope for fixtures when mixed with
- module-level functions. Thanks Anatloy Bubenkoff.
-
-- you can specify "-q" or "-qq" to get different levels of "quieter"
- reporting (thanks Katarzyna Jachim)
-
-- fix issue300 - Fix order of conftest loading when starting py.test
- in a subdirectory.
-
-- fix issue323 - sorting of many module-scoped arg parametrizations
-
-- make sessionfinish hooks execute with the same cwd-context as at
- session start (helps fix plugin behaviour which write output files
- with relative path such as pytest-cov)
-
-- fix issue316 - properly reference collection hooks in docs
-
-- fix issue 306 - cleanup of -k/-m options to only match markers/test
- names/keywords respectively. Thanks Wouter van Ackooy.
-
-- improved doctest counting for doctests in python modules --
- files without any doctest items will not show up anymore
- and doctest examples are counted as separate test items.
- thanks Danilo Bellini.
-
-- fix issue245 by depending on the released py-1.4.14
- which fixes py.io.dupfile to work with files with no
- mode. Thanks Jason R. Coombs.
-
-- fix junitxml generation when test output contains control characters,
- addressing issue267, thanks Jaap Broekhuizen
-
-- fix issue338: honor --tb style for setup/teardown errors as well. Thanks Maho.
-
-- fix issue307 - use yaml.safe_load in example, thanks Mark Eichin.
-
-- better parametrize error messages, thanks Brianna Laugher
-
-- pytest_terminal_summary(terminalreporter) hooks can now use
- ".section(title)" and ".line(msg)" methods to print extra
- information at the end of a test run.
-
-2.3.5
-=====
-
-- fix issue169: respect --tb=style with setup/teardown errors as well.
-
-- never consider a fixture function for test function collection
-
-- allow re-running of test items / helps to fix pytest-reruntests plugin
- and also help to keep less fixture/resource references alive
-
-- put captured stdout/stderr into junitxml output even for passing tests
- (thanks Adam Goucher)
-
-- Issue 265 - integrate nose setup/teardown with setupstate
- so it doesnt try to teardown if it did not setup
-
-- issue 271 - dont write junitxml on slave nodes
-
-- Issue 274 - dont try to show full doctest example
- when doctest does not know the example location
-
-- issue 280 - disable assertion rewriting on buggy CPython 2.6.0
-
-- inject "getfixture()" helper to retrieve fixtures from doctests,
- thanks Andreas Zeidler
-
-- issue 259 - when assertion rewriting, be consistent with the default
- source encoding of ASCII on Python 2
-
-- issue 251 - report a skip instead of ignoring classes with init
-
-- issue250 unicode/str mixes in parametrization names and values now works
-
-- issue257, assertion-triggered compilation of source ending in a
- comment line doesn't blow up in python2.5 (fixed through py>=1.4.13.dev6)
-
-- fix --genscript option to generate standalone scripts that also
- work with python3.3 (importer ordering)
-
-- issue171 - in assertion rewriting, show the repr of some
- global variables
-
-- fix option help for "-k"
-
-- move long description of distribution into README.rst
-
-- improve docstring for metafunc.parametrize()
-
-- fix bug where using capsys with pytest.set_trace() in a test
- function would break when looking at capsys.readouterr()
-
-- allow to specify prefixes starting with "_" when
- customizing python_functions test discovery. (thanks Graham Horler)
-
-- improve PYTEST_DEBUG tracing output by puting
- extra data on a new lines with additional indent
-
-- ensure OutcomeExceptions like skip/fail have initialized exception attributes
-
-- issue 260 - don't use nose special setup on plain unittest cases
-
-- fix issue134 - print the collect errors that prevent running specified test items
-
-- fix issue266 - accept unicode in MarkEvaluator expressions
-
-2.3.4
-=====
-
-- yielded test functions will now have autouse-fixtures active but
- cannot accept fixtures as funcargs - it's anyway recommended to
- rather use the post-2.0 parametrize features instead of yield, see:
- http://pytest.org/latest/example/parametrize.html
-- fix autouse-issue where autouse-fixtures would not be discovered
- if defined in a a/conftest.py file and tests in a/tests/test_some.py
-- fix issue226 - LIFO ordering for fixture teardowns
-- fix issue224 - invocations with >256 char arguments now work
-- fix issue91 - add/discuss package/directory level setups in example
-- allow to dynamically define markers via
- item.keywords[...]=assignment integrating with "-m" option
-- make "-k" accept an expressions the same as with "-m" so that one
- can write: -k "name1 or name2" etc. This is a slight incompatibility
- if you used special syntax like "TestClass.test_method" which you now
- need to write as -k "TestClass and test_method" to match a certain
- method in a certain test class.
-
-2.3.3
-=====
-
-- fix issue214 - parse modules that contain special objects like e. g.
- flask's request object which blows up on getattr access if no request
- is active. thanks Thomas Waldmann.
-
-- fix issue213 - allow to parametrize with values like numpy arrays that
- do not support an __eq__ operator
-
-- fix issue215 - split test_python.org into multiple files
-
-- fix issue148 - @unittest.skip on classes is now recognized and avoids
- calling setUpClass/tearDownClass, thanks Pavel Repin
-
-- fix issue209 - reintroduce python2.4 support by depending on newer
- pylib which re-introduced statement-finding for pre-AST interpreters
-
-- nose support: only call setup if its a callable, thanks Andrew
- Taumoefolau
-
-- fix issue219 - add py2.4-3.3 classifiers to TROVE list
-
-- in tracebacks *,** arg values are now shown next to normal arguments
- (thanks Manuel Jacob)
-
-- fix issue217 - support mock.patch with pytest's fixtures - note that
- you need either mock-1.0.1 or the python3.3 builtin unittest.mock.
-
-- fix issue127 - improve documentation for pytest_addoption() and
- add a ``config.getoption(name)`` helper function for consistency.
-
-2.3.2
-=====
-
-- fix issue208 and fix issue29 use new py version to avoid long pauses
- when printing tracebacks in long modules
-
-- fix issue205 - conftests in subdirs customizing
- pytest_pycollect_makemodule and pytest_pycollect_makeitem
- now work properly
-
-- fix teardown-ordering for parametrized setups
-
-- fix issue127 - better documentation for pytest_addoption
- and related objects.
-
-- fix unittest behaviour: TestCase.runtest only called if there are
- test methods defined
-
-- improve trial support: don't collect its empty
- unittest.TestCase.runTest() method
-
-- "python setup.py test" now works with pytest itself
-
-- fix/improve internal/packaging related bits:
-
- - exception message check of test_nose.py now passes on python33 as well
-
- - issue206 - fix test_assertrewrite.py to work when a global
- PYTHONDONTWRITEBYTECODE=1 is present
-
- - add tox.ini to pytest distribution so that ignore-dirs and others config
- bits are properly distributed for maintainers who run pytest-own tests
-
-2.3.1
-=====
-
-- fix issue202 - fix regression: using "self" from fixture functions now
- works as expected (it's the same "self" instance that a test method
- which uses the fixture sees)
-
-- skip pexpect using tests (test_pdb.py mostly) on freebsd* systems
- due to pexpect not supporting it properly (hanging)
-
-- link to web pages from --markers output which provides help for
- pytest.mark.* usage.
-
-2.3.0
-=====
-
-- fix issue202 - better automatic names for parametrized test functions
-- fix issue139 - introduce @pytest.fixture which allows direct scoping
- and parametrization of funcarg factories.
-- fix issue198 - conftest fixtures were not found on windows32 in some
- circumstances with nested directory structures due to path manipulation issues
-- fix issue193 skip test functions with were parametrized with empty
- parameter sets
-- fix python3.3 compat, mostly reporting bits that previously depended
- on dict ordering
-- introduce re-ordering of tests by resource and parametrization setup
- which takes precedence to the usual file-ordering
-- fix issue185 monkeypatching time.time does not cause pytest to fail
-- fix issue172 duplicate call of pytest.fixture decoratored setup_module
- functions
-- fix junitxml=path construction so that if tests change the
- current working directory and the path is a relative path
- it is constructed correctly from the original current working dir.
-- fix "python setup.py test" example to cause a proper "errno" return
-- fix issue165 - fix broken doc links and mention stackoverflow for FAQ
-- catch unicode-issues when writing failure representations
- to terminal to prevent the whole session from crashing
-- fix xfail/skip confusion: a skip-mark or an imperative pytest.skip
- will now take precedence before xfail-markers because we
- can't determine xfail/xpass status in case of a skip. see also:
- http://stackoverflow.com/questions/11105828/in-py-test-when-i-explicitly-skip-a-test-that-is-marked-as-xfail-how-can-i-get
-
-- always report installed 3rd party plugins in the header of a test run
-
-- fix issue160: a failing setup of an xfail-marked tests should
- be reported as xfail (not xpass)
-
-- fix issue128: show captured output when capsys/capfd are used
-
-- fix issue179: propperly show the dependency chain of factories
-
-- pluginmanager.register(...) now raises ValueError if the
- plugin has been already registered or the name is taken
-
-- fix issue159: improve http://pytest.org/latest/faq.html
- especially with respect to the "magic" history, also mention
- pytest-django, trial and unittest integration.
-
-- make request.keywords and node.keywords writable. All descendant
- collection nodes will see keyword values. Keywords are dictionaries
- containing markers and other info.
-
-- fix issue 178: xml binary escapes are now wrapped in py.xml.raw
-
-- fix issue 176: correctly catch the builtin AssertionError
- even when we replaced AssertionError with a subclass on the
- python level
-
-- factory discovery no longer fails with magic global callables
- that provide no sane __code__ object (mock.call for example)
-
-- fix issue 182: testdir.inprocess_run now considers passed plugins
-
-- fix issue 188: ensure sys.exc_info is clear on python2
- before calling into a test
-
-- fix issue 191: add unittest TestCase runTest method support
-- fix issue 156: monkeypatch correctly handles class level descriptors
-
-- reporting refinements:
-
- - pytest_report_header now receives a "startdir" so that
- you can use startdir.bestrelpath(yourpath) to show
- nice relative path
-
- - allow plugins to implement both pytest_report_header and
- pytest_sessionstart (sessionstart is invoked first).
-
- - don't show deselected reason line if there is none
-
- - py.test -vv will show all of assert comparisations instead of truncating
-
-2.2.4
-=====
-
-- fix error message for rewritten assertions involving the % operator
-- fix issue 126: correctly match all invalid xml characters for junitxml
- binary escape
-- fix issue with unittest: now @unittest.expectedFailure markers should
- be processed correctly (you can also use @pytest.mark markers)
-- document integration with the extended distribute/setuptools test commands
-- fix issue 140: propperly get the real functions
- of bound classmethods for setup/teardown_class
-- fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net
-- fix issue #143: call unconfigure/sessionfinish always when
- configure/sessionstart where called
-- fix issue #144: better mangle test ids to junitxml classnames
-- upgrade distribute_setup.py to 0.6.27
-
-2.2.3
-=====
-
-- fix uploaded package to only include neccesary files
-
-2.2.2
-=====
-
-- fix issue101: wrong args to unittest.TestCase test function now
- produce better output
-- fix issue102: report more useful errors and hints for when a
- test directory was renamed and some pyc/__pycache__ remain
-- fix issue106: allow parametrize to be applied multiple times
- e.g. from module, class and at function level.
-- fix issue107: actually perform session scope finalization
-- don't check in parametrize if indirect parameters are funcarg names
-- add chdir method to monkeypatch funcarg
-- fix crash resulting from calling monkeypatch undo a second time
-- fix issue115: make --collectonly robust against early failure
- (missing files/directories)
-- "-qq --collectonly" now shows only files and the number of tests in them
-- "-q --collectonly" now shows test ids
-- allow adding of attributes to test reports such that it also works
- with distributed testing (no upgrade of pytest-xdist needed)
-
-2.2.1
-=====
-
-- fix issue99 (in pytest and py) internallerrors with resultlog now
- produce better output - fixed by normalizing pytest_internalerror
- input arguments.
-- fix issue97 / traceback issues (in pytest and py) improve traceback output
- in conjunction with jinja2 and cython which hack tracebacks
-- fix issue93 (in pytest and pytest-xdist) avoid "delayed teardowns":
- the final test in a test node will now run its teardown directly
- instead of waiting for the end of the session. Thanks Dave Hunt for
- the good reporting and feedback. The pytest_runtest_protocol as well
- as the pytest_runtest_teardown hooks now have "nextitem" available
- which will be None indicating the end of the test run.
-- fix collection crash due to unknown-source collected items, thanks
- to Ralf Schmitt (fixed by depending on a more recent pylib)
-
-2.2.0
-=====
-
-- fix issue90: introduce eager tearing down of test items so that
- teardown function are called earlier.
-- add an all-powerful metafunc.parametrize function which allows to
- parametrize test function arguments in multiple steps and therefore
- from indepdenent plugins and palces.
-- add a @pytest.mark.parametrize helper which allows to easily
- call a test function with different argument values
-- Add examples to the "parametrize" example page, including a quick port
- of Test scenarios and the new parametrize function and decorator.
-- introduce registration for "pytest.mark.*" helpers via ini-files
- or through plugin hooks. Also introduce a "--strict" option which
- will treat unregistered markers as errors
- allowing to avoid typos and maintain a well described set of markers
- for your test suite. See exaples at http://pytest.org/latest/mark.html
- and its links.
-- issue50: introduce "-m marker" option to select tests based on markers
- (this is a stricter and more predictable version of '-k' in that "-m"
- only matches complete markers and has more obvious rules for and/or
- semantics.
-- new feature to help optimizing the speed of your tests:
- --durations=N option for displaying N slowest test calls
- and setup/teardown methods.
-- fix issue87: --pastebin now works with python3
-- fix issue89: --pdb with unexpected exceptions in doctest work more sensibly
-- fix and cleanup pytest's own test suite to not leak FDs
-- fix issue83: link to generated funcarg list
-- fix issue74: pyarg module names are now checked against imp.find_module false positives
-- fix compatibility with twisted/trial-11.1.0 use cases
-- simplify Node.listchain
-- simplify junitxml output code by relying on py.xml
-- add support for skip properties on unittest classes and functions
-
-2.1.3
-=====
-
-- fix issue79: assertion rewriting failed on some comparisons in boolops
-- correctly handle zero length arguments (a la pytest '')
-- fix issue67 / junitxml now contains correct test durations, thanks ronny
-- fix issue75 / skipping test failure on jython
-- fix issue77 / Allow assertrepr_compare hook to apply to a subset of tests
-
-2.1.2
-=====
-
-- fix assertion rewriting on files with windows newlines on some Python versions
-- refine test discovery by package/module name (--pyargs), thanks Florian Mayer
-- fix issue69 / assertion rewriting fixed on some boolean operations
-- fix issue68 / packages now work with assertion rewriting
-- fix issue66: use different assertion rewriting caches when the -O option is passed
-- don't try assertion rewriting on Jython, use reinterp
-
-2.1.1
-=====
-
-- fix issue64 / pytest.set_trace now works within pytest_generate_tests hooks
-- fix issue60 / fix error conditions involving the creation of __pycache__
-- fix issue63 / assertion rewriting on inserts involving strings containing '%'
-- fix assertion rewriting on calls with a ** arg
-- don't cache rewritten modules if bytecode generation is disabled
-- fix assertion rewriting in read-only directories
-- fix issue59: provide system-out/err tags for junitxml output
-- fix issue61: assertion rewriting on boolean operations with 3 or more operands
-- you can now build a man page with "cd doc ; make man"
-
-2.1.0
-=====
-
-- fix issue53 call nosestyle setup functions with correct ordering
-- fix issue58 and issue59: new assertion code fixes
-- merge Benjamin's assertionrewrite branch: now assertions
- for test modules on python 2.6 and above are done by rewriting
- the AST and saving the pyc file before the test module is imported.
- see doc/assert.txt for more info.
-- fix issue43: improve doctests with better traceback reporting on
- unexpected exceptions
-- fix issue47: timing output in junitxml for test cases is now correct
-- fix issue48: typo in MarkInfo repr leading to exception
-- fix issue49: avoid confusing error when initizaliation partially fails
-- fix issue44: env/username expansion for junitxml file path
-- show releaselevel information in test runs for pypy
-- reworked doc pages for better navigation and PDF generation
-- report KeyboardInterrupt even if interrupted during session startup
-- fix issue 35 - provide PDF doc version and download link from index page
-
-2.0.3
-=====
-
-- fix issue38: nicer tracebacks on calls to hooks, particularly early
- configure/sessionstart ones
-
-- fix missing skip reason/meta information in junitxml files, reported
- via http://lists.idyll.org/pipermail/testing-in-python/2011-March/003928.html
-
-- fix issue34: avoid collection failure with "test" prefixed classes
- deriving from object.
-
-- don't require zlib (and other libs) for genscript plugin without
- --genscript actually being used.
-
-- speed up skips (by not doing a full traceback represenation
- internally)
-
-- fix issue37: avoid invalid characters in junitxml's output
-
-2.0.2
-=====
-
-- tackle issue32 - speed up test runs of very quick test functions
- by reducing the relative overhead
-
-- fix issue30 - extended xfail/skipif handling and improved reporting.
- If you have a syntax error in your skip/xfail
- expressions you now get nice error reports.
-
- Also you can now access module globals from xfail/skipif
- expressions so that this for example works now::
-
- import pytest
- import mymodule
- @pytest.mark.skipif("mymodule.__version__[0] == "1")
- def test_function():
- pass
-
- This will not run the test function if the module's version string
- does not start with a "1". Note that specifying a string instead
- of a boolean expressions allows py.test to report meaningful information
- when summarizing a test run as to what conditions lead to skipping
- (or xfail-ing) tests.
-
-- fix issue28 - setup_method and pytest_generate_tests work together
- The setup_method fixture method now gets called also for
- test function invocations generated from the pytest_generate_tests
- hook.
-
-- fix issue27 - collectonly and keyword-selection (-k) now work together
- Also, if you do "py.test --collectonly -q" you now get a flat list
- of test ids that you can use to paste to the py.test commandline
- in order to execute a particular test.
-
-- fix issue25 avoid reported problems with --pdb and python3.2/encodings output
-
-- fix issue23 - tmpdir argument now works on Python3.2 and WindowsXP
- Starting with Python3.2 os.symlink may be supported. By requiring
- a newer py lib version the py.path.local() implementation acknowledges
- this.
-
-- fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular
- thanks to Laura Creighton who also revieved parts of the documentation.
-
-- fix slighly wrong output of verbose progress reporting for classes
- (thanks Amaury)
-
-- more precise (avoiding of) deprecation warnings for node.Class|Function accesses
-
-- avoid std unittest assertion helper code in tracebacks (thanks Ronny)
-
-2.0.1
-=====
-
-- refine and unify initial capturing so that it works nicely
- even if the logging module is used on an early-loaded conftest.py
- file or plugin.
-- allow to omit "()" in test ids to allow for uniform test ids
- as produced by Alfredo's nice pytest.vim plugin.
-- fix issue12 - show plugin versions with "--version" and
- "--traceconfig" and also document how to add extra information
- to reporting test header
-- fix issue17 (import-* reporting issue on python3) by
- requiring py>1.4.0 (1.4.1 is going to include it)
-- fix issue10 (numpy arrays truth checking) by refining
- assertion interpretation in py lib
-- fix issue15: make nose compatibility tests compatible
- with python3 (now that nose-1.0 supports python3)
-- remove somewhat surprising "same-conftest" detection because
- it ignores conftest.py when they appear in several subdirs.
-- improve assertions ("not in"), thanks Floris Bruynooghe
-- improve behaviour/warnings when running on top of "python -OO"
- (assertions and docstrings are turned off, leading to potential
- false positives)
-- introduce a pytest_cmdline_processargs(args) hook
- to allow dynamic computation of command line arguments.
- This fixes a regression because py.test prior to 2.0
- allowed to set command line options from conftest.py
- files which so far pytest-2.0 only allowed from ini-files now.
-- fix issue7: assert failures in doctest modules.
- unexpected failures in doctests will not generally
- show nicer, i.e. within the doctest failing context.
-- fix issue9: setup/teardown functions for an xfail-marked
- test will report as xfail if they fail but report as normally
- passing (not xpassing) if they succeed. This only is true
- for "direct" setup/teardown invocations because teardown_class/
- teardown_module cannot closely relate to a single test.
-- fix issue14: no logging errors at process exit
-- refinements to "collecting" output on non-ttys
-- refine internal plugin registration and --traceconfig output
-- introduce a mechanism to prevent/unregister plugins from the
- command line, see http://pytest.org/plugins.html#cmdunregister
-- activate resultlog plugin by default
-- fix regression wrt yielded tests which due to the
- collection-before-running semantics were not
- setup as with pytest 1.3.4. Note, however, that
- the recommended and much cleaner way to do test
- parametraization remains the "pytest_generate_tests"
- mechanism, see the docs.
-
-2.0.0
-=====
-
-- pytest-2.0 is now its own package and depends on pylib-2.0
-- new ability: python -m pytest / python -m pytest.main ability
-- new python invcation: pytest.main(args, plugins) to load
- some custom plugins early.
-- try harder to run unittest test suites in a more compatible manner
- by deferring setup/teardown semantics to the unittest package.
- also work harder to run twisted/trial and Django tests which
- should now basically work by default.
-- introduce a new way to set config options via ini-style files,
- by default setup.cfg and tox.ini files are searched. The old
- ways (certain environment variables, dynamic conftest.py reading
- is removed).
-- add a new "-q" option which decreases verbosity and prints a more
- nose/unittest-style "dot" output.
-- fix issue135 - marks now work with unittest test cases as well
-- fix issue126 - introduce py.test.set_trace() to trace execution via
- PDB during the running of tests even if capturing is ongoing.
-- fix issue123 - new "python -m py.test" invocation for py.test
- (requires Python 2.5 or above)
-- fix issue124 - make reporting more resilient against tests opening
- files on filedescriptor 1 (stdout).
-- fix issue109 - sibling conftest.py files will not be loaded.
- (and Directory collectors cannot be customized anymore from a Directory's
- conftest.py - this needs to happen at least one level up).
-- introduce (customizable) assertion failure representations and enhance
- output on assertion failures for comparisons and other cases (Floris Bruynooghe)
-- nose-plugin: pass through type-signature failures in setup/teardown
- functions instead of not calling them (Ed Singleton)
-- remove py.test.collect.Directory (follows from a major refactoring
- and simplification of the collection process)
-- majorly reduce py.test core code, shift function/python testing to own plugin
-- fix issue88 (finding custom test nodes from command line arg)
-- refine 'tmpdir' creation, will now create basenames better associated
- with test names (thanks Ronny)
-- "xpass" (unexpected pass) tests don't cause exitcode!=0
-- fix issue131 / issue60 - importing doctests in __init__ files used as namespace packages
-- fix issue93 stdout/stderr is captured while importing conftest.py
-- fix bug: unittest collected functions now also can have "pytestmark"
- applied at class/module level
-- add ability to use "class" level for cached_setup helper
-- fix strangeness: mark.* objects are now immutable, create new instances
-
-1.3.4
-=====
-
-- fix issue111: improve install documentation for windows
-- fix issue119: fix custom collectability of __init__.py as a module
-- fix issue116: --doctestmodules work with __init__.py files as well
-- fix issue115: unify internal exception passthrough/catching/GeneratorExit
-- fix issue118: new --tb=native for presenting cpython-standard exceptions
-
-1.3.3
-=====
-
-- fix issue113: assertion representation problem with triple-quoted strings
- (and possibly other cases)
-- make conftest loading detect that a conftest file with the same
- content was already loaded, avoids surprises in nested directory structures
- which can be produced e.g. by Hudson. It probably removes the need to use
- --confcutdir in most cases.
-- fix terminal coloring for win32
- (thanks Michael Foord for reporting)
-- fix weirdness: make terminal width detection work on stdout instead of stdin
- (thanks Armin Ronacher for reporting)
-- remove trailing whitespace in all py/text distribution files
-
-1.3.2
-=====
-
-**New features**
-
-- fix issue103: introduce py.test.raises as context manager, examples::
-
- with py.test.raises(ZeroDivisionError):
- x = 0
- 1 / x
-
- with py.test.raises(RuntimeError) as excinfo:
- call_something()
-
- # you may do extra checks on excinfo.value|type|traceback here
-
- (thanks Ronny Pfannschmidt)
-
-- Funcarg factories can now dynamically apply a marker to a
- test invocation. This is for example useful if a factory
- provides parameters to a test which are expected-to-fail::
-
- def pytest_funcarg__arg(request):
- request.applymarker(py.test.mark.xfail(reason="flaky config"))
- ...
-
- def test_function(arg):
- ...
-
-- improved error reporting on collection and import errors. This makes
- use of a more general mechanism, namely that for custom test item/collect
- nodes ``node.repr_failure(excinfo)`` is now uniformly called so that you can
- override it to return a string error representation of your choice
- which is going to be reported as a (red) string.
-
-- introduce '--junitprefix=STR' option to prepend a prefix
- to all reports in the junitxml file.
-
-**Bug fixes**
-
-- make tests and the ``pytest_recwarn`` plugin in particular fully compatible
- to Python2.7 (if you use the ``recwarn`` funcarg warnings will be enabled so that
- you can properly check for their existence in a cross-python manner).
-- refine --pdb: ignore xfailed tests, unify its TB-reporting and
- don't display failures again at the end.
-- fix assertion interpretation with the ** operator (thanks Benjamin Peterson)
-- fix issue105 assignment on the same line as a failing assertion (thanks Benjamin Peterson)
-- fix issue104 proper escaping for test names in junitxml plugin (thanks anonymous)
-- fix issue57 -f|--looponfail to work with xpassing tests (thanks Ronny)
-- fix issue92 collectonly reporter and --pastebin (thanks Benjamin Peterson)
-- fix py.code.compile(source) to generate unique filenames
-- fix assertion re-interp problems on PyPy, by defering code
- compilation to the (overridable) Frame.eval class. (thanks Amaury Forgeot)
-- fix py.path.local.pyimport() to work with directories
-- streamline py.path.local.mkdtemp implementation and usage
-- don't print empty lines when showing junitxml-filename
-- add optional boolean ignore_errors parameter to py.path.local.remove
-- fix terminal writing on win32/python2.4
-- py.process.cmdexec() now tries harder to return properly encoded unicode objects
- on all python versions
-- install plain py.test/py.which scripts also for Jython, this helps to
- get canonical script paths in virtualenv situations
-- make path.bestrelpath(path) return ".", note that when calling
- X.bestrelpath the assumption is that X is a directory.
-- make initial conftest discovery ignore "--" prefixed arguments
-- fix resultlog plugin when used in an multicpu/multihost xdist situation
- (thanks Jakub Gustak)
-- perform distributed testing related reporting in the xdist-plugin
- rather than having dist-related code in the generic py.test
- distribution
-- fix homedir detection on Windows
-- ship distribute_setup.py version 0.6.13
-
-1.3.1
-=====
-
-**New features**
-
-- issue91: introduce new py.test.xfail(reason) helper
- to imperatively mark a test as expected to fail. Can
- be used from within setup and test functions. This is
- useful especially for parametrized tests when certain
- configurations are expected-to-fail. In this case the
- declarative approach with the @py.test.mark.xfail cannot
- be used as it would mark all configurations as xfail.
-
-- issue102: introduce new --maxfail=NUM option to stop
- test runs after NUM failures. This is a generalization
- of the '-x' or '--exitfirst' option which is now equivalent
- to '--maxfail=1'. Both '-x' and '--maxfail' will
- now also print a line near the end indicating the Interruption.
-
-- issue89: allow py.test.mark decorators to be used on classes
- (class decorators were introduced with python2.6) and
- also allow to have multiple markers applied at class/module level
- by specifying a list.
-
-- improve and refine letter reporting in the progress bar:
- . pass
- f failed test
- s skipped tests (reminder: use for dependency/platform mismatch only)
- x xfailed test (test that was expected to fail)
- X xpassed test (test that was expected to fail but passed)
-
- You can use any combination of 'fsxX' with the '-r' extended
- reporting option. The xfail/xpass results will show up as
- skipped tests in the junitxml output - which also fixes
- issue99.
-
-- make py.test.cmdline.main() return the exitstatus instead of raising
- SystemExit and also allow it to be called multiple times. This of
- course requires that your application and tests are properly teared
- down and don't have global state.
-
-**Bug Fixes**
-
-- improved traceback presentation:
- - improved and unified reporting for "--tb=short" option
- - Errors during test module imports are much shorter, (using --tb=short style)
- - raises shows shorter more relevant tracebacks
- - --fulltrace now more systematically makes traces longer / inhibits cutting
-
-- improve support for raises and other dynamically compiled code by
- manipulating python's linecache.cache instead of the previous
- rather hacky way of creating custom code objects. This makes
- it seemlessly work on Jython and PyPy where it previously didn't.
-
-- fix issue96: make capturing more resilient against Control-C
- interruptions (involved somewhat substantial refactoring
- to the underlying capturing functionality to avoid race
- conditions).
-
-- fix chaining of conditional skipif/xfail decorators - so it works now
- as expected to use multiple @py.test.mark.skipif(condition) decorators,
- including specific reporting which of the conditions lead to skipping.
-
-- fix issue95: late-import zlib so that it's not required
- for general py.test startup.
-
-- fix issue94: make reporting more robust against bogus source code
- (and internally be more careful when presenting unexpected byte sequences)
-
-
-1.3.0
-=====
-
-- deprecate --report option in favour of a new shorter and easier to
- remember -r option: it takes a string argument consisting of any
- combination of 'xfsX' characters. They relate to the single chars
- you see during the dotted progress printing and will print an extra line
- per test at the end of the test run. This extra line indicates the exact
- position or test ID that you directly paste to the py.test cmdline in order
- to re-run a particular test.
-
-- allow external plugins to register new hooks via the new
- pytest_addhooks(pluginmanager) hook. The new release of
- the pytest-xdist plugin for distributed and looponfailing
- testing requires this feature.
-
-- add a new pytest_ignore_collect(path, config) hook to allow projects and
- plugins to define exclusion behaviour for their directory structure -
- for example you may define in a conftest.py this method::
-
- def pytest_ignore_collect(path):
- return path.check(link=1)
-
- to prevent even a collection try of any tests in symlinked dirs.
-
-- new pytest_pycollect_makemodule(path, parent) hook for
- allowing customization of the Module collection object for a
- matching test module.
-
-- extend and refine xfail mechanism:
- ``@py.test.mark.xfail(run=False)`` do not run the decorated test
- ``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries
- specifiying ``--runxfail`` on command line virtually ignores xfail markers
-
-- expose (previously internal) commonly useful methods:
- py.io.get_terminal_with() -> return terminal width
- py.io.ansi_print(...) -> print colored/bold text on linux/win32
- py.io.saferepr(obj) -> return limited representation string
-
-- expose test outcome related exceptions as py.test.skip.Exception,
- py.test.raises.Exception etc., useful mostly for plugins
- doing special outcome interpretation/tweaking
-
-- (issue85) fix junitxml plugin to handle tests with non-ascii output
-
-- fix/refine python3 compatibility (thanks Benjamin Peterson)
-
-- fixes for making the jython/win32 combination work, note however:
- jython2.5.1/win32 does not provide a command line launcher, see
- http://bugs.jython.org/issue1491 . See pylib install documentation
- for how to work around.
-
-- fixes for handling of unicode exception values and unprintable objects
-
-- (issue87) fix unboundlocal error in assertionold code
-
-- (issue86) improve documentation for looponfailing
-
-- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method
-
-- ship distribute_setup.py version 0.6.10
-
-- added links to the new capturelog and coverage plugins
-
-
-1.2.0
-=====
-
-- refined usage and options for "py.cleanup"::
-
- py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
- py.cleanup -e .swp -e .cache # also remove files with these extensions
- py.cleanup -s # remove "build" and "dist" directory next to setup.py files
- py.cleanup -d # also remove empty directories
- py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
- py.cleanup -n # dry run, only show what would be removed
-
-- add a new option "py.test --funcargs" which shows available funcargs
- and their help strings (docstrings on their respective factory function)
- for a given test path
-
-- display a short and concise traceback if a funcarg lookup fails
-
-- early-load "conftest.py" files in non-dot first-level sub directories.
- allows to conveniently keep and access test-related options in a ``test``
- subdir and still add command line options.
-
-- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
-
-- fix issue78: always call python-level teardown functions even if the
- according setup failed. This includes refinements for calling setup_module/class functions
- which will now only be called once instead of the previous behaviour where they'd be called
- multiple times if they raise an exception (including a Skipped exception). Any exception
- will be re-corded and associated with all tests in the according module/class scope.
-
-- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
-
-- fix pdb debugging to be in the correct frame on raises-related errors
-
-- update apipkg.py to fix an issue where recursive imports might
- unnecessarily break importing
-
-- fix plugin links
-
-1.1.1
-=====
-
-- moved dist/looponfailing from py.test core into a new
- separately released pytest-xdist plugin.
-
-- new junitxml plugin: --junitxml=path will generate a junit style xml file
- which is processable e.g. by the Hudson CI system.
-
-- new option: --genscript=path will generate a standalone py.test script
- which will not need any libraries installed. thanks to Ralf Schmitt.
-
-- new option: --ignore will prevent specified path from collection.
- Can be specified multiple times.
-
-- new option: --confcutdir=dir will make py.test only consider conftest
- files that are relative to the specified dir.
-
-- new funcarg: "pytestconfig" is the pytest config object for access
- to command line args and can now be easily used in a test.
-
-- install ``py.test`` and ``py.which`` with a ``-$VERSION`` suffix to
- disambiguate between Python3, python2.X, Jython and PyPy installed versions.
-
-- new "pytestconfig" funcarg allows access to test config object
-
-- new "pytest_report_header" hook can return additional lines
- to be displayed at the header of a test run.
-
-- (experimental) allow "py.test path::name1::name2::..." for pointing
- to a test within a test collection directly. This might eventually
- evolve as a full substitute to "-k" specifications.
-
-- streamlined plugin loading: order is now as documented in
- customize.html: setuptools, ENV, commandline, conftest.
- also setuptools entry point names are turned to canonical namees ("pytest_*")
-
-- automatically skip tests that need 'capfd' but have no os.dup
-
-- allow pytest_generate_tests to be defined in classes as well
-
-- deprecate usage of 'disabled' attribute in favour of pytestmark
-- deprecate definition of Directory, Module, Class and Function nodes
- in conftest.py files. Use pytest collect hooks instead.
-
-- collection/item node specific runtest/collect hooks are only called exactly
- on matching conftest.py files, i.e. ones which are exactly below
- the filesystem path of an item
-
-- change: the first pytest_collect_directory hook to return something
- will now prevent further hooks to be called.
-
-- change: figleaf plugin now requires --figleaf to run. Also
- change its long command line options to be a bit shorter (see py.test -h).
-
-- change: pytest doctest plugin is now enabled by default and has a
- new option --doctest-glob to set a pattern for file matches.
-
-- change: remove internal py._* helper vars, only keep py._pydir
-
-- robustify capturing to survive if custom pytest_runtest_setup
- code failed and prevented the capturing setup code from running.
-
-- make py.test.* helpers provided by default plugins visible early -
- works transparently both for pydoc and for interactive sessions
- which will regularly see e.g. py.test.mark and py.test.importorskip.
-
-- simplify internal plugin manager machinery
-- simplify internal collection tree by introducing a RootCollector node
-
-- fix assert reinterpreation that sees a call containing "keyword=..."
-
-- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
- hooks on slaves during dist-testing, report module/session teardown
- hooks correctly.
-
-- fix issue65: properly handle dist-testing if no
- execnet/py lib installed remotely.
-
-- skip some install-tests if no execnet is available
-
-- fix docs, fix internal bin/ script generation
-
-
-1.1.0
-=====
-
-- introduce automatic plugin registration via 'pytest11'
- entrypoints via setuptools' pkg_resources.iter_entry_points
-
-- fix py.test dist-testing to work with execnet >= 1.0.0b4
-
-- re-introduce py.test.cmdline.main() for better backward compatibility
-
-- svn paths: fix a bug with path.check(versioned=True) for svn paths,
- allow '%' in svn paths, make svnwc.update() default to interactive mode
- like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
-
-- refine distributed tarball to contain test and no pyc files
-
-- try harder to have deprecation warnings for py.compat.* accesses
- report a correct location
-
-1.0.3
-=====
-
-* adjust and improve docs
-
-* remove py.rest tool and internal namespace - it was
- never really advertised and can still be used with
- the old release if needed. If there is interest
- it could be revived into its own tool i guess.
-
-* fix issue48 and issue59: raise an Error if the module
- from an imported test file does not seem to come from
- the filepath - avoids "same-name" confusion that has
- been reported repeatedly
-
-* merged Ronny's nose-compatibility hacks: now
- nose-style setup_module() and setup() functions are
- supported
-
-* introduce generalized py.test.mark function marking
-
-* reshuffle / refine command line grouping
-
-* deprecate parser.addgroup in favour of getgroup which creates option group
-
-* add --report command line option that allows to control showing of skipped/xfailed sections
-
-* generalized skipping: a new way to mark python functions with skipif or xfail
- at function, class and modules level based on platform or sys-module attributes.
-
-* extend py.test.mark decorator to allow for positional args
-
-* introduce and test "py.cleanup -d" to remove empty directories
-
-* fix issue #59 - robustify unittest test collection
-
-* make bpython/help interaction work by adding an __all__ attribute
- to ApiModule, cleanup initpkg
-
-* use MIT license for pylib, add some contributors
-
-* remove py.execnet code and substitute all usages with 'execnet' proper
-
-* fix issue50 - cached_setup now caches more to expectations
- for test functions with multiple arguments.
-
-* merge Jarko's fixes, issue #45 and #46
-
-* add the ability to specify a path for py.lookup to search in
-
-* fix a funcarg cached_setup bug probably only occuring
- in distributed testing and "module" scope with teardown.
-
-* many fixes and changes for making the code base python3 compatible,
- many thanks to Benjamin Peterson for helping with this.
-
-* consolidate builtins implementation to be compatible with >=2.3,
- add helpers to ease keeping 2 and 3k compatible code
-
-* deprecate py.compat.doctest|subprocess|textwrap|optparse
-
-* deprecate py.magic.autopath, remove py/magic directory
-
-* move pytest assertion handling to py/code and a pytest_assertion
- plugin, add "--no-assert" option, deprecate py.magic namespaces
- in favour of (less) py.code ones.
-
-* consolidate and cleanup py/code classes and files
-
-* cleanup py/misc, move tests to bin-for-dist
-
-* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
-
-* consolidate py.log implementation, remove old approach.
-
-* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
- text/unicode and byte-streams (uses underlying standard lib io.*
- if available)
-
-* make py.unittest_convert helper script available which converts "unittest.py"
- style files into the simpler assert/direct-test-classes py.test/nosetests
- style. The script was written by Laura Creighton.
-
-* simplified internal localpath implementation
-
-1.0.2
-=====
-
-* fixing packaging issues, triggered by fedora redhat packaging,
- also added doc, examples and contrib dirs to the tarball.
-
-* added a documentation link to the new django plugin.
-
-1.0.1
-=====
-
-* added a 'pytest_nose' plugin which handles nose.SkipTest,
- nose-style function/method/generator setup/teardown and
- tries to report functions correctly.
-
-* capturing of unicode writes or encoded strings to sys.stdout/err
- work better, also terminalwriting was adapted and somewhat
- unified between windows and linux.
-
-* improved documentation layout and content a lot
-
-* added a "--help-config" option to show conftest.py / ENV-var names for
- all longopt cmdline options, and some special conftest.py variables.
- renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
-
-* fix issue #27: better reporting on non-collectable items given on commandline
- (e.g. pyc files)
-
-* fix issue #33: added --version flag (thanks Benjamin Peterson)
-
-* fix issue #32: adding support for "incomplete" paths to wcpath.status()
-
-* "Test" prefixed classes are *not* collected by default anymore if they
- have an __init__ method
-
-* monkeypatch setenv() now accepts a "prepend" parameter
-
-* improved reporting of collection error tracebacks
-
-* simplified multicall mechanism and plugin architecture,
- renamed some internal methods and argnames
-
-1.0.0
-=====
-
-* more terse reporting try to show filesystem path relatively to current dir
-* improve xfail output a bit
-
-1.0.0b9
-=======
-
-* cleanly handle and report final teardown of test setup
-
-* fix svn-1.6 compat issue with py.path.svnwc().versioned()
- (thanks Wouter Vanden Hove)
-
-* setup/teardown or collection problems now show as ERRORs
- or with big "E"'s in the progress lines. they are reported
- and counted separately.
-
-* dist-testing: properly handle test items that get locally
- collected but cannot be collected on the remote side - often
- due to platform/dependency reasons
-
-* simplified py.test.mark API - see keyword plugin documentation
-
-* integrate better with logging: capturing now by default captures
- test functions and their immediate setup/teardown in a single stream
-
-* capsys and capfd funcargs now have a readouterr() and a close() method
- (underlyingly py.io.StdCapture/FD objects are used which grew a
- readouterr() method as well to return snapshots of captured out/err)
-
-* make assert-reinterpretation work better with comparisons not
- returning bools (reported with numpy from thanks maciej fijalkowski)
-
-* reworked per-test output capturing into the pytest_iocapture.py plugin
- and thus removed capturing code from config object
-
-* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
-
-
-1.0.0b8
-=======
-
-* pytest_unittest-plugin is now enabled by default
-
-* introduced pytest_keyboardinterrupt hook and
- refined pytest_sessionfinish hooked, added tests.
-
-* workaround a buggy logging module interaction ("closing already closed
- files"). Thanks to Sridhar Ratnakumar for triggering.
-
-* if plugins use "py.test.importorskip" for importing
- a dependency only a warning will be issued instead
- of exiting the testing process.
-
-* many improvements to docs:
- - refined funcargs doc , use the term "factory" instead of "provider"
- - added a new talk/tutorial doc page
- - better download page
- - better plugin docstrings
- - added new plugins page and automatic doc generation script
-
-* fixed teardown problem related to partially failing funcarg setups
- (thanks MrTopf for reporting), "pytest_runtest_teardown" is now
- always invoked even if the "pytest_runtest_setup" failed.
-
-* tweaked doctest output for docstrings in py modules,
- thanks Radomir.
-
-1.0.0b7
-=======
-
-* renamed py.test.xfail back to py.test.mark.xfail to avoid
- two ways to decorate for xfail
-
-* re-added py.test.mark decorator for setting keywords on functions
- (it was actually documented so removing it was not nice)
-
-* remove scope-argument from request.addfinalizer() because
- request.cached_setup has the scope arg. TOOWTDI.
-
-* perform setup finalization before reporting failures
-
-* apply modified patches from Andreas Kloeckner to allow
- test functions to have no func_code (#22) and to make
- "-k" and function keywords work (#20)
-
-* apply patch from Daniel Peolzleithner (issue #23)
-
-* resolve issue #18, multiprocessing.Manager() and
- redirection clash
-
-* make __name__ == "__channelexec__" for remote_exec code
-
-1.0.0b3
-=======
-
-* plugin classes are removed: one now defines
- hooks directly in conftest.py or global pytest_*.py
- files.
-
-* added new pytest_namespace(config) hook that allows
- to inject helpers directly to the py.test.* namespace.
-
-* documented and refined many hooks
-
-* added new style of generative tests via
- pytest_generate_tests hook that integrates
- well with function arguments.
-
-
-1.0.0b1
-=======
-
-* introduced new "funcarg" setup method,
- see doc/test/funcarg.txt
-
-* introduced plugin architecture and many
- new py.test plugins, see
- doc/test/plugins.txt
-
-* teardown_method is now guaranteed to get
- called after a test method has run.
-
-* new method: py.test.importorskip(mod,minversion)
- will either import or call py.test.skip()
-
-* completely revised internal py.test architecture
-
-* new py.process.ForkedFunc object allowing to
- fork execution of a function to a sub process
- and getting a result back.
-
-XXX lots of things missing here XXX
-
-0.9.2
-=====
-
-* refined installation and metadata, created new setup.py,
- now based on setuptools/ez_setup (thanks to Ralf Schmitt
- for his support).
-
-* improved the way of making py.* scripts available in
- windows environments, they are now added to the
- Scripts directory as ".cmd" files.
-
-* py.path.svnwc.status() now is more complete and
- uses xml output from the 'svn' command if available
- (Guido Wesdorp)
-
-* fix for py.path.svn* to work with svn 1.5
- (Chris Lamb)
-
-* fix path.relto(otherpath) method on windows to
- use normcase for checking if a path is relative.
-
-* py.test's traceback is better parseable from editors
- (follows the filenames:LINENO: MSG convention)
- (thanks to Osmo Salomaa)
-
-* fix to javascript-generation, "py.test --runbrowser"
- should work more reliably now
-
-* removed previously accidentally added
- py.test.broken and py.test.notimplemented helpers.
-
-* there now is a py.__version__ attribute
-
-0.9.1
-=====
-
-This is a fairly complete list of v0.9.1, which can
-serve as a reference for developers.
-
-* allowing + signs in py.path.svn urls [39106]
-* fixed support for Failed exceptions without excinfo in py.test [39340]
-* added support for killing processes for Windows (as well as platforms that
- support os.kill) in py.misc.killproc [39655]
-* added setup/teardown for generative tests to py.test [40702]
-* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739]
-* fixed problem with calling .remove() on wcpaths of non-versioned files in
- py.path [44248]
-* fixed some import and inheritance issues in py.test [41480, 44648, 44655]
-* fail to run greenlet tests when pypy is available, but without stackless
- [45294]
-* small fixes in rsession tests [45295]
-* fixed issue with 2.5 type representations in py.test [45483, 45484]
-* made that internal reporting issues displaying is done atomically in py.test
- [45518]
-* made that non-existing files are igored by the py.lookup script [45519]
-* improved exception name creation in py.test [45535]
-* made that less threads are used in execnet [merge in 45539]
-* removed lock required for atomical reporting issue displaying in py.test
- [45545]
-* removed globals from execnet [45541, 45547]
-* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit
- get called in 2.5 (py.execnet) [45548]
-* fixed bug in joining threads in py.execnet's servemain [45549]
-* refactored py.test.rsession tests to not rely on exact output format anymore
- [45646]
-* using repr() on test outcome [45647]
-* added 'Reason' classes for py.test.skip() [45648, 45649]
-* killed some unnecessary sanity check in py.test.collect [45655]
-* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only
- usable by Administrators [45901]
-* added support for locking and non-recursive commits to py.path.svnwc [45994]
-* locking files in py.execnet to prevent CPython from segfaulting [46010]
-* added export() method to py.path.svnurl
-* fixed -d -x in py.test [47277]
-* fixed argument concatenation problem in py.path.svnwc [49423]
-* restore py.test behaviour that it exits with code 1 when there are failures
- [49974]
-* don't fail on html files that don't have an accompanying .txt file [50606]
-* fixed 'utestconvert.py < input' [50645]
-* small fix for code indentation in py.code.source [50755]
-* fix _docgen.py documentation building [51285]
-* improved checks for source representation of code blocks in py.test [51292]
-* added support for passing authentication to py.path.svn* objects [52000,
- 52001]
-* removed sorted() call for py.apigen tests in favour of [].sort() to support
- Python 2.3 [52481]
diff --git a/tests/wpt/web-platform-tests/tools/pytest/CONTRIBUTING.rst b/tests/wpt/web-platform-tests/tools/pytest/CONTRIBUTING.rst
deleted file mode 100644
index 75ee3ec32d3..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/CONTRIBUTING.rst
+++ /dev/null
@@ -1,253 +0,0 @@
-============================
-Contribution getting started
-============================
-
-Contributions are highly welcomed and appreciated. Every little help counts,
-so do not hesitate!
-
-.. contents:: Contribution links
- :depth: 2
-
-
-.. _submitfeedback:
-
-Feature requests and feedback
------------------------------
-
-Do you like pytest? Share some love on Twitter or in your blog posts!
-
-We'd also like to hear about your propositions and suggestions. Feel free to
-`submit them as issues <https://github.com/pytest-dev/pytest/issues>`_ and:
-
-* Explain in detail how they should work.
-* Keep the scope as narrow as possible. This will make it easier to implement.
-
-
-.. _reportbugs:
-
-Report bugs
------------
-
-Report bugs for pytest in the `issue tracker <https://github.com/pytest-dev/pytest/issues>`_.
-
-If you are reporting a bug, please include:
-
-* Your operating system name and version.
-* Any details about your local setup that might be helpful in troubleshooting,
- specifically Python interpreter version,
- installed libraries and pytest version.
-* Detailed steps to reproduce the bug.
-
-If you can write a demonstration test that currently fails but should pass (xfail),
-that is a very useful commit to make as well, even if you can't find how
-to fix the bug yet.
-
-
-.. _fixbugs:
-
-Fix bugs
---------
-
-Look through the GitHub issues for bugs. Here is sample filter you can use:
-https://github.com/pytest-dev/pytest/labels/bug
-
-:ref:`Talk <contact>` to developers to find out how you can fix specific bugs.
-
-Don't forget to check the issue trackers of your favourite plugins, too!
-
-.. _writeplugins:
-
-Implement features
-------------------
-
-Look through the GitHub issues for enhancements. Here is sample filter you
-can use:
-https://github.com/pytest-dev/pytest/labels/enhancement
-
-:ref:`Talk <contact>` to developers to find out how you can implement specific
-features.
-
-Write documentation
--------------------
-
-pytest could always use more documentation. What exactly is needed?
-
-* More complementary documentation. Have you perhaps found something unclear?
-* Documentation translations. We currently have only English.
-* Docstrings. There can never be too many of them.
-* Blog posts, articles and such -- they're all very appreciated.
-
-You can also edit documentation files directly in the Github web interface
-without needing to make a fork and local copy. This can be convenient for
-small fixes.
-
-
-.. _submitplugin:
-
-Submitting Plugins to pytest-dev
---------------------------------
-
-Pytest development of the core, some plugins and support code happens
-in repositories living under the ``pytest-dev`` organisations:
-
-- `pytest-dev on GitHub <https://github.com/pytest-dev>`_
-
-- `pytest-dev on Bitbucket <https://bitbucket.org/pytest-dev>`_
-
-All pytest-dev Contributors team members have write access to all contained
-repositories. pytest core and plugins are generally developed
-using `pull requests`_ to respective repositories.
-
-The objectives of the ``pytest-dev`` organisation are:
-
-* Having a central location for popular pytest plugins
-* Sharing some of the maintenance responsibility (in case a maintainer no longer whishes to maintain a plugin)
-
-You can submit your plugin by subscribing to the `pytest-dev mail list
-<https://mail.python.org/mailman/listinfo/pytest-dev>`_ and writing a
-mail pointing to your existing pytest plugin repository which must have
-the following:
-
-- PyPI presence with a ``setup.py`` that contains a license, ``pytest-``
- prefixed name, version number, authors, short and long description.
-
-- a ``tox.ini`` for running tests using `tox <http://tox.testrun.org>`_.
-
-- a ``README.txt`` describing how to use the plugin and on which
- platforms it runs.
-
-- a ``LICENSE.txt`` file or equivalent containing the licensing
- information, with matching info in ``setup.py``.
-
-- an issue tracker for bug reports and enhancement requests.
-
-If no contributor strongly objects and two agree, the repository can then be
-transferred to the ``pytest-dev`` organisation.
-
-Here's a rundown of how a repository transfer usually proceeds
-(using a repository named ``joedoe/pytest-xyz`` as example):
-
-* One of the ``pytest-dev`` administrators creates:
-
- - ``pytest-xyz-admin`` team, with full administration rights to
- ``pytest-dev/pytest-xyz``.
- - ``pytest-xyz-developers`` team, with write access to
- ``pytest-dev/pytest-xyz``.
-
-* ``joedoe`` is invited to the ``pytest-xyz-admin`` team;
-
-* After accepting the invitation, ``joedoe`` transfers the repository from its
- original location to ``pytest-dev/pytest-xyz`` (A nice feature is that GitHub handles URL redirection from
- the old to the new location automatically).
-
-* ``joedoe`` is free to add any other collaborators to the
- ``pytest-xyz-admin`` or ``pytest-xyz-developers`` team as desired.
-
-The ``pytest-dev/Contributors`` team has write access to all projects, and
-every project administrator is in it. We recommend that each plugin has at least three
-people who have the right to release to PyPI.
-
-Repository owners can be assured that no ``pytest-dev`` administrator will ever make
-releases of your repository or take ownership in any way, except in rare cases
-where someone becomes unresponsive after months of contact attempts.
-As stated, the objective is to share maintenance and avoid "plugin-abandon".
-
-
-.. _`pull requests`:
-.. _pull-requests:
-
-Preparing Pull Requests on GitHub
----------------------------------
-
-There's an excellent tutorial on how Pull Requests work in the
-`GitHub Help Center <https://help.github.com/articles/using-pull-requests/>`_
-
-
-.. note::
- What is a "pull request"? It informs project's core developers about the
- changes you want to review and merge. Pull requests are stored on
- `GitHub servers <https://github.com/pytest-dev/pytest/pulls>`_.
- Once you send pull request, we can discuss it's potential modifications and
- even add more commits to it later on.
-
-There's an excellent tutorial on how Pull Requests work in the
-`GitHub Help Center <https://help.github.com/articles/using-pull-requests/>`_,
-but here is a simple overview:
-
-#. Fork the
- `pytest GitHub repository <https://github.com/pytest-dev/pytest>`__. It's
- fine to use ``pytest`` as your fork repository name because it will live
- under your user.
-
-#. Clone your fork locally using `git <https://git-scm.com/>`_ and create a branch::
-
- $ git clone git@github.com:YOUR_GITHUB_USERNAME/pytest.git
- $ cd pytest
- # now, to fix a bug create your own branch off "master":
-
- $ git checkout -b your-bugfix-branch-name master
-
- # or to instead add a feature create your own branch off "features":
-
- $ git checkout -b your-feature-branch-name features
-
- Given we have "major.minor.micro" version numbers, bugfixes will usually
- be released in micro releases whereas features will be released in
- minor releases and incompatible changes in major releases.
-
- If you need some help with Git, follow this quick start
- guide: https://git.wiki.kernel.org/index.php/QuickStart
-
-#. Install tox
-
- Tox is used to run all the tests and will automatically setup virtualenvs
- to run the tests in.
- (will implicitly use http://www.virtualenv.org/en/latest/)::
-
- $ pip install tox
-
-#. Run all the tests
-
- You need to have Python 2.7 and 3.5 available in your system. Now
- running tests is as simple as issuing this command::
-
- $ python runtox.py -e linting,py27,py35
-
- This command will run tests via the "tox" tool against Python 2.7 and 3.5
- and also perform "lint" coding-style checks. ``runtox.py`` is
- a thin wrapper around ``tox`` which installs from a development package
- index where newer (not yet released to pypi) versions of dependencies
- (especially ``py``) might be present.
-
-#. You can now edit your local working copy.
-
- You can now make the changes you want and run the tests again as necessary.
-
- To run tests on py27 and pass options to pytest (e.g. enter pdb on failure)
- to pytest you can do::
-
- $ python runtox.py -e py27 -- --pdb
-
- or to only run tests in a particular test module on py35::
-
- $ python runtox.py -e py35 -- testing/test_config.py
-
-#. Commit and push once your tests pass and you are happy with your change(s)::
-
- $ git commit -a -m "<commit message>"
- $ git push -u
-
- Make sure you add a CHANGELOG message, and add yourself to AUTHORS. If you
- are unsure about either of these steps, submit your pull request and we'll
- help you fix it up.
-
-#. Finally, submit a pull request through the GitHub website using this data::
-
- head-fork: YOUR_GITHUB_USERNAME/pytest
- compare: your-branch-name
-
- base-fork: pytest-dev/pytest
- base: master # if it's a bugfix
- base: feature # if it's a feature
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/HOWTORELEASE.rst b/tests/wpt/web-platform-tests/tools/pytest/HOWTORELEASE.rst
deleted file mode 100644
index 3ebfa28b1ed..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/HOWTORELEASE.rst
+++ /dev/null
@@ -1,92 +0,0 @@
-How to release pytest
---------------------------------------------
-
-Note: this assumes you have already registered on pypi.
-
-0. create the branch release-VERSION
- use features as base for minor/major releases
- and master as base for bugfix releases
-
-1. Bump version numbers in _pytest/__init__.py (setup.py reads it)
-
-2. Check and finalize CHANGELOG
-
-3. Write doc/en/announce/release-VERSION.txt and include
- it in doc/en/announce/index.txt::
-
- git log 2.8.2..HEAD --format='%aN' | sort -u # lists the names of authors involved
-
-4. Use devpi for uploading a release tarball to a staging area::
-
- devpi use https://devpi.net/USER/dev
- devpi upload --formats sdist,bdist_wheel
-
-5. Run from multiple machines::
-
- devpi use https://devpi.net/USER/dev
- devpi test pytest==VERSION
-
-6. Check that tests pass for relevant combinations with::
-
- devpi list pytest
-
- or look at failures with "devpi list -f pytest".
-
-7. Regenerate the docs examples using tox, and check for regressions::
-
- tox -e regen
- git diff
-
-
-8. Build the docs, you need a virtualenv with py and sphinx
- installed::
-
- cd doc/en
- make html
-
- Commit any changes before tagging the release.
-
-9. Tag the release::
-
- git tag VERSION
- git push
-
-10. Upload the docs using doc/en/Makefile::
-
- cd doc/en
- make install # or "installall" if you have LaTeX installed for PDF
-
- This requires ssh-login permission on pytest.org because it uses
- rsync.
- Note that the ``install`` target of ``doc/en/Makefile`` defines where the
- rsync goes to, typically to the "latest" section of pytest.org.
-
- If you are making a minor release (e.g. 5.4), you also need to manually
- create a symlink for "latest"::
-
- ssh pytest-dev@pytest.org
- ln -s 5.4 latest
-
- Browse to pytest.org to verify.
-
-11. Publish to pypi::
-
- devpi push pytest-VERSION pypi:NAME
-
- where NAME is the name of pypi.python.org as configured in your ``~/.pypirc``
- file `for devpi <http://doc.devpi.net/latest/quickstart-releaseprocess.html?highlight=pypirc#devpi-push-releasing-to-an-external-index>`_.
-
-
-12. Send release announcement to mailing lists:
-
- - pytest-dev
- - testing-in-python
- - python-announce-list@python.org
-
-
-13. **after the release** Bump the version number in ``_pytest/__init__.py``,
- to the next Minor release version (i.e. if you released ``pytest-2.8.0``,
- set it to ``pytest-2.9.0.dev1``).
-
-14. merge the actual release into the master branch and do a pull request against it
-15. merge from master to features
diff --git a/tests/wpt/web-platform-tests/tools/pytest/ISSUES.txt b/tests/wpt/web-platform-tests/tools/pytest/ISSUES.txt
deleted file mode 100644
index 081d727e86a..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/ISSUES.txt
+++ /dev/null
@@ -1,365 +0,0 @@
-
-
-recorder = monkeypatch.function(".......")
--------------------------------------------------------------
-tags: nice feature
-
-Like monkeypatch.replace but sets a mock-like call recorder:
-
- recorder = monkeypatch.function("os.path.abspath")
- recorder.set_return("/hello")
- os.path.abspath("hello")
- call, = recorder.calls
- assert call.args.path == "hello"
- assert call.returned == "/hello"
- ...
-
-Unlike mock, "args.path" acts on the parsed auto-spec'ed ``os.path.abspath``
-so it's independent from if the client side called "os.path.abspath(path=...)"
-or "os.path.abspath('positional')".
-
-
-refine parametrize API
--------------------------------------------------------------
-tags: critical feature
-
-extend metafunc.parametrize to directly support indirection, example:
-
- def setupdb(request, config):
- # setup "resource" based on test request and the values passed
- # in to parametrize. setupfunc is called for each such value.
- # you may use request.addfinalizer() or request.cached_setup ...
- return dynamic_setup_database(val)
-
- @pytest.mark.parametrize("db", ["pg", "mysql"], setupfunc=setupdb)
- def test_heavy_functional_test(db):
- ...
-
-There would be no need to write or explain funcarg factories and
-their special __ syntax.
-
-The examples and improvements should also show how to put the parametrize
-decorator to a class, to a module or even to a directory. For the directory
-part a conftest.py content like this::
-
- pytestmark = [
- @pytest.mark.parametrize_setup("db", ...),
- ]
-
-probably makes sense in order to keep the declarative nature. This mirrors
-the marker-mechanism with respect to a test module but puts it to a directory
-scale.
-
-When doing larger scoped parametrization it probably becomes necessary
-to allow parametrization to be ignored if the according parameter is not
-used (currently any parametrized argument that is not present in a function will cause a ValueError). Example:
-
- @pytest.mark.parametrize("db", ..., mustmatch=False)
-
-means to not raise an error but simply ignore the parametrization
-if the signature of a decorated function does not match. XXX is it
-not sufficient to always allow non-matches?
-
-
-allow parametrized attributes on classes
---------------------------------------------------
-
-tags: wish 2.4
-
-example:
-
- @pytest.mark.parametrize_attr("db", setupfunc, [1,2,3], scope="class")
- @pytest.mark.parametrize_attr("tmp", setupfunc, scope="...")
- class TestMe:
- def test_hello(self):
- access self.db ...
-
-this would run the test_hello() function three times with three
-different values for self.db. This could also work with unittest/nose
-style tests, i.e. it leverages existing test suites without needing
-to rewrite them. Together with the previously mentioned setup_test()
-maybe the setupfunc could be omitted?
-
-optimizations
----------------------------------------------------------------
-tags: 2.4 core
-
-- look at ihook optimization such that all lookups for
- hooks relating to the same fspath are cached.
-
-fix start/finish partial finailization problem
----------------------------------------------------------------
-tags: bug core
-
-if a configure/runtest_setup/sessionstart/... hook invocation partially
-fails the sessionfinishes is not called. Each hook implementation
-should better be repsonsible for registering a cleanup/finalizer
-appropriately to avoid this issue. Moreover/Alternatively, we could
-record which implementations of a hook succeeded and only call their
-teardown.
-
-
-relax requirement to have tests/testing contain an __init__
-----------------------------------------------------------------
-tags: feature
-bb: http://bitbucket.org/hpk42/py-trunk/issue/64
-
-A local test run of a "tests" directory may work
-but a remote one fail because the tests directory
-does not contain an "__init__.py". Either give
-an error or make it work without the __init__.py
-i.e. port the nose-logic of unloading a test module.
-
-customize test function collection
--------------------------------------------------------
-tags: feature
-
-- introduce pytest.mark.nocollect for not considering a function for
- test collection at all. maybe also introduce a pytest.mark.test to
- explicitly mark a function to become a tested one. Lookup JUnit ways
- of tagging tests.
-
-introduce pytest.mark.importorskip
--------------------------------------------------------
-tags: feature
-
-in addition to the imperative pytest.importorskip also introduce
-a pytest.mark.importorskip so that the test count is more correct.
-
-
-introduce pytest.mark.platform
--------------------------------------------------------
-tags: feature
-
-Introduce nice-to-spell platform-skipping, examples:
-
- @pytest.mark.platform("python3")
- @pytest.mark.platform("not python3")
- @pytest.mark.platform("win32 and not python3")
- @pytest.mark.platform("darwin")
- @pytest.mark.platform("not (jython and win32)")
- @pytest.mark.platform("not (jython and win32)", xfail=True)
-
-etc. Idea is to allow Python expressions which can operate
-on common spellings for operating systems and python
-interpreter versions.
-
-pytest.mark.xfail signature change
--------------------------------------------------------
-tags: feature
-
-change to pytest.mark.xfail(reason, (optional)condition)
-to better implement the word meaning. It also signals
-better that we always have some kind of an implementation
-reason that can be formualated.
-Compatibility? how to introduce a new name/keep compat?
-
-allow to non-intrusively apply skipfs/xfail/marks
----------------------------------------------------
-tags: feature
-
-use case: mark a module or directory structures
-to be skipped on certain platforms (i.e. no import
-attempt will be made).
-
-consider introducing a hook/mechanism that allows to apply marks
-from conftests or plugins. (See extended parametrization)
-
-
-explicit referencing of conftest.py files
------------------------------------------
-tags: feature
-
-allow to name conftest.py files (in sub directories) that should
-be imported early, as to include command line options.
-
-improve central pytest ini file
--------------------------------
-tags: feature
-
-introduce more declarative configuration options:
-- (to-be-collected test directories)
-- required plugins
-- test func/class/file matching patterns
-- skip/xfail (non-intrusive)
-- pytest.ini and tox.ini and setup.cfg configuration in the same file
-
-new documentation
-----------------------------------
-tags: feature
-
-- logo pytest
-- examples for unittest or functional testing
-- resource management for functional testing
-- patterns: page object
-
-have imported module mismatch honour relative paths
---------------------------------------------------------
-tags: bug
-
-With 1.1.1 pytest fails at least on windows if an import
-is relative and compared against an absolute conftest.py
-path. Normalize.
-
-consider globals: pytest.ensuretemp and config
---------------------------------------------------------------
-tags: experimental-wish
-
-consider deprecating pytest.ensuretemp and pytest.config
-to further reduce pytest globality. Also consider
-having pytest.config and ensuretemp coming from
-a plugin rather than being there from the start.
-
-
-consider pytest_addsyspath hook
------------------------------------------
-tags: wish
-
-pytest could call a new pytest_addsyspath() in order to systematically
-allow manipulation of sys.path and to inhibit it via --no-addsyspath
-in order to more easily run against installed packages.
-
-Alternatively it could also be done via the config object
-and pytest_configure.
-
-
-
-deprecate global pytest.config usage
-----------------------------------------------------------------
-tags: feature
-
-pytest.ensuretemp and pytest.config are probably the last
-objects containing global state. Often using them is not
-necessary. This is about trying to get rid of them, i.e.
-deprecating them and checking with PyPy's usages as well
-as others.
-
-remove deprecated bits in collect.py
--------------------------------------------------------------------
-tags: feature
-
-In an effort to further simplify code, review and remove deprecated bits
-in collect.py. Probably good:
-- inline consider_file/dir methods, no need to have them
- subclass-overridable because of hooks
-
-implement fslayout decorator
----------------------------------
-tags: feature
-
-Improve the way how tests can work with pre-made examples,
-keeping the layout close to the test function:
-
-@pytest.mark.fslayout("""
- conftest.py:
- # empty
- tests/
- test_%(NAME)s: # becomes test_run1.py
- def test_function(self):
- pass
-""")
-def test_run(pytester, fslayout):
- p = fslayout.findone("test_*.py")
- result = pytester.runpytest(p)
- assert result.ret == 0
- assert result.passed == 1
-
-Another idea is to allow to define a full scenario including the run
-in one content string::
-
- runscenario("""
- test_{TESTNAME}.py:
- import pytest
- @pytest.mark.xfail
- def test_that_fails():
- assert 0
-
- @pytest.mark.skipif("True")
- def test_hello():
- pass
-
- conftest.py:
- import pytest
- def pytest_runsetup_setup(item):
- pytest.skip("abc")
-
- runpytest -rsxX
- *SKIP*{TESTNAME}*
- *1 skipped*
- """)
-
-This could be run with at least three different ways to invoke pytest:
-through the shell, through "python -m pytest" and inlined. As inlined
-would be the fastest it could be run first (or "--fast" mode).
-
-
-Create isolate plugin
----------------------
-tags: feature
-
-The idea is that you can e.g. import modules in a test and afterwards
-sys.modules, sys.meta_path etc would be reverted. It can go further
-then just importing however, e.g. current working directory, file
-descriptors, ...
-
-This would probably be done by marking::
-
- @pytest.mark.isolate(importing=True, cwd=True, fds=False)
- def test_foo():
- ...
-
-With the possibility of doing this globally in an ini-file.
-
-
-fnmatch for test names
-----------------------
-tags: feature-wish
-
-various testsuites use suffixes instead of prefixes for test classes
-also it lends itself to bdd style test names::
-
- class UserBehaviour:
- def anonymous_should_not_have_inbox(user):
- ...
- def registred_should_have_inbox(user):
- ..
-
-using the following in pytest.ini::
-
- [pytest]
- python_classes = Test *Behaviour *Test
- python_functions = test *_should_*
-
-
-mechanism for running named parts of tests with different reporting behaviour
-------------------------------------------------------------------------------
-tags: feature-wish-incomplete
-
-a few use-cases come to mind:
-
-* fail assertions and record that without stopping a complete test
-
- * this is in particular hepfull if a small bit of a test is known to fail/xfail::
-
- def test_fun():
- with pytest.section('fdcheck, marks=pytest.mark.xfail_if(...)):
- breaks_on_windows()
-
-* divide functional/acceptance tests into sections
-* provide a different mechanism for generators, maybe something like::
-
- def pytest_runtest_call(item)
- if not generator:
- ...
- prepare_check = GeneratorCheckprepare()
-
- gen = item.obj(**fixtures)
- for check in gen
- id, call = prepare_check(check)
- # bubble should only prevent exception propagation after a failure
- # the whole test should still fail
- # there might be need for a lower level api and taking custom markers into account
- with pytest.section(id, bubble=False):
- call()
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/LICENSE b/tests/wpt/web-platform-tests/tools/pytest/LICENSE
deleted file mode 100644
index 9e27bd78419..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2004-2016 Holger Krekel and others
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/MANIFEST.in b/tests/wpt/web-platform-tests/tools/pytest/MANIFEST.in
deleted file mode 100644
index 266a9184dc3..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/MANIFEST.in
+++ /dev/null
@@ -1,34 +0,0 @@
-include CHANGELOG.rst
-include LICENSE
-include AUTHORS
-
-include README.rst
-include CONTRIBUTING.rst
-
-include tox.ini
-include setup.py
-
-include .coveragerc
-
-include plugin-test.sh
-include requirements-docs.txt
-include runtox.py
-
-recursive-include bench *.py
-recursive-include extra *.py
-
-graft testing
-graft doc
-
-exclude _pytest/impl
-
-graft _pytest/vendored_packages
-
-recursive-exclude * *.pyc *.pyo
-
-exclude appveyor/install.ps1
-exclude appveyor.yml
-exclude appveyor
-
-exclude ISSUES.txt
-exclude HOWTORELEASE.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/README.rst b/tests/wpt/web-platform-tests/tools/pytest/README.rst
deleted file mode 100644
index 68fc92211d8..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/README.rst
+++ /dev/null
@@ -1,102 +0,0 @@
-.. image:: http://pytest.org/latest/_static/pytest1.png
- :target: http://pytest.org
- :align: center
- :alt: pytest
-
-------
-
-.. image:: https://img.shields.io/pypi/v/pytest.svg
- :target: https://pypi.python.org/pypi/pytest
-.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
- :target: https://pypi.python.org/pypi/pytest
-.. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg
- :target: https://coveralls.io/r/pytest-dev/pytest
-.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master
- :target: https://travis-ci.org/pytest-dev/pytest
-.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
- :target: https://ci.appveyor.com/project/pytestbot/pytest
-
-The ``pytest`` framework makes it easy to write small tests, yet
-scales to support complex functional testing for applications and libraries.
-
-An example of a simple test:
-
-.. code-block:: python
-
- # content of test_sample.py
- def func(x):
- return x + 1
-
- def test_answer():
- assert func(3) == 5
-
-
-To execute it::
-
- $ py.test
- ======= test session starts ========
- platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
- collected 1 items
-
- test_sample.py F
-
- ======= FAILURES ========
- _______ test_answer ________
-
- def test_answer():
- > assert func(3) == 5
- E assert 4 == 5
- E + where 4 = func(3)
-
- test_sample.py:5: AssertionError
- ======= 1 failed in 0.12 seconds ========
-
-Due to ``py.test``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples.
-
-
-Features
---------
-
-- Detailed info on failing `assert statements <http://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
-
-- `Auto-discovery
- <http://pytest.org/latest/goodpractices.html#python-test-discovery>`_
- of test modules and functions;
-
-- `Modular fixtures <http://pytest.org/latest/fixture.html>`_ for
- managing small or parametrized long-lived test resources;
-
-- Can run `unittest <http://pytest.org/latest/unittest.html>`_ (or trial),
- `nose <http://pytest.org/latest/nose.html>`_ test suites out of the box;
-
-- Python2.6+, Python3.2+, PyPy-2.3, Jython-2.5 (untested);
-
-- Rich plugin architecture, with over 150+ `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_ and thriving community;
-
-
-Documentation
--------------
-
-For full documentation, including installation, tutorials and PDF documents, please see http://pytest.org.
-
-
-Bugs/Requests
--------------
-
-Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features.
-
-
-Changelog
----------
-
-Consult the `Changelog <http://pytest.org/latest/changelog.html>`_ page for fixes and enhancements of each version.
-
-
-License
--------
-
-Copyright Holger Krekel and others, 2004-2016.
-
-Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
-
-.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/__init__.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/__init__.py
deleted file mode 100644
index 723fb9e855a..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-#
-__version__ = '2.9.1'
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_argcomplete.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/_argcomplete.py
deleted file mode 100644
index 955855a9648..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_argcomplete.py
+++ /dev/null
@@ -1,101 +0,0 @@
-
-"""allow bash-completion for argparse with argcomplete if installed
-needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail
-to find the magic string, so _ARGCOMPLETE env. var is never set, and
-this does not need special code.
-
-argcomplete does not support python 2.5 (although the changes for that
-are minor).
-
-Function try_argcomplete(parser) should be called directly before
-the call to ArgumentParser.parse_args().
-
-The filescompleter is what you normally would use on the positional
-arguments specification, in order to get "dirname/" after "dirn<TAB>"
-instead of the default "dirname ":
-
- optparser.add_argument(Config._file_or_dir, nargs='*'
- ).completer=filescompleter
-
-Other, application specific, completers should go in the file
-doing the add_argument calls as they need to be specified as .completer
-attributes as well. (If argcomplete is not installed, the function the
-attribute points to will not be used).
-
-SPEEDUP
-=======
-The generic argcomplete script for bash-completion
-(/etc/bash_completion.d/python-argcomplete.sh )
-uses a python program to determine startup script generated by pip.
-You can speed up completion somewhat by changing this script to include
- # PYTHON_ARGCOMPLETE_OK
-so the the python-argcomplete-check-easy-install-script does not
-need to be called to find the entry point of the code and see if that is
-marked with PYTHON_ARGCOMPLETE_OK
-
-INSTALL/DEBUGGING
-=================
-To include this support in another application that has setup.py generated
-scripts:
-- add the line:
- # PYTHON_ARGCOMPLETE_OK
- near the top of the main python entry point
-- include in the file calling parse_args():
- from _argcomplete import try_argcomplete, filescompleter
- , call try_argcomplete just before parse_args(), and optionally add
- filescompleter to the positional arguments' add_argument()
-If things do not work right away:
-- switch on argcomplete debugging with (also helpful when doing custom
- completers):
- export _ARC_DEBUG=1
-- run:
- python-argcomplete-check-easy-install-script $(which appname)
- echo $?
- will echo 0 if the magic line has been found, 1 if not
-- sometimes it helps to find early on errors using:
- _ARGCOMPLETE=1 _ARC_DEBUG=1 appname
- which should throw a KeyError: 'COMPLINE' (which is properly set by the
- global argcomplete script).
-"""
-
-import sys
-import os
-from glob import glob
-
-class FastFilesCompleter:
- 'Fast file completer class'
- def __init__(self, directories=True):
- self.directories = directories
-
- def __call__(self, prefix, **kwargs):
- """only called on non option completions"""
- if os.path.sep in prefix[1:]: #
- prefix_dir = len(os.path.dirname(prefix) + os.path.sep)
- else:
- prefix_dir = 0
- completion = []
- globbed = []
- if '*' not in prefix and '?' not in prefix:
- if prefix[-1] == os.path.sep: # we are on unix, otherwise no bash
- globbed.extend(glob(prefix + '.*'))
- prefix += '*'
- globbed.extend(glob(prefix))
- for x in sorted(globbed):
- if os.path.isdir(x):
- x += '/'
- # append stripping the prefix (like bash, not like compgen)
- completion.append(x[prefix_dir:])
- return completion
-
-if os.environ.get('_ARGCOMPLETE'):
- try:
- import argcomplete.completers
- except ImportError:
- sys.exit(-1)
- filescompleter = FastFilesCompleter()
-
- def try_argcomplete(parser):
- argcomplete.autocomplete(parser)
-else:
- def try_argcomplete(parser): pass
- filescompleter = None
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/__init__.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/__init__.py
deleted file mode 100644
index c046b9716ca..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/__init__.py
+++ /dev/null
@@ -1,12 +0,0 @@
-""" python inspection/code generation API """
-from .code import Code # noqa
-from .code import ExceptionInfo # noqa
-from .code import Frame # noqa
-from .code import Traceback # noqa
-from .code import getrawcode # noqa
-from .code import patch_builtins # noqa
-from .code import unpatch_builtins # noqa
-from .source import Source # noqa
-from .source import compile_ as compile # noqa
-from .source import getfslineno # noqa
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/_py2traceback.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/_py2traceback.py
deleted file mode 100644
index a830d9899ae..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/_py2traceback.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# copied from python-2.7.3's traceback.py
-# CHANGES:
-# - some_str is replaced, trying to create unicode strings
-#
-import types
-
-def format_exception_only(etype, value):
- """Format the exception part of a traceback.
-
- The arguments are the exception type and value such as given by
- sys.last_type and sys.last_value. The return value is a list of
- strings, each ending in a newline.
-
- Normally, the list contains a single string; however, for
- SyntaxError exceptions, it contains several lines that (when
- printed) display detailed information about where the syntax
- error occurred.
-
- The message indicating which exception occurred is always the last
- string in the list.
-
- """
-
- # An instance should not have a meaningful value parameter, but
- # sometimes does, particularly for string exceptions, such as
- # >>> raise string1, string2 # deprecated
- #
- # Clear these out first because issubtype(string1, SyntaxError)
- # would throw another exception and mask the original problem.
- if (isinstance(etype, BaseException) or
- isinstance(etype, types.InstanceType) or
- etype is None or type(etype) is str):
- return [_format_final_exc_line(etype, value)]
-
- stype = etype.__name__
-
- if not issubclass(etype, SyntaxError):
- return [_format_final_exc_line(stype, value)]
-
- # It was a syntax error; show exactly where the problem was found.
- lines = []
- try:
- msg, (filename, lineno, offset, badline) = value.args
- except Exception:
- pass
- else:
- filename = filename or "<string>"
- lines.append(' File "%s", line %d\n' % (filename, lineno))
- if badline is not None:
- if isinstance(badline, bytes): # python 2 only
- badline = badline.decode('utf-8', 'replace')
- lines.append(u' %s\n' % badline.strip())
- if offset is not None:
- caretspace = badline.rstrip('\n')[:offset].lstrip()
- # non-space whitespace (likes tabs) must be kept for alignment
- caretspace = ((c.isspace() and c or ' ') for c in caretspace)
- # only three spaces to account for offset1 == pos 0
- lines.append(' %s^\n' % ''.join(caretspace))
- value = msg
-
- lines.append(_format_final_exc_line(stype, value))
- return lines
-
-def _format_final_exc_line(etype, value):
- """Return a list of a single line -- normal case for format_exception_only"""
- valuestr = _some_str(value)
- if value is None or not valuestr:
- line = "%s\n" % etype
- else:
- line = "%s: %s\n" % (etype, valuestr)
- return line
-
-def _some_str(value):
- try:
- return unicode(value)
- except Exception:
- try:
- return str(value)
- except Exception:
- pass
- return '<unprintable %s object>' % type(value).__name__
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/code.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/code.py
deleted file mode 100644
index bc68aac5548..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/code.py
+++ /dev/null
@@ -1,795 +0,0 @@
-import sys
-from inspect import CO_VARARGS, CO_VARKEYWORDS
-
-import py
-
-builtin_repr = repr
-
-reprlib = py.builtin._tryimport('repr', 'reprlib')
-
-if sys.version_info[0] >= 3:
- from traceback import format_exception_only
-else:
- from ._py2traceback import format_exception_only
-
-class Code(object):
- """ wrapper around Python code objects """
- def __init__(self, rawcode):
- if not hasattr(rawcode, "co_filename"):
- rawcode = getrawcode(rawcode)
- try:
- self.filename = rawcode.co_filename
- self.firstlineno = rawcode.co_firstlineno - 1
- self.name = rawcode.co_name
- except AttributeError:
- raise TypeError("not a code object: %r" %(rawcode,))
- self.raw = rawcode
-
- def __eq__(self, other):
- return self.raw == other.raw
-
- def __ne__(self, other):
- return not self == other
-
- @property
- def path(self):
- """ return a path object pointing to source code (note that it
- might not point to an actually existing file). """
- p = py.path.local(self.raw.co_filename)
- # maybe don't try this checking
- if not p.check():
- # XXX maybe try harder like the weird logic
- # in the standard lib [linecache.updatecache] does?
- p = self.raw.co_filename
- return p
-
- @property
- def fullsource(self):
- """ return a _pytest._code.Source object for the full source file of the code
- """
- from _pytest._code import source
- full, _ = source.findsource(self.raw)
- return full
-
- def source(self):
- """ return a _pytest._code.Source object for the code object's source only
- """
- # return source only for that part of code
- import _pytest._code
- return _pytest._code.Source(self.raw)
-
- def getargs(self, var=False):
- """ return a tuple with the argument names for the code object
-
- if 'var' is set True also return the names of the variable and
- keyword arguments when present
- """
- # handfull shortcut for getting args
- raw = self.raw
- argcount = raw.co_argcount
- if var:
- argcount += raw.co_flags & CO_VARARGS
- argcount += raw.co_flags & CO_VARKEYWORDS
- return raw.co_varnames[:argcount]
-
-class Frame(object):
- """Wrapper around a Python frame holding f_locals and f_globals
- in which expressions can be evaluated."""
-
- def __init__(self, frame):
- self.lineno = frame.f_lineno - 1
- self.f_globals = frame.f_globals
- self.f_locals = frame.f_locals
- self.raw = frame
- self.code = Code(frame.f_code)
-
- @property
- def statement(self):
- """ statement this frame is at """
- import _pytest._code
- if self.code.fullsource is None:
- return _pytest._code.Source("")
- return self.code.fullsource.getstatement(self.lineno)
-
- def eval(self, code, **vars):
- """ evaluate 'code' in the frame
-
- 'vars' are optional additional local variables
-
- returns the result of the evaluation
- """
- f_locals = self.f_locals.copy()
- f_locals.update(vars)
- return eval(code, self.f_globals, f_locals)
-
- def exec_(self, code, **vars):
- """ exec 'code' in the frame
-
- 'vars' are optiona; additional local variables
- """
- f_locals = self.f_locals.copy()
- f_locals.update(vars)
- py.builtin.exec_(code, self.f_globals, f_locals )
-
- def repr(self, object):
- """ return a 'safe' (non-recursive, one-line) string repr for 'object'
- """
- return py.io.saferepr(object)
-
- def is_true(self, object):
- return object
-
- def getargs(self, var=False):
- """ return a list of tuples (name, value) for all arguments
-
- if 'var' is set True also include the variable and keyword
- arguments when present
- """
- retval = []
- for arg in self.code.getargs(var):
- try:
- retval.append((arg, self.f_locals[arg]))
- except KeyError:
- pass # this can occur when using Psyco
- return retval
-
-class TracebackEntry(object):
- """ a single entry in a traceback """
-
- _repr_style = None
- exprinfo = None
-
- def __init__(self, rawentry):
- self._rawentry = rawentry
- self.lineno = rawentry.tb_lineno - 1
-
- def set_repr_style(self, mode):
- assert mode in ("short", "long")
- self._repr_style = mode
-
- @property
- def frame(self):
- import _pytest._code
- return _pytest._code.Frame(self._rawentry.tb_frame)
-
- @property
- def relline(self):
- return self.lineno - self.frame.code.firstlineno
-
- def __repr__(self):
- return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1)
-
- @property
- def statement(self):
- """ _pytest._code.Source object for the current statement """
- source = self.frame.code.fullsource
- return source.getstatement(self.lineno)
-
- @property
- def path(self):
- """ path to the source code """
- return self.frame.code.path
-
- def getlocals(self):
- return self.frame.f_locals
- locals = property(getlocals, None, None, "locals of underlaying frame")
-
- def reinterpret(self):
- """Reinterpret the failing statement and returns a detailed information
- about what operations are performed."""
- from _pytest.assertion.reinterpret import reinterpret
- if self.exprinfo is None:
- source = py.builtin._totext(self.statement).strip()
- x = reinterpret(source, self.frame, should_fail=True)
- if not py.builtin._istext(x):
- raise TypeError("interpret returned non-string %r" % (x,))
- self.exprinfo = x
- return self.exprinfo
-
- def getfirstlinesource(self):
- # on Jython this firstlineno can be -1 apparently
- return max(self.frame.code.firstlineno, 0)
-
- def getsource(self, astcache=None):
- """ return failing source code. """
- # we use the passed in astcache to not reparse asttrees
- # within exception info printing
- from _pytest._code.source import getstatementrange_ast
- source = self.frame.code.fullsource
- if source is None:
- return None
- key = astnode = None
- if astcache is not None:
- key = self.frame.code.path
- if key is not None:
- astnode = astcache.get(key, None)
- start = self.getfirstlinesource()
- try:
- astnode, _, end = getstatementrange_ast(self.lineno, source,
- astnode=astnode)
- except SyntaxError:
- end = self.lineno + 1
- else:
- if key is not None:
- astcache[key] = astnode
- return source[start:end]
-
- source = property(getsource)
-
- def ishidden(self):
- """ return True if the current frame has a var __tracebackhide__
- resolving to True
-
- mostly for internal use
- """
- try:
- return self.frame.f_locals['__tracebackhide__']
- except KeyError:
- try:
- return self.frame.f_globals['__tracebackhide__']
- except KeyError:
- return False
-
- def __str__(self):
- try:
- fn = str(self.path)
- except py.error.Error:
- fn = '???'
- name = self.frame.code.name
- try:
- line = str(self.statement).lstrip()
- except KeyboardInterrupt:
- raise
- except:
- line = "???"
- return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line)
-
- def name(self):
- return self.frame.code.raw.co_name
- name = property(name, None, None, "co_name of underlaying code")
-
-class Traceback(list):
- """ Traceback objects encapsulate and offer higher level
- access to Traceback entries.
- """
- Entry = TracebackEntry
- def __init__(self, tb):
- """ initialize from given python traceback object. """
- if hasattr(tb, 'tb_next'):
- def f(cur):
- while cur is not None:
- yield self.Entry(cur)
- cur = cur.tb_next
- list.__init__(self, f(tb))
- else:
- list.__init__(self, tb)
-
- def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None):
- """ return a Traceback instance wrapping part of this Traceback
-
- by provding any combination of path, lineno and firstlineno, the
- first frame to start the to-be-returned traceback is determined
-
- this allows cutting the first part of a Traceback instance e.g.
- for formatting reasons (removing some uninteresting bits that deal
- with handling of the exception/traceback)
- """
- for x in self:
- code = x.frame.code
- codepath = code.path
- if ((path is None or codepath == path) and
- (excludepath is None or not hasattr(codepath, 'relto') or
- not codepath.relto(excludepath)) and
- (lineno is None or x.lineno == lineno) and
- (firstlineno is None or x.frame.code.firstlineno == firstlineno)):
- return Traceback(x._rawentry)
- return self
-
- def __getitem__(self, key):
- val = super(Traceback, self).__getitem__(key)
- if isinstance(key, type(slice(0))):
- val = self.__class__(val)
- return val
-
- def filter(self, fn=lambda x: not x.ishidden()):
- """ return a Traceback instance with certain items removed
-
- fn is a function that gets a single argument, a TracebackItem
- instance, and should return True when the item should be added
- to the Traceback, False when not
-
- by default this removes all the TracebackItems which are hidden
- (see ishidden() above)
- """
- return Traceback(filter(fn, self))
-
- def getcrashentry(self):
- """ return last non-hidden traceback entry that lead
- to the exception of a traceback.
- """
- for i in range(-1, -len(self)-1, -1):
- entry = self[i]
- if not entry.ishidden():
- return entry
- return self[-1]
-
- def recursionindex(self):
- """ return the index of the frame/TracebackItem where recursion
- originates if appropriate, None if no recursion occurred
- """
- cache = {}
- for i, entry in enumerate(self):
- # id for the code.raw is needed to work around
- # the strange metaprogramming in the decorator lib from pypi
- # which generates code objects that have hash/value equality
- #XXX needs a test
- key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
- #print "checking for recursion at", key
- l = cache.setdefault(key, [])
- if l:
- f = entry.frame
- loc = f.f_locals
- for otherloc in l:
- if f.is_true(f.eval(co_equal,
- __recursioncache_locals_1=loc,
- __recursioncache_locals_2=otherloc)):
- return i
- l.append(entry.frame.f_locals)
- return None
-
-co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2',
- '?', 'eval')
-
-class ExceptionInfo(object):
- """ wraps sys.exc_info() objects and offers
- help for navigating the traceback.
- """
- _striptext = ''
- def __init__(self, tup=None, exprinfo=None):
- import _pytest._code
- if tup is None:
- tup = sys.exc_info()
- if exprinfo is None and isinstance(tup[1], AssertionError):
- exprinfo = getattr(tup[1], 'msg', None)
- if exprinfo is None:
- exprinfo = str(tup[1])
- if exprinfo and exprinfo.startswith('assert '):
- self._striptext = 'AssertionError: '
- self._excinfo = tup
- #: the exception class
- self.type = tup[0]
- #: the exception instance
- self.value = tup[1]
- #: the exception raw traceback
- self.tb = tup[2]
- #: the exception type name
- self.typename = self.type.__name__
- #: the exception traceback (_pytest._code.Traceback instance)
- self.traceback = _pytest._code.Traceback(self.tb)
-
- def __repr__(self):
- return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback))
-
- def exconly(self, tryshort=False):
- """ return the exception as a string
-
- when 'tryshort' resolves to True, and the exception is a
- _pytest._code._AssertionError, only the actual exception part of
- the exception representation is returned (so 'AssertionError: ' is
- removed from the beginning)
- """
- lines = format_exception_only(self.type, self.value)
- text = ''.join(lines)
- text = text.rstrip()
- if tryshort:
- if text.startswith(self._striptext):
- text = text[len(self._striptext):]
- return text
-
- def errisinstance(self, exc):
- """ return True if the exception is an instance of exc """
- return isinstance(self.value, exc)
-
- def _getreprcrash(self):
- exconly = self.exconly(tryshort=True)
- entry = self.traceback.getcrashentry()
- path, lineno = entry.frame.code.raw.co_filename, entry.lineno
- return ReprFileLocation(path, lineno+1, exconly)
-
- def getrepr(self, showlocals=False, style="long",
- abspath=False, tbfilter=True, funcargs=False):
- """ return str()able representation of this exception info.
- showlocals: show locals per traceback entry
- style: long|short|no|native traceback style
- tbfilter: hide entries (where __tracebackhide__ is true)
-
- in case of style==native, tbfilter and showlocals is ignored.
- """
- if style == 'native':
- return ReprExceptionInfo(ReprTracebackNative(
- py.std.traceback.format_exception(
- self.type,
- self.value,
- self.traceback[0]._rawentry,
- )), self._getreprcrash())
-
- fmt = FormattedExcinfo(showlocals=showlocals, style=style,
- abspath=abspath, tbfilter=tbfilter, funcargs=funcargs)
- return fmt.repr_excinfo(self)
-
- def __str__(self):
- entry = self.traceback[-1]
- loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
- return str(loc)
-
- def __unicode__(self):
- entry = self.traceback[-1]
- loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
- return unicode(loc)
-
-
-class FormattedExcinfo(object):
- """ presenting information about failing Functions and Generators. """
- # for traceback entries
- flow_marker = ">"
- fail_marker = "E"
-
- def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False):
- self.showlocals = showlocals
- self.style = style
- self.tbfilter = tbfilter
- self.funcargs = funcargs
- self.abspath = abspath
- self.astcache = {}
-
- def _getindent(self, source):
- # figure out indent for given source
- try:
- s = str(source.getstatement(len(source)-1))
- except KeyboardInterrupt:
- raise
- except:
- try:
- s = str(source[-1])
- except KeyboardInterrupt:
- raise
- except:
- return 0
- return 4 + (len(s) - len(s.lstrip()))
-
- def _getentrysource(self, entry):
- source = entry.getsource(self.astcache)
- if source is not None:
- source = source.deindent()
- return source
-
- def _saferepr(self, obj):
- return py.io.saferepr(obj)
-
- def repr_args(self, entry):
- if self.funcargs:
- args = []
- for argname, argvalue in entry.frame.getargs(var=True):
- args.append((argname, self._saferepr(argvalue)))
- return ReprFuncArgs(args)
-
- def get_source(self, source, line_index=-1, excinfo=None, short=False):
- """ return formatted and marked up source lines. """
- import _pytest._code
- lines = []
- if source is None or line_index >= len(source.lines):
- source = _pytest._code.Source("???")
- line_index = 0
- if line_index < 0:
- line_index += len(source)
- space_prefix = " "
- if short:
- lines.append(space_prefix + source.lines[line_index].strip())
- else:
- for line in source.lines[:line_index]:
- lines.append(space_prefix + line)
- lines.append(self.flow_marker + " " + source.lines[line_index])
- for line in source.lines[line_index+1:]:
- lines.append(space_prefix + line)
- if excinfo is not None:
- indent = 4 if short else self._getindent(source)
- lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
- return lines
-
- def get_exconly(self, excinfo, indent=4, markall=False):
- lines = []
- indent = " " * indent
- # get the real exception information out
- exlines = excinfo.exconly(tryshort=True).split('\n')
- failindent = self.fail_marker + indent[1:]
- for line in exlines:
- lines.append(failindent + line)
- if not markall:
- failindent = indent
- return lines
-
- def repr_locals(self, locals):
- if self.showlocals:
- lines = []
- keys = [loc for loc in locals if loc[0] != "@"]
- keys.sort()
- for name in keys:
- value = locals[name]
- if name == '__builtins__':
- lines.append("__builtins__ = <builtins>")
- else:
- # This formatting could all be handled by the
- # _repr() function, which is only reprlib.Repr in
- # disguise, so is very configurable.
- str_repr = self._saferepr(value)
- #if len(str_repr) < 70 or not isinstance(value,
- # (list, tuple, dict)):
- lines.append("%-10s = %s" %(name, str_repr))
- #else:
- # self._line("%-10s =\\" % (name,))
- # # XXX
- # py.std.pprint.pprint(value, stream=self.excinfowriter)
- return ReprLocals(lines)
-
- def repr_traceback_entry(self, entry, excinfo=None):
- import _pytest._code
- source = self._getentrysource(entry)
- if source is None:
- source = _pytest._code.Source("???")
- line_index = 0
- else:
- # entry.getfirstlinesource() can be -1, should be 0 on jython
- line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
-
- lines = []
- style = entry._repr_style
- if style is None:
- style = self.style
- if style in ("short", "long"):
- short = style == "short"
- reprargs = self.repr_args(entry) if not short else None
- s = self.get_source(source, line_index, excinfo, short=short)
- lines.extend(s)
- if short:
- message = "in %s" %(entry.name)
- else:
- message = excinfo and excinfo.typename or ""
- path = self._makepath(entry.path)
- filelocrepr = ReprFileLocation(path, entry.lineno+1, message)
- localsrepr = None
- if not short:
- localsrepr = self.repr_locals(entry.locals)
- return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style)
- if excinfo:
- lines.extend(self.get_exconly(excinfo, indent=4))
- return ReprEntry(lines, None, None, None, style)
-
- def _makepath(self, path):
- if not self.abspath:
- try:
- np = py.path.local().bestrelpath(path)
- except OSError:
- return path
- if len(np) < len(str(path)):
- path = np
- return path
-
- def repr_traceback(self, excinfo):
- traceback = excinfo.traceback
- if self.tbfilter:
- traceback = traceback.filter()
- recursionindex = None
- if excinfo.errisinstance(RuntimeError):
- if "maximum recursion depth exceeded" in str(excinfo.value):
- recursionindex = traceback.recursionindex()
- last = traceback[-1]
- entries = []
- extraline = None
- for index, entry in enumerate(traceback):
- einfo = (last == entry) and excinfo or None
- reprentry = self.repr_traceback_entry(entry, einfo)
- entries.append(reprentry)
- if index == recursionindex:
- extraline = "!!! Recursion detected (same locals & position)"
- break
- return ReprTraceback(entries, extraline, style=self.style)
-
- def repr_excinfo(self, excinfo):
- reprtraceback = self.repr_traceback(excinfo)
- reprcrash = excinfo._getreprcrash()
- return ReprExceptionInfo(reprtraceback, reprcrash)
-
-class TerminalRepr:
- def __str__(self):
- s = self.__unicode__()
- if sys.version_info[0] < 3:
- s = s.encode('utf-8')
- return s
-
- def __unicode__(self):
- # FYI this is called from pytest-xdist's serialization of exception
- # information.
- io = py.io.TextIO()
- tw = py.io.TerminalWriter(file=io)
- self.toterminal(tw)
- return io.getvalue().strip()
-
- def __repr__(self):
- return "<%s instance at %0x>" %(self.__class__, id(self))
-
-
-class ReprExceptionInfo(TerminalRepr):
- def __init__(self, reprtraceback, reprcrash):
- self.reprtraceback = reprtraceback
- self.reprcrash = reprcrash
- self.sections = []
-
- def addsection(self, name, content, sep="-"):
- self.sections.append((name, content, sep))
-
- def toterminal(self, tw):
- self.reprtraceback.toterminal(tw)
- for name, content, sep in self.sections:
- tw.sep(sep, name)
- tw.line(content)
-
-class ReprTraceback(TerminalRepr):
- entrysep = "_ "
-
- def __init__(self, reprentries, extraline, style):
- self.reprentries = reprentries
- self.extraline = extraline
- self.style = style
-
- def toterminal(self, tw):
- # the entries might have different styles
- for i, entry in enumerate(self.reprentries):
- if entry.style == "long":
- tw.line("")
- entry.toterminal(tw)
- if i < len(self.reprentries) - 1:
- next_entry = self.reprentries[i+1]
- if entry.style == "long" or \
- entry.style == "short" and next_entry.style == "long":
- tw.sep(self.entrysep)
-
- if self.extraline:
- tw.line(self.extraline)
-
-class ReprTracebackNative(ReprTraceback):
- def __init__(self, tblines):
- self.style = "native"
- self.reprentries = [ReprEntryNative(tblines)]
- self.extraline = None
-
-class ReprEntryNative(TerminalRepr):
- style = "native"
-
- def __init__(self, tblines):
- self.lines = tblines
-
- def toterminal(self, tw):
- tw.write("".join(self.lines))
-
-class ReprEntry(TerminalRepr):
- localssep = "_ "
-
- def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style):
- self.lines = lines
- self.reprfuncargs = reprfuncargs
- self.reprlocals = reprlocals
- self.reprfileloc = filelocrepr
- self.style = style
-
- def toterminal(self, tw):
- if self.style == "short":
- self.reprfileloc.toterminal(tw)
- for line in self.lines:
- red = line.startswith("E ")
- tw.line(line, bold=True, red=red)
- #tw.line("")
- return
- if self.reprfuncargs:
- self.reprfuncargs.toterminal(tw)
- for line in self.lines:
- red = line.startswith("E ")
- tw.line(line, bold=True, red=red)
- if self.reprlocals:
- #tw.sep(self.localssep, "Locals")
- tw.line("")
- self.reprlocals.toterminal(tw)
- if self.reprfileloc:
- if self.lines:
- tw.line("")
- self.reprfileloc.toterminal(tw)
-
- def __str__(self):
- return "%s\n%s\n%s" % ("\n".join(self.lines),
- self.reprlocals,
- self.reprfileloc)
-
-class ReprFileLocation(TerminalRepr):
- def __init__(self, path, lineno, message):
- self.path = str(path)
- self.lineno = lineno
- self.message = message
-
- def toterminal(self, tw):
- # filename and lineno output for each entry,
- # using an output format that most editors unterstand
- msg = self.message
- i = msg.find("\n")
- if i != -1:
- msg = msg[:i]
- tw.line("%s:%s: %s" %(self.path, self.lineno, msg))
-
-class ReprLocals(TerminalRepr):
- def __init__(self, lines):
- self.lines = lines
-
- def toterminal(self, tw):
- for line in self.lines:
- tw.line(line)
-
-class ReprFuncArgs(TerminalRepr):
- def __init__(self, args):
- self.args = args
-
- def toterminal(self, tw):
- if self.args:
- linesofar = ""
- for name, value in self.args:
- ns = "%s = %s" %(name, value)
- if len(ns) + len(linesofar) + 2 > tw.fullwidth:
- if linesofar:
- tw.line(linesofar)
- linesofar = ns
- else:
- if linesofar:
- linesofar += ", " + ns
- else:
- linesofar = ns
- if linesofar:
- tw.line(linesofar)
- tw.line("")
-
-
-
-oldbuiltins = {}
-
-def patch_builtins(assertion=True, compile=True):
- """ put compile and AssertionError builtins to Python's builtins. """
- if assertion:
- from _pytest.assertion import reinterpret
- l = oldbuiltins.setdefault('AssertionError', [])
- l.append(py.builtin.builtins.AssertionError)
- py.builtin.builtins.AssertionError = reinterpret.AssertionError
- if compile:
- import _pytest._code
- l = oldbuiltins.setdefault('compile', [])
- l.append(py.builtin.builtins.compile)
- py.builtin.builtins.compile = _pytest._code.compile
-
-def unpatch_builtins(assertion=True, compile=True):
- """ remove compile and AssertionError builtins from Python builtins. """
- if assertion:
- py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop()
- if compile:
- py.builtin.builtins.compile = oldbuiltins['compile'].pop()
-
-def getrawcode(obj, trycall=True):
- """ return code object for given function. """
- try:
- return obj.__code__
- except AttributeError:
- obj = getattr(obj, 'im_func', obj)
- obj = getattr(obj, 'func_code', obj)
- obj = getattr(obj, 'f_code', obj)
- obj = getattr(obj, '__code__', obj)
- if trycall and not hasattr(obj, 'co_firstlineno'):
- if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj):
- x = getrawcode(obj.__call__, trycall=False)
- if hasattr(x, 'co_firstlineno'):
- return x
- return obj
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/source.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/source.py
deleted file mode 100644
index a1521f8a212..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_code/source.py
+++ /dev/null
@@ -1,421 +0,0 @@
-from __future__ import generators
-
-from bisect import bisect_right
-import sys
-import inspect, tokenize
-import py
-from types import ModuleType
-cpy_compile = compile
-
-try:
- import _ast
- from _ast import PyCF_ONLY_AST as _AST_FLAG
-except ImportError:
- _AST_FLAG = 0
- _ast = None
-
-
-class Source(object):
- """ a immutable object holding a source code fragment,
- possibly deindenting it.
- """
- _compilecounter = 0
- def __init__(self, *parts, **kwargs):
- self.lines = lines = []
- de = kwargs.get('deindent', True)
- rstrip = kwargs.get('rstrip', True)
- for part in parts:
- if not part:
- partlines = []
- if isinstance(part, Source):
- partlines = part.lines
- elif isinstance(part, (tuple, list)):
- partlines = [x.rstrip("\n") for x in part]
- elif isinstance(part, py.builtin._basestring):
- partlines = part.split('\n')
- if rstrip:
- while partlines:
- if partlines[-1].strip():
- break
- partlines.pop()
- else:
- partlines = getsource(part, deindent=de).lines
- if de:
- partlines = deindent(partlines)
- lines.extend(partlines)
-
- def __eq__(self, other):
- try:
- return self.lines == other.lines
- except AttributeError:
- if isinstance(other, str):
- return str(self) == other
- return False
-
- def __getitem__(self, key):
- if isinstance(key, int):
- return self.lines[key]
- else:
- if key.step not in (None, 1):
- raise IndexError("cannot slice a Source with a step")
- return self.__getslice__(key.start, key.stop)
-
- def __len__(self):
- return len(self.lines)
-
- def __getslice__(self, start, end):
- newsource = Source()
- newsource.lines = self.lines[start:end]
- return newsource
-
- def strip(self):
- """ return new source object with trailing
- and leading blank lines removed.
- """
- start, end = 0, len(self)
- while start < end and not self.lines[start].strip():
- start += 1
- while end > start and not self.lines[end-1].strip():
- end -= 1
- source = Source()
- source.lines[:] = self.lines[start:end]
- return source
-
- def putaround(self, before='', after='', indent=' ' * 4):
- """ return a copy of the source object with
- 'before' and 'after' wrapped around it.
- """
- before = Source(before)
- after = Source(after)
- newsource = Source()
- lines = [ (indent + line) for line in self.lines]
- newsource.lines = before.lines + lines + after.lines
- return newsource
-
- def indent(self, indent=' ' * 4):
- """ return a copy of the source object with
- all lines indented by the given indent-string.
- """
- newsource = Source()
- newsource.lines = [(indent+line) for line in self.lines]
- return newsource
-
- def getstatement(self, lineno, assertion=False):
- """ return Source statement which contains the
- given linenumber (counted from 0).
- """
- start, end = self.getstatementrange(lineno, assertion)
- return self[start:end]
-
- def getstatementrange(self, lineno, assertion=False):
- """ return (start, end) tuple which spans the minimal
- statement region which containing the given lineno.
- """
- if not (0 <= lineno < len(self)):
- raise IndexError("lineno out of range")
- ast, start, end = getstatementrange_ast(lineno, self)
- return start, end
-
- def deindent(self, offset=None):
- """ return a new source object deindented by offset.
- If offset is None then guess an indentation offset from
- the first non-blank line. Subsequent lines which have a
- lower indentation offset will be copied verbatim as
- they are assumed to be part of multilines.
- """
- # XXX maybe use the tokenizer to properly handle multiline
- # strings etc.pp?
- newsource = Source()
- newsource.lines[:] = deindent(self.lines, offset)
- return newsource
-
- def isparseable(self, deindent=True):
- """ return True if source is parseable, heuristically
- deindenting it by default.
- """
- try:
- import parser
- except ImportError:
- syntax_checker = lambda x: compile(x, 'asd', 'exec')
- else:
- syntax_checker = parser.suite
-
- if deindent:
- source = str(self.deindent())
- else:
- source = str(self)
- try:
- #compile(source+'\n', "x", "exec")
- syntax_checker(source+'\n')
- except KeyboardInterrupt:
- raise
- except Exception:
- return False
- else:
- return True
-
- def __str__(self):
- return "\n".join(self.lines)
-
- def compile(self, filename=None, mode='exec',
- flag=generators.compiler_flag,
- dont_inherit=0, _genframe=None):
- """ return compiled code object. if filename is None
- invent an artificial filename which displays
- the source/line position of the caller frame.
- """
- if not filename or py.path.local(filename).check(file=0):
- if _genframe is None:
- _genframe = sys._getframe(1) # the caller
- fn,lineno = _genframe.f_code.co_filename, _genframe.f_lineno
- base = "<%d-codegen " % self._compilecounter
- self.__class__._compilecounter += 1
- if not filename:
- filename = base + '%s:%d>' % (fn, lineno)
- else:
- filename = base + '%r %s:%d>' % (filename, fn, lineno)
- source = "\n".join(self.lines) + '\n'
- try:
- co = cpy_compile(source, filename, mode, flag)
- except SyntaxError:
- ex = sys.exc_info()[1]
- # re-represent syntax errors from parsing python strings
- msglines = self.lines[:ex.lineno]
- if ex.offset:
- msglines.append(" "*ex.offset + '^')
- msglines.append("(code was compiled probably from here: %s)" % filename)
- newex = SyntaxError('\n'.join(msglines))
- newex.offset = ex.offset
- newex.lineno = ex.lineno
- newex.text = ex.text
- raise newex
- else:
- if flag & _AST_FLAG:
- return co
- lines = [(x + "\n") for x in self.lines]
- if sys.version_info[0] >= 3:
- # XXX py3's inspect.getsourcefile() checks for a module
- # and a pep302 __loader__ ... we don't have a module
- # at code compile-time so we need to fake it here
- m = ModuleType("_pycodecompile_pseudo_module")
- py.std.inspect.modulesbyfile[filename] = None
- py.std.sys.modules[None] = m
- m.__loader__ = 1
- py.std.linecache.cache[filename] = (1, None, lines, filename)
- return co
-
-#
-# public API shortcut functions
-#
-
-def compile_(source, filename=None, mode='exec', flags=
- generators.compiler_flag, dont_inherit=0):
- """ compile the given source to a raw code object,
- and maintain an internal cache which allows later
- retrieval of the source code for the code object
- and any recursively created code objects.
- """
- if _ast is not None and isinstance(source, _ast.AST):
- # XXX should Source support having AST?
- return cpy_compile(source, filename, mode, flags, dont_inherit)
- _genframe = sys._getframe(1) # the caller
- s = Source(source)
- co = s.compile(filename, mode, flags, _genframe=_genframe)
- return co
-
-
-def getfslineno(obj):
- """ Return source location (path, lineno) for the given object.
- If the source cannot be determined return ("", -1)
- """
- import _pytest._code
- try:
- code = _pytest._code.Code(obj)
- except TypeError:
- try:
- fn = (py.std.inspect.getsourcefile(obj) or
- py.std.inspect.getfile(obj))
- except TypeError:
- return "", -1
-
- fspath = fn and py.path.local(fn) or None
- lineno = -1
- if fspath:
- try:
- _, lineno = findsource(obj)
- except IOError:
- pass
- else:
- fspath = code.path
- lineno = code.firstlineno
- assert isinstance(lineno, int)
- return fspath, lineno
-
-#
-# helper functions
-#
-
-def findsource(obj):
- try:
- sourcelines, lineno = py.std.inspect.findsource(obj)
- except py.builtin._sysex:
- raise
- except:
- return None, -1
- source = Source()
- source.lines = [line.rstrip() for line in sourcelines]
- return source, lineno
-
-def getsource(obj, **kwargs):
- import _pytest._code
- obj = _pytest._code.getrawcode(obj)
- try:
- strsrc = inspect.getsource(obj)
- except IndentationError:
- strsrc = "\"Buggy python version consider upgrading, cannot get source\""
- assert isinstance(strsrc, str)
- return Source(strsrc, **kwargs)
-
-def deindent(lines, offset=None):
- if offset is None:
- for line in lines:
- line = line.expandtabs()
- s = line.lstrip()
- if s:
- offset = len(line)-len(s)
- break
- else:
- offset = 0
- if offset == 0:
- return list(lines)
- newlines = []
- def readline_generator(lines):
- for line in lines:
- yield line + '\n'
- while True:
- yield ''
-
- it = readline_generator(lines)
-
- try:
- for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(lambda: next(it)):
- if sline > len(lines):
- break # End of input reached
- if sline > len(newlines):
- line = lines[sline - 1].expandtabs()
- if line.lstrip() and line[:offset].isspace():
- line = line[offset:] # Deindent
- newlines.append(line)
-
- for i in range(sline, eline):
- # Don't deindent continuing lines of
- # multiline tokens (i.e. multiline strings)
- newlines.append(lines[i])
- except (IndentationError, tokenize.TokenError):
- pass
- # Add any lines we didn't see. E.g. if an exception was raised.
- newlines.extend(lines[len(newlines):])
- return newlines
-
-
-def get_statement_startend2(lineno, node):
- import ast
- # flatten all statements and except handlers into one lineno-list
- # AST's line numbers start indexing at 1
- l = []
- for x in ast.walk(node):
- if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler):
- l.append(x.lineno - 1)
- for name in "finalbody", "orelse":
- val = getattr(x, name, None)
- if val:
- # treat the finally/orelse part as its own statement
- l.append(val[0].lineno - 1 - 1)
- l.sort()
- insert_index = bisect_right(l, lineno)
- start = l[insert_index - 1]
- if insert_index >= len(l):
- end = None
- else:
- end = l[insert_index]
- return start, end
-
-
-def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
- if astnode is None:
- content = str(source)
- if sys.version_info < (2,7):
- content += "\n"
- try:
- astnode = compile(content, "source", "exec", 1024) # 1024 for AST
- except ValueError:
- start, end = getstatementrange_old(lineno, source, assertion)
- return None, start, end
- start, end = get_statement_startend2(lineno, astnode)
- # we need to correct the end:
- # - ast-parsing strips comments
- # - there might be empty lines
- # - we might have lesser indented code blocks at the end
- if end is None:
- end = len(source.lines)
-
- if end > start + 1:
- # make sure we don't span differently indented code blocks
- # by using the BlockFinder helper used which inspect.getsource() uses itself
- block_finder = inspect.BlockFinder()
- # if we start with an indented line, put blockfinder to "started" mode
- block_finder.started = source.lines[start][0].isspace()
- it = ((x + "\n") for x in source.lines[start:end])
- try:
- for tok in tokenize.generate_tokens(lambda: next(it)):
- block_finder.tokeneater(*tok)
- except (inspect.EndOfBlock, IndentationError):
- end = block_finder.last + start
- except Exception:
- pass
-
- # the end might still point to a comment or empty line, correct it
- while end:
- line = source.lines[end - 1].lstrip()
- if line.startswith("#") or not line:
- end -= 1
- else:
- break
- return astnode, start, end
-
-
-def getstatementrange_old(lineno, source, assertion=False):
- """ return (start, end) tuple which spans the minimal
- statement region which containing the given lineno.
- raise an IndexError if no such statementrange can be found.
- """
- # XXX this logic is only used on python2.4 and below
- # 1. find the start of the statement
- from codeop import compile_command
- for start in range(lineno, -1, -1):
- if assertion:
- line = source.lines[start]
- # the following lines are not fully tested, change with care
- if 'super' in line and 'self' in line and '__init__' in line:
- raise IndexError("likely a subclass")
- if "assert" not in line and "raise" not in line:
- continue
- trylines = source.lines[start:lineno+1]
- # quick hack to prepare parsing an indented line with
- # compile_command() (which errors on "return" outside defs)
- trylines.insert(0, 'def xxx():')
- trysource = '\n '.join(trylines)
- # ^ space here
- try:
- compile_command(trysource)
- except (SyntaxError, OverflowError, ValueError):
- continue
-
- # 2. find the end of the statement
- for end in range(lineno+1, len(source)+1):
- trysource = source[start:end]
- if trysource.isparseable():
- return start, end
- raise SyntaxError("no valid source range around line %d " % (lineno,))
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_pluggy.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/_pluggy.py
deleted file mode 100644
index 87d32cf8dd1..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/_pluggy.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""
-imports symbols from vendored "pluggy" if available, otherwise
-falls back to importing "pluggy" from the default namespace.
-"""
-
-try:
- from _pytest.vendored_packages.pluggy import * # noqa
- from _pytest.vendored_packages.pluggy import __version__ # noqa
-except ImportError:
- from pluggy import * # noqa
- from pluggy import __version__ # noqa
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/__init__.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/__init__.py
deleted file mode 100644
index 6921deb2a60..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/__init__.py
+++ /dev/null
@@ -1,176 +0,0 @@
-"""
-support for presenting detailed information in failing assertions.
-"""
-import py
-import os
-import sys
-from _pytest.monkeypatch import monkeypatch
-from _pytest.assertion import util
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("debugconfig")
- group.addoption('--assert',
- action="store",
- dest="assertmode",
- choices=("rewrite", "reinterp", "plain",),
- default="rewrite",
- metavar="MODE",
- help="""control assertion debugging tools. 'plain'
- performs no assertion debugging. 'reinterp'
- reinterprets assert statements after they failed
- to provide assertion expression information.
- 'rewrite' (the default) rewrites assert
- statements in test modules on import to
- provide assert expression information. """)
- group.addoption('--no-assert',
- action="store_true",
- default=False,
- dest="noassert",
- help="DEPRECATED equivalent to --assert=plain")
- group.addoption('--nomagic', '--no-magic',
- action="store_true",
- default=False,
- help="DEPRECATED equivalent to --assert=plain")
-
-
-class AssertionState:
- """State for the assertion plugin."""
-
- def __init__(self, config, mode):
- self.mode = mode
- self.trace = config.trace.root.get("assertion")
-
-
-def pytest_configure(config):
- mode = config.getvalue("assertmode")
- if config.getvalue("noassert") or config.getvalue("nomagic"):
- mode = "plain"
- if mode == "rewrite":
- try:
- import ast # noqa
- except ImportError:
- mode = "reinterp"
- else:
- # Both Jython and CPython 2.6.0 have AST bugs that make the
- # assertion rewriting hook malfunction.
- if (sys.platform.startswith('java') or
- sys.version_info[:3] == (2, 6, 0)):
- mode = "reinterp"
- if mode != "plain":
- _load_modules(mode)
- m = monkeypatch()
- config._cleanup.append(m.undo)
- m.setattr(py.builtin.builtins, 'AssertionError',
- reinterpret.AssertionError) # noqa
- hook = None
- if mode == "rewrite":
- hook = rewrite.AssertionRewritingHook() # noqa
- sys.meta_path.insert(0, hook)
- warn_about_missing_assertion(mode)
- config._assertstate = AssertionState(config, mode)
- config._assertstate.hook = hook
- config._assertstate.trace("configured with mode set to %r" % (mode,))
- def undo():
- hook = config._assertstate.hook
- if hook is not None and hook in sys.meta_path:
- sys.meta_path.remove(hook)
- config.add_cleanup(undo)
-
-
-def pytest_collection(session):
- # this hook is only called when test modules are collected
- # so for example not in the master process of pytest-xdist
- # (which does not collect test modules)
- hook = session.config._assertstate.hook
- if hook is not None:
- hook.set_session(session)
-
-
-def _running_on_ci():
- """Check if we're currently running on a CI system."""
- env_vars = ['CI', 'BUILD_NUMBER']
- return any(var in os.environ for var in env_vars)
-
-
-def pytest_runtest_setup(item):
- """Setup the pytest_assertrepr_compare hook
-
- The newinterpret and rewrite modules will use util._reprcompare if
- it exists to use custom reporting via the
- pytest_assertrepr_compare hook. This sets up this custom
- comparison for the test.
- """
- def callbinrepr(op, left, right):
- """Call the pytest_assertrepr_compare hook and prepare the result
-
- This uses the first result from the hook and then ensures the
- following:
- * Overly verbose explanations are dropped unless -vv was used or
- running on a CI.
- * Embedded newlines are escaped to help util.format_explanation()
- later.
- * If the rewrite mode is used embedded %-characters are replaced
- to protect later % formatting.
-
- The result can be formatted by util.format_explanation() for
- pretty printing.
- """
- hook_result = item.ihook.pytest_assertrepr_compare(
- config=item.config, op=op, left=left, right=right)
- for new_expl in hook_result:
- if new_expl:
- if (sum(len(p) for p in new_expl[1:]) > 80*8 and
- item.config.option.verbose < 2 and
- not _running_on_ci()):
- show_max = 10
- truncated_lines = len(new_expl) - show_max
- new_expl[show_max:] = [py.builtin._totext(
- 'Detailed information truncated (%d more lines)'
- ', use "-vv" to show' % truncated_lines)]
- new_expl = [line.replace("\n", "\\n") for line in new_expl]
- res = py.builtin._totext("\n~").join(new_expl)
- if item.config.getvalue("assertmode") == "rewrite":
- res = res.replace("%", "%%")
- return res
- util._reprcompare = callbinrepr
-
-
-def pytest_runtest_teardown(item):
- util._reprcompare = None
-
-
-def pytest_sessionfinish(session):
- hook = session.config._assertstate.hook
- if hook is not None:
- hook.session = None
-
-
-def _load_modules(mode):
- """Lazily import assertion related code."""
- global rewrite, reinterpret
- from _pytest.assertion import reinterpret # noqa
- if mode == "rewrite":
- from _pytest.assertion import rewrite # noqa
-
-
-def warn_about_missing_assertion(mode):
- try:
- assert False
- except AssertionError:
- pass
- else:
- if mode == "rewrite":
- specifically = ("assertions which are not in test modules "
- "will be ignored")
- else:
- specifically = "failing tests may report as passing"
-
- sys.stderr.write("WARNING: " + specifically +
- " because assert statements are not executed "
- "by the underlying Python interpreter "
- "(are you using python -O?)\n")
-
-
-# Expose this plugin's implementation for the pytest_assertrepr_compare hook
-pytest_assertrepr_compare = util.assertrepr_compare
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/reinterpret.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/reinterpret.py
deleted file mode 100644
index f4262c3aced..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/reinterpret.py
+++ /dev/null
@@ -1,407 +0,0 @@
-"""
-Find intermediate evalutation results in assert statements through builtin AST.
-"""
-import ast
-import sys
-
-import _pytest._code
-import py
-from _pytest.assertion import util
-u = py.builtin._totext
-
-
-class AssertionError(util.BuiltinAssertionError):
- def __init__(self, *args):
- util.BuiltinAssertionError.__init__(self, *args)
- if args:
- # on Python2.6 we get len(args)==2 for: assert 0, (x,y)
- # on Python2.7 and above we always get len(args) == 1
- # with args[0] being the (x,y) tuple.
- if len(args) > 1:
- toprint = args
- else:
- toprint = args[0]
- try:
- self.msg = u(toprint)
- except Exception:
- self.msg = u(
- "<[broken __repr__] %s at %0xd>"
- % (toprint.__class__, id(toprint)))
- else:
- f = _pytest._code.Frame(sys._getframe(1))
- try:
- source = f.code.fullsource
- if source is not None:
- try:
- source = source.getstatement(f.lineno, assertion=True)
- except IndexError:
- source = None
- else:
- source = str(source.deindent()).strip()
- except py.error.ENOENT:
- source = None
- # this can also occur during reinterpretation, when the
- # co_filename is set to "<run>".
- if source:
- self.msg = reinterpret(source, f, should_fail=True)
- else:
- self.msg = "<could not determine information>"
- if not self.args:
- self.args = (self.msg,)
-
-if sys.version_info > (3, 0):
- AssertionError.__module__ = "builtins"
-
-if sys.platform.startswith("java"):
- # See http://bugs.jython.org/issue1497
- _exprs = ("BoolOp", "BinOp", "UnaryOp", "Lambda", "IfExp", "Dict",
- "ListComp", "GeneratorExp", "Yield", "Compare", "Call",
- "Repr", "Num", "Str", "Attribute", "Subscript", "Name",
- "List", "Tuple")
- _stmts = ("FunctionDef", "ClassDef", "Return", "Delete", "Assign",
- "AugAssign", "Print", "For", "While", "If", "With", "Raise",
- "TryExcept", "TryFinally", "Assert", "Import", "ImportFrom",
- "Exec", "Global", "Expr", "Pass", "Break", "Continue")
- _expr_nodes = set(getattr(ast, name) for name in _exprs)
- _stmt_nodes = set(getattr(ast, name) for name in _stmts)
- def _is_ast_expr(node):
- return node.__class__ in _expr_nodes
- def _is_ast_stmt(node):
- return node.__class__ in _stmt_nodes
-else:
- def _is_ast_expr(node):
- return isinstance(node, ast.expr)
- def _is_ast_stmt(node):
- return isinstance(node, ast.stmt)
-
-try:
- _Starred = ast.Starred
-except AttributeError:
- # Python 2. Define a dummy class so isinstance() will always be False.
- class _Starred(object): pass
-
-
-class Failure(Exception):
- """Error found while interpreting AST."""
-
- def __init__(self, explanation=""):
- self.cause = sys.exc_info()
- self.explanation = explanation
-
-
-def reinterpret(source, frame, should_fail=False):
- mod = ast.parse(source)
- visitor = DebugInterpreter(frame)
- try:
- visitor.visit(mod)
- except Failure:
- failure = sys.exc_info()[1]
- return getfailure(failure)
- if should_fail:
- return ("(assertion failed, but when it was re-run for "
- "printing intermediate values, it did not fail. Suggestions: "
- "compute assert expression before the assert or use --assert=plain)")
-
-def run(offending_line, frame=None):
- if frame is None:
- frame = _pytest._code.Frame(sys._getframe(1))
- return reinterpret(offending_line, frame)
-
-def getfailure(e):
- explanation = util.format_explanation(e.explanation)
- value = e.cause[1]
- if str(value):
- lines = explanation.split('\n')
- lines[0] += " << %s" % (value,)
- explanation = '\n'.join(lines)
- text = "%s: %s" % (e.cause[0].__name__, explanation)
- if text.startswith('AssertionError: assert '):
- text = text[16:]
- return text
-
-operator_map = {
- ast.BitOr : "|",
- ast.BitXor : "^",
- ast.BitAnd : "&",
- ast.LShift : "<<",
- ast.RShift : ">>",
- ast.Add : "+",
- ast.Sub : "-",
- ast.Mult : "*",
- ast.Div : "/",
- ast.FloorDiv : "//",
- ast.Mod : "%",
- ast.Eq : "==",
- ast.NotEq : "!=",
- ast.Lt : "<",
- ast.LtE : "<=",
- ast.Gt : ">",
- ast.GtE : ">=",
- ast.Pow : "**",
- ast.Is : "is",
- ast.IsNot : "is not",
- ast.In : "in",
- ast.NotIn : "not in"
-}
-
-unary_map = {
- ast.Not : "not %s",
- ast.Invert : "~%s",
- ast.USub : "-%s",
- ast.UAdd : "+%s"
-}
-
-
-class DebugInterpreter(ast.NodeVisitor):
- """Interpret AST nodes to gleam useful debugging information. """
-
- def __init__(self, frame):
- self.frame = frame
-
- def generic_visit(self, node):
- # Fallback when we don't have a special implementation.
- if _is_ast_expr(node):
- mod = ast.Expression(node)
- co = self._compile(mod)
- try:
- result = self.frame.eval(co)
- except Exception:
- raise Failure()
- explanation = self.frame.repr(result)
- return explanation, result
- elif _is_ast_stmt(node):
- mod = ast.Module([node])
- co = self._compile(mod, "exec")
- try:
- self.frame.exec_(co)
- except Exception:
- raise Failure()
- return None, None
- else:
- raise AssertionError("can't handle %s" %(node,))
-
- def _compile(self, source, mode="eval"):
- return compile(source, "<assertion interpretation>", mode)
-
- def visit_Expr(self, expr):
- return self.visit(expr.value)
-
- def visit_Module(self, mod):
- for stmt in mod.body:
- self.visit(stmt)
-
- def visit_Name(self, name):
- explanation, result = self.generic_visit(name)
- # See if the name is local.
- source = "%r in locals() is not globals()" % (name.id,)
- co = self._compile(source)
- try:
- local = self.frame.eval(co)
- except Exception:
- # have to assume it isn't
- local = None
- if local is None or not self.frame.is_true(local):
- return name.id, result
- return explanation, result
-
- def visit_Compare(self, comp):
- left = comp.left
- left_explanation, left_result = self.visit(left)
- for op, next_op in zip(comp.ops, comp.comparators):
- next_explanation, next_result = self.visit(next_op)
- op_symbol = operator_map[op.__class__]
- explanation = "%s %s %s" % (left_explanation, op_symbol,
- next_explanation)
- source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,)
- co = self._compile(source)
- try:
- result = self.frame.eval(co, __exprinfo_left=left_result,
- __exprinfo_right=next_result)
- except Exception:
- raise Failure(explanation)
- try:
- if not self.frame.is_true(result):
- break
- except KeyboardInterrupt:
- raise
- except:
- break
- left_explanation, left_result = next_explanation, next_result
-
- if util._reprcompare is not None:
- res = util._reprcompare(op_symbol, left_result, next_result)
- if res:
- explanation = res
- return explanation, result
-
- def visit_BoolOp(self, boolop):
- is_or = isinstance(boolop.op, ast.Or)
- explanations = []
- for operand in boolop.values:
- explanation, result = self.visit(operand)
- explanations.append(explanation)
- if result == is_or:
- break
- name = is_or and " or " or " and "
- explanation = "(" + name.join(explanations) + ")"
- return explanation, result
-
- def visit_UnaryOp(self, unary):
- pattern = unary_map[unary.op.__class__]
- operand_explanation, operand_result = self.visit(unary.operand)
- explanation = pattern % (operand_explanation,)
- co = self._compile(pattern % ("__exprinfo_expr",))
- try:
- result = self.frame.eval(co, __exprinfo_expr=operand_result)
- except Exception:
- raise Failure(explanation)
- return explanation, result
-
- def visit_BinOp(self, binop):
- left_explanation, left_result = self.visit(binop.left)
- right_explanation, right_result = self.visit(binop.right)
- symbol = operator_map[binop.op.__class__]
- explanation = "(%s %s %s)" % (left_explanation, symbol,
- right_explanation)
- source = "__exprinfo_left %s __exprinfo_right" % (symbol,)
- co = self._compile(source)
- try:
- result = self.frame.eval(co, __exprinfo_left=left_result,
- __exprinfo_right=right_result)
- except Exception:
- raise Failure(explanation)
- return explanation, result
-
- def visit_Call(self, call):
- func_explanation, func = self.visit(call.func)
- arg_explanations = []
- ns = {"__exprinfo_func" : func}
- arguments = []
- for arg in call.args:
- arg_explanation, arg_result = self.visit(arg)
- if isinstance(arg, _Starred):
- arg_name = "__exprinfo_star"
- ns[arg_name] = arg_result
- arguments.append("*%s" % (arg_name,))
- arg_explanations.append("*%s" % (arg_explanation,))
- else:
- arg_name = "__exprinfo_%s" % (len(ns),)
- ns[arg_name] = arg_result
- arguments.append(arg_name)
- arg_explanations.append(arg_explanation)
- for keyword in call.keywords:
- arg_explanation, arg_result = self.visit(keyword.value)
- if keyword.arg:
- arg_name = "__exprinfo_%s" % (len(ns),)
- keyword_source = "%s=%%s" % (keyword.arg)
- arguments.append(keyword_source % (arg_name,))
- arg_explanations.append(keyword_source % (arg_explanation,))
- else:
- arg_name = "__exprinfo_kwds"
- arguments.append("**%s" % (arg_name,))
- arg_explanations.append("**%s" % (arg_explanation,))
-
- ns[arg_name] = arg_result
-
- if getattr(call, 'starargs', None):
- arg_explanation, arg_result = self.visit(call.starargs)
- arg_name = "__exprinfo_star"
- ns[arg_name] = arg_result
- arguments.append("*%s" % (arg_name,))
- arg_explanations.append("*%s" % (arg_explanation,))
-
- if getattr(call, 'kwargs', None):
- arg_explanation, arg_result = self.visit(call.kwargs)
- arg_name = "__exprinfo_kwds"
- ns[arg_name] = arg_result
- arguments.append("**%s" % (arg_name,))
- arg_explanations.append("**%s" % (arg_explanation,))
- args_explained = ", ".join(arg_explanations)
- explanation = "%s(%s)" % (func_explanation, args_explained)
- args = ", ".join(arguments)
- source = "__exprinfo_func(%s)" % (args,)
- co = self._compile(source)
- try:
- result = self.frame.eval(co, **ns)
- except Exception:
- raise Failure(explanation)
- pattern = "%s\n{%s = %s\n}"
- rep = self.frame.repr(result)
- explanation = pattern % (rep, rep, explanation)
- return explanation, result
-
- def _is_builtin_name(self, name):
- pattern = "%r not in globals() and %r not in locals()"
- source = pattern % (name.id, name.id)
- co = self._compile(source)
- try:
- return self.frame.eval(co)
- except Exception:
- return False
-
- def visit_Attribute(self, attr):
- if not isinstance(attr.ctx, ast.Load):
- return self.generic_visit(attr)
- source_explanation, source_result = self.visit(attr.value)
- explanation = "%s.%s" % (source_explanation, attr.attr)
- source = "__exprinfo_expr.%s" % (attr.attr,)
- co = self._compile(source)
- try:
- try:
- result = self.frame.eval(co, __exprinfo_expr=source_result)
- except AttributeError:
- # Maybe the attribute name needs to be mangled?
- if not attr.attr.startswith("__") or attr.attr.endswith("__"):
- raise
- source = "getattr(__exprinfo_expr.__class__, '__name__', '')"
- co = self._compile(source)
- class_name = self.frame.eval(co, __exprinfo_expr=source_result)
- mangled_attr = "_" + class_name + attr.attr
- source = "__exprinfo_expr.%s" % (mangled_attr,)
- co = self._compile(source)
- result = self.frame.eval(co, __exprinfo_expr=source_result)
- except Exception:
- raise Failure(explanation)
- explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result),
- self.frame.repr(result),
- source_explanation, attr.attr)
- # Check if the attr is from an instance.
- source = "%r in getattr(__exprinfo_expr, '__dict__', {})"
- source = source % (attr.attr,)
- co = self._compile(source)
- try:
- from_instance = self.frame.eval(co, __exprinfo_expr=source_result)
- except Exception:
- from_instance = None
- if from_instance is None or self.frame.is_true(from_instance):
- rep = self.frame.repr(result)
- pattern = "%s\n{%s = %s\n}"
- explanation = pattern % (rep, rep, explanation)
- return explanation, result
-
- def visit_Assert(self, assrt):
- test_explanation, test_result = self.visit(assrt.test)
- explanation = "assert %s" % (test_explanation,)
- if not self.frame.is_true(test_result):
- try:
- raise util.BuiltinAssertionError
- except Exception:
- raise Failure(explanation)
- return explanation, test_result
-
- def visit_Assign(self, assign):
- value_explanation, value_result = self.visit(assign.value)
- explanation = "... = %s" % (value_explanation,)
- name = ast.Name("__exprinfo_expr", ast.Load(),
- lineno=assign.value.lineno,
- col_offset=assign.value.col_offset)
- new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno,
- col_offset=assign.col_offset)
- mod = ast.Module([new_assign])
- co = self._compile(mod, "exec")
- try:
- self.frame.exec_(co, __exprinfo_expr=value_result)
- except Exception:
- raise Failure(explanation)
- return explanation, value_result
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/rewrite.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/rewrite.py
deleted file mode 100644
index 14b8e49db2b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/rewrite.py
+++ /dev/null
@@ -1,885 +0,0 @@
-"""Rewrite assertion AST to produce nice error messages"""
-
-import ast
-import errno
-import itertools
-import imp
-import marshal
-import os
-import re
-import struct
-import sys
-import types
-
-import py
-from _pytest.assertion import util
-
-
-# pytest caches rewritten pycs in __pycache__.
-if hasattr(imp, "get_tag"):
- PYTEST_TAG = imp.get_tag() + "-PYTEST"
-else:
- if hasattr(sys, "pypy_version_info"):
- impl = "pypy"
- elif sys.platform == "java":
- impl = "jython"
- else:
- impl = "cpython"
- ver = sys.version_info
- PYTEST_TAG = "%s-%s%s-PYTEST" % (impl, ver[0], ver[1])
- del ver, impl
-
-PYC_EXT = ".py" + (__debug__ and "c" or "o")
-PYC_TAIL = "." + PYTEST_TAG + PYC_EXT
-
-REWRITE_NEWLINES = sys.version_info[:2] != (2, 7) and sys.version_info < (3, 2)
-ASCII_IS_DEFAULT_ENCODING = sys.version_info[0] < 3
-
-if sys.version_info >= (3,5):
- ast_Call = ast.Call
-else:
- ast_Call = lambda a,b,c: ast.Call(a, b, c, None, None)
-
-
-class AssertionRewritingHook(object):
- """PEP302 Import hook which rewrites asserts."""
-
- def __init__(self):
- self.session = None
- self.modules = {}
- self._register_with_pkg_resources()
-
- def set_session(self, session):
- self.fnpats = session.config.getini("python_files")
- self.session = session
-
- def find_module(self, name, path=None):
- if self.session is None:
- return None
- sess = self.session
- state = sess.config._assertstate
- state.trace("find_module called for: %s" % name)
- names = name.rsplit(".", 1)
- lastname = names[-1]
- pth = None
- if path is not None:
- # Starting with Python 3.3, path is a _NamespacePath(), which
- # causes problems if not converted to list.
- path = list(path)
- if len(path) == 1:
- pth = path[0]
- if pth is None:
- try:
- fd, fn, desc = imp.find_module(lastname, path)
- except ImportError:
- return None
- if fd is not None:
- fd.close()
- tp = desc[2]
- if tp == imp.PY_COMPILED:
- if hasattr(imp, "source_from_cache"):
- fn = imp.source_from_cache(fn)
- else:
- fn = fn[:-1]
- elif tp != imp.PY_SOURCE:
- # Don't know what this is.
- return None
- else:
- fn = os.path.join(pth, name.rpartition(".")[2] + ".py")
- fn_pypath = py.path.local(fn)
- # Is this a test file?
- if not sess.isinitpath(fn):
- # We have to be very careful here because imports in this code can
- # trigger a cycle.
- self.session = None
- try:
- for pat in self.fnpats:
- if fn_pypath.fnmatch(pat):
- state.trace("matched test file %r" % (fn,))
- break
- else:
- return None
- finally:
- self.session = sess
- else:
- state.trace("matched test file (was specified on cmdline): %r" %
- (fn,))
- # The requested module looks like a test file, so rewrite it. This is
- # the most magical part of the process: load the source, rewrite the
- # asserts, and load the rewritten source. We also cache the rewritten
- # module code in a special pyc. We must be aware of the possibility of
- # concurrent pytest processes rewriting and loading pycs. To avoid
- # tricky race conditions, we maintain the following invariant: The
- # cached pyc is always a complete, valid pyc. Operations on it must be
- # atomic. POSIX's atomic rename comes in handy.
- write = not sys.dont_write_bytecode
- cache_dir = os.path.join(fn_pypath.dirname, "__pycache__")
- if write:
- try:
- os.mkdir(cache_dir)
- except OSError:
- e = sys.exc_info()[1].errno
- if e == errno.EEXIST:
- # Either the __pycache__ directory already exists (the
- # common case) or it's blocked by a non-dir node. In the
- # latter case, we'll ignore it in _write_pyc.
- pass
- elif e in [errno.ENOENT, errno.ENOTDIR]:
- # One of the path components was not a directory, likely
- # because we're in a zip file.
- write = False
- elif e in [errno.EACCES, errno.EROFS, errno.EPERM]:
- state.trace("read only directory: %r" % fn_pypath.dirname)
- write = False
- else:
- raise
- cache_name = fn_pypath.basename[:-3] + PYC_TAIL
- pyc = os.path.join(cache_dir, cache_name)
- # Notice that even if we're in a read-only directory, I'm going
- # to check for a cached pyc. This may not be optimal...
- co = _read_pyc(fn_pypath, pyc, state.trace)
- if co is None:
- state.trace("rewriting %r" % (fn,))
- source_stat, co = _rewrite_test(state, fn_pypath)
- if co is None:
- # Probably a SyntaxError in the test.
- return None
- if write:
- _make_rewritten_pyc(state, source_stat, pyc, co)
- else:
- state.trace("found cached rewritten pyc for %r" % (fn,))
- self.modules[name] = co, pyc
- return self
-
- def load_module(self, name):
- # If there is an existing module object named 'fullname' in
- # sys.modules, the loader must use that existing module. (Otherwise,
- # the reload() builtin will not work correctly.)
- if name in sys.modules:
- return sys.modules[name]
-
- co, pyc = self.modules.pop(name)
- # I wish I could just call imp.load_compiled here, but __file__ has to
- # be set properly. In Python 3.2+, this all would be handled correctly
- # by load_compiled.
- mod = sys.modules[name] = imp.new_module(name)
- try:
- mod.__file__ = co.co_filename
- # Normally, this attribute is 3.2+.
- mod.__cached__ = pyc
- mod.__loader__ = self
- py.builtin.exec_(co, mod.__dict__)
- except:
- del sys.modules[name]
- raise
- return sys.modules[name]
-
-
-
- def is_package(self, name):
- try:
- fd, fn, desc = imp.find_module(name)
- except ImportError:
- return False
- if fd is not None:
- fd.close()
- tp = desc[2]
- return tp == imp.PKG_DIRECTORY
-
- @classmethod
- def _register_with_pkg_resources(cls):
- """
- Ensure package resources can be loaded from this loader. May be called
- multiple times, as the operation is idempotent.
- """
- try:
- import pkg_resources
- # access an attribute in case a deferred importer is present
- pkg_resources.__name__
- except ImportError:
- return
-
- # Since pytest tests are always located in the file system, the
- # DefaultProvider is appropriate.
- pkg_resources.register_loader_type(cls, pkg_resources.DefaultProvider)
-
- def get_data(self, pathname):
- """Optional PEP302 get_data API.
- """
- with open(pathname, 'rb') as f:
- return f.read()
-
-
-def _write_pyc(state, co, source_stat, pyc):
- # Technically, we don't have to have the same pyc format as
- # (C)Python, since these "pycs" should never be seen by builtin
- # import. However, there's little reason deviate, and I hope
- # sometime to be able to use imp.load_compiled to load them. (See
- # the comment in load_module above.)
- try:
- fp = open(pyc, "wb")
- except IOError:
- err = sys.exc_info()[1].errno
- state.trace("error writing pyc file at %s: errno=%s" %(pyc, err))
- # we ignore any failure to write the cache file
- # there are many reasons, permission-denied, __pycache__ being a
- # file etc.
- return False
- try:
- fp.write(imp.get_magic())
- mtime = int(source_stat.mtime)
- size = source_stat.size & 0xFFFFFFFF
- fp.write(struct.pack("<ll", mtime, size))
- marshal.dump(co, fp)
- finally:
- fp.close()
- return True
-
-RN = "\r\n".encode("utf-8")
-N = "\n".encode("utf-8")
-
-cookie_re = re.compile(r"^[ \t\f]*#.*coding[:=][ \t]*[-\w.]+")
-BOM_UTF8 = '\xef\xbb\xbf'
-
-def _rewrite_test(state, fn):
- """Try to read and rewrite *fn* and return the code object."""
- try:
- stat = fn.stat()
- source = fn.read("rb")
- except EnvironmentError:
- return None, None
- if ASCII_IS_DEFAULT_ENCODING:
- # ASCII is the default encoding in Python 2. Without a coding
- # declaration, Python 2 will complain about any bytes in the file
- # outside the ASCII range. Sadly, this behavior does not extend to
- # compile() or ast.parse(), which prefer to interpret the bytes as
- # latin-1. (At least they properly handle explicit coding cookies.) To
- # preserve this error behavior, we could force ast.parse() to use ASCII
- # as the encoding by inserting a coding cookie. Unfortunately, that
- # messes up line numbers. Thus, we have to check ourselves if anything
- # is outside the ASCII range in the case no encoding is explicitly
- # declared. For more context, see issue #269. Yay for Python 3 which
- # gets this right.
- end1 = source.find("\n")
- end2 = source.find("\n", end1 + 1)
- if (not source.startswith(BOM_UTF8) and
- cookie_re.match(source[0:end1]) is None and
- cookie_re.match(source[end1 + 1:end2]) is None):
- if hasattr(state, "_indecode"):
- # encodings imported us again, so don't rewrite.
- return None, None
- state._indecode = True
- try:
- try:
- source.decode("ascii")
- except UnicodeDecodeError:
- # Let it fail in real import.
- return None, None
- finally:
- del state._indecode
- # On Python versions which are not 2.7 and less than or equal to 3.1, the
- # parser expects *nix newlines.
- if REWRITE_NEWLINES:
- source = source.replace(RN, N) + N
- try:
- tree = ast.parse(source)
- except SyntaxError:
- # Let this pop up again in the real import.
- state.trace("failed to parse: %r" % (fn,))
- return None, None
- rewrite_asserts(tree)
- try:
- co = compile(tree, fn.strpath, "exec")
- except SyntaxError:
- # It's possible that this error is from some bug in the
- # assertion rewriting, but I don't know of a fast way to tell.
- state.trace("failed to compile: %r" % (fn,))
- return None, None
- return stat, co
-
-def _make_rewritten_pyc(state, source_stat, pyc, co):
- """Try to dump rewritten code to *pyc*."""
- if sys.platform.startswith("win"):
- # Windows grants exclusive access to open files and doesn't have atomic
- # rename, so just write into the final file.
- _write_pyc(state, co, source_stat, pyc)
- else:
- # When not on windows, assume rename is atomic. Dump the code object
- # into a file specific to this process and atomically replace it.
- proc_pyc = pyc + "." + str(os.getpid())
- if _write_pyc(state, co, source_stat, proc_pyc):
- os.rename(proc_pyc, pyc)
-
-def _read_pyc(source, pyc, trace=lambda x: None):
- """Possibly read a pytest pyc containing rewritten code.
-
- Return rewritten code if successful or None if not.
- """
- try:
- fp = open(pyc, "rb")
- except IOError:
- return None
- with fp:
- try:
- mtime = int(source.mtime())
- size = source.size()
- data = fp.read(12)
- except EnvironmentError as e:
- trace('_read_pyc(%s): EnvironmentError %s' % (source, e))
- return None
- # Check for invalid or out of date pyc file.
- if (len(data) != 12 or data[:4] != imp.get_magic() or
- struct.unpack("<ll", data[4:]) != (mtime, size)):
- trace('_read_pyc(%s): invalid or out of date pyc' % source)
- return None
- try:
- co = marshal.load(fp)
- except Exception as e:
- trace('_read_pyc(%s): marshal.load error %s' % (source, e))
- return None
- if not isinstance(co, types.CodeType):
- trace('_read_pyc(%s): not a code object' % source)
- return None
- return co
-
-
-def rewrite_asserts(mod):
- """Rewrite the assert statements in mod."""
- AssertionRewriter().run(mod)
-
-
-def _saferepr(obj):
- """Get a safe repr of an object for assertion error messages.
-
- The assertion formatting (util.format_explanation()) requires
- newlines to be escaped since they are a special character for it.
- Normally assertion.util.format_explanation() does this but for a
- custom repr it is possible to contain one of the special escape
- sequences, especially '\n{' and '\n}' are likely to be present in
- JSON reprs.
-
- """
- repr = py.io.saferepr(obj)
- if py.builtin._istext(repr):
- t = py.builtin.text
- else:
- t = py.builtin.bytes
- return repr.replace(t("\n"), t("\\n"))
-
-
-from _pytest.assertion.util import format_explanation as _format_explanation # noqa
-
-def _format_assertmsg(obj):
- """Format the custom assertion message given.
-
- For strings this simply replaces newlines with '\n~' so that
- util.format_explanation() will preserve them instead of escaping
- newlines. For other objects py.io.saferepr() is used first.
-
- """
- # reprlib appears to have a bug which means that if a string
- # contains a newline it gets escaped, however if an object has a
- # .__repr__() which contains newlines it does not get escaped.
- # However in either case we want to preserve the newline.
- if py.builtin._istext(obj) or py.builtin._isbytes(obj):
- s = obj
- is_repr = False
- else:
- s = py.io.saferepr(obj)
- is_repr = True
- if py.builtin._istext(s):
- t = py.builtin.text
- else:
- t = py.builtin.bytes
- s = s.replace(t("\n"), t("\n~")).replace(t("%"), t("%%"))
- if is_repr:
- s = s.replace(t("\\n"), t("\n~"))
- return s
-
-def _should_repr_global_name(obj):
- return not hasattr(obj, "__name__") and not py.builtin.callable(obj)
-
-def _format_boolop(explanations, is_or):
- explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")"
- if py.builtin._istext(explanation):
- t = py.builtin.text
- else:
- t = py.builtin.bytes
- return explanation.replace(t('%'), t('%%'))
-
-def _call_reprcompare(ops, results, expls, each_obj):
- for i, res, expl in zip(range(len(ops)), results, expls):
- try:
- done = not res
- except Exception:
- done = True
- if done:
- break
- if util._reprcompare is not None:
- custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1])
- if custom is not None:
- return custom
- return expl
-
-
-unary_map = {
- ast.Not: "not %s",
- ast.Invert: "~%s",
- ast.USub: "-%s",
- ast.UAdd: "+%s"
-}
-
-binop_map = {
- ast.BitOr: "|",
- ast.BitXor: "^",
- ast.BitAnd: "&",
- ast.LShift: "<<",
- ast.RShift: ">>",
- ast.Add: "+",
- ast.Sub: "-",
- ast.Mult: "*",
- ast.Div: "/",
- ast.FloorDiv: "//",
- ast.Mod: "%%", # escaped for string formatting
- ast.Eq: "==",
- ast.NotEq: "!=",
- ast.Lt: "<",
- ast.LtE: "<=",
- ast.Gt: ">",
- ast.GtE: ">=",
- ast.Pow: "**",
- ast.Is: "is",
- ast.IsNot: "is not",
- ast.In: "in",
- ast.NotIn: "not in"
-}
-# Python 3.5+ compatibility
-try:
- binop_map[ast.MatMult] = "@"
-except AttributeError:
- pass
-
-# Python 3.4+ compatibility
-if hasattr(ast, "NameConstant"):
- _NameConstant = ast.NameConstant
-else:
- def _NameConstant(c):
- return ast.Name(str(c), ast.Load())
-
-
-def set_location(node, lineno, col_offset):
- """Set node location information recursively."""
- def _fix(node, lineno, col_offset):
- if "lineno" in node._attributes:
- node.lineno = lineno
- if "col_offset" in node._attributes:
- node.col_offset = col_offset
- for child in ast.iter_child_nodes(node):
- _fix(child, lineno, col_offset)
- _fix(node, lineno, col_offset)
- return node
-
-
-class AssertionRewriter(ast.NodeVisitor):
- """Assertion rewriting implementation.
-
- The main entrypoint is to call .run() with an ast.Module instance,
- this will then find all the assert statements and re-write them to
- provide intermediate values and a detailed assertion error. See
- http://pybites.blogspot.be/2011/07/behind-scenes-of-pytests-new-assertion.html
- for an overview of how this works.
-
- The entry point here is .run() which will iterate over all the
- statements in an ast.Module and for each ast.Assert statement it
- finds call .visit() with it. Then .visit_Assert() takes over and
- is responsible for creating new ast statements to replace the
- original assert statement: it re-writes the test of an assertion
- to provide intermediate values and replace it with an if statement
- which raises an assertion error with a detailed explanation in
- case the expression is false.
-
- For this .visit_Assert() uses the visitor pattern to visit all the
- AST nodes of the ast.Assert.test field, each visit call returning
- an AST node and the corresponding explanation string. During this
- state is kept in several instance attributes:
-
- :statements: All the AST statements which will replace the assert
- statement.
-
- :variables: This is populated by .variable() with each variable
- used by the statements so that they can all be set to None at
- the end of the statements.
-
- :variable_counter: Counter to create new unique variables needed
- by statements. Variables are created using .variable() and
- have the form of "@py_assert0".
-
- :on_failure: The AST statements which will be executed if the
- assertion test fails. This is the code which will construct
- the failure message and raises the AssertionError.
-
- :explanation_specifiers: A dict filled by .explanation_param()
- with %-formatting placeholders and their corresponding
- expressions to use in the building of an assertion message.
- This is used by .pop_format_context() to build a message.
-
- :stack: A stack of the explanation_specifiers dicts maintained by
- .push_format_context() and .pop_format_context() which allows
- to build another %-formatted string while already building one.
-
- This state is reset on every new assert statement visited and used
- by the other visitors.
-
- """
-
- def run(self, mod):
- """Find all assert statements in *mod* and rewrite them."""
- if not mod.body:
- # Nothing to do.
- return
- # Insert some special imports at the top of the module but after any
- # docstrings and __future__ imports.
- aliases = [ast.alias(py.builtin.builtins.__name__, "@py_builtins"),
- ast.alias("_pytest.assertion.rewrite", "@pytest_ar")]
- expect_docstring = True
- pos = 0
- lineno = 0
- for item in mod.body:
- if (expect_docstring and isinstance(item, ast.Expr) and
- isinstance(item.value, ast.Str)):
- doc = item.value.s
- if "PYTEST_DONT_REWRITE" in doc:
- # The module has disabled assertion rewriting.
- return
- lineno += len(doc) - 1
- expect_docstring = False
- elif (not isinstance(item, ast.ImportFrom) or item.level > 0 or
- item.module != "__future__"):
- lineno = item.lineno
- break
- pos += 1
- imports = [ast.Import([alias], lineno=lineno, col_offset=0)
- for alias in aliases]
- mod.body[pos:pos] = imports
- # Collect asserts.
- nodes = [mod]
- while nodes:
- node = nodes.pop()
- for name, field in ast.iter_fields(node):
- if isinstance(field, list):
- new = []
- for i, child in enumerate(field):
- if isinstance(child, ast.Assert):
- # Transform assert.
- new.extend(self.visit(child))
- else:
- new.append(child)
- if isinstance(child, ast.AST):
- nodes.append(child)
- setattr(node, name, new)
- elif (isinstance(field, ast.AST) and
- # Don't recurse into expressions as they can't contain
- # asserts.
- not isinstance(field, ast.expr)):
- nodes.append(field)
-
- def variable(self):
- """Get a new variable."""
- # Use a character invalid in python identifiers to avoid clashing.
- name = "@py_assert" + str(next(self.variable_counter))
- self.variables.append(name)
- return name
-
- def assign(self, expr):
- """Give *expr* a name."""
- name = self.variable()
- self.statements.append(ast.Assign([ast.Name(name, ast.Store())], expr))
- return ast.Name(name, ast.Load())
-
- def display(self, expr):
- """Call py.io.saferepr on the expression."""
- return self.helper("saferepr", expr)
-
- def helper(self, name, *args):
- """Call a helper in this module."""
- py_name = ast.Name("@pytest_ar", ast.Load())
- attr = ast.Attribute(py_name, "_" + name, ast.Load())
- return ast_Call(attr, list(args), [])
-
- def builtin(self, name):
- """Return the builtin called *name*."""
- builtin_name = ast.Name("@py_builtins", ast.Load())
- return ast.Attribute(builtin_name, name, ast.Load())
-
- def explanation_param(self, expr):
- """Return a new named %-formatting placeholder for expr.
-
- This creates a %-formatting placeholder for expr in the
- current formatting context, e.g. ``%(py0)s``. The placeholder
- and expr are placed in the current format context so that it
- can be used on the next call to .pop_format_context().
-
- """
- specifier = "py" + str(next(self.variable_counter))
- self.explanation_specifiers[specifier] = expr
- return "%(" + specifier + ")s"
-
- def push_format_context(self):
- """Create a new formatting context.
-
- The format context is used for when an explanation wants to
- have a variable value formatted in the assertion message. In
- this case the value required can be added using
- .explanation_param(). Finally .pop_format_context() is used
- to format a string of %-formatted values as added by
- .explanation_param().
-
- """
- self.explanation_specifiers = {}
- self.stack.append(self.explanation_specifiers)
-
- def pop_format_context(self, expl_expr):
- """Format the %-formatted string with current format context.
-
- The expl_expr should be an ast.Str instance constructed from
- the %-placeholders created by .explanation_param(). This will
- add the required code to format said string to .on_failure and
- return the ast.Name instance of the formatted string.
-
- """
- current = self.stack.pop()
- if self.stack:
- self.explanation_specifiers = self.stack[-1]
- keys = [ast.Str(key) for key in current.keys()]
- format_dict = ast.Dict(keys, list(current.values()))
- form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
- name = "@py_format" + str(next(self.variable_counter))
- self.on_failure.append(ast.Assign([ast.Name(name, ast.Store())], form))
- return ast.Name(name, ast.Load())
-
- def generic_visit(self, node):
- """Handle expressions we don't have custom code for."""
- assert isinstance(node, ast.expr)
- res = self.assign(node)
- return res, self.explanation_param(self.display(res))
-
- def visit_Assert(self, assert_):
- """Return the AST statements to replace the ast.Assert instance.
-
- This re-writes the test of an assertion to provide
- intermediate values and replace it with an if statement which
- raises an assertion error with a detailed explanation in case
- the expression is false.
-
- """
- self.statements = []
- self.variables = []
- self.variable_counter = itertools.count()
- self.stack = []
- self.on_failure = []
- self.push_format_context()
- # Rewrite assert into a bunch of statements.
- top_condition, explanation = self.visit(assert_.test)
- # Create failure message.
- body = self.on_failure
- negation = ast.UnaryOp(ast.Not(), top_condition)
- self.statements.append(ast.If(negation, body, []))
- if assert_.msg:
- assertmsg = self.helper('format_assertmsg', assert_.msg)
- explanation = "\n>assert " + explanation
- else:
- assertmsg = ast.Str("")
- explanation = "assert " + explanation
- template = ast.BinOp(assertmsg, ast.Add(), ast.Str(explanation))
- msg = self.pop_format_context(template)
- fmt = self.helper("format_explanation", msg)
- err_name = ast.Name("AssertionError", ast.Load())
- exc = ast_Call(err_name, [fmt], [])
- if sys.version_info[0] >= 3:
- raise_ = ast.Raise(exc, None)
- else:
- raise_ = ast.Raise(exc, None, None)
- body.append(raise_)
- # Clear temporary variables by setting them to None.
- if self.variables:
- variables = [ast.Name(name, ast.Store())
- for name in self.variables]
- clear = ast.Assign(variables, _NameConstant(None))
- self.statements.append(clear)
- # Fix line numbers.
- for stmt in self.statements:
- set_location(stmt, assert_.lineno, assert_.col_offset)
- return self.statements
-
- def visit_Name(self, name):
- # Display the repr of the name if it's a local variable or
- # _should_repr_global_name() thinks it's acceptable.
- locs = ast_Call(self.builtin("locals"), [], [])
- inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs])
- dorepr = self.helper("should_repr_global_name", name)
- test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
- expr = ast.IfExp(test, self.display(name), ast.Str(name.id))
- return name, self.explanation_param(expr)
-
- def visit_BoolOp(self, boolop):
- res_var = self.variable()
- expl_list = self.assign(ast.List([], ast.Load()))
- app = ast.Attribute(expl_list, "append", ast.Load())
- is_or = int(isinstance(boolop.op, ast.Or))
- body = save = self.statements
- fail_save = self.on_failure
- levels = len(boolop.values) - 1
- self.push_format_context()
- # Process each operand, short-circuting if needed.
- for i, v in enumerate(boolop.values):
- if i:
- fail_inner = []
- # cond is set in a prior loop iteration below
- self.on_failure.append(ast.If(cond, fail_inner, [])) # noqa
- self.on_failure = fail_inner
- self.push_format_context()
- res, expl = self.visit(v)
- body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
- expl_format = self.pop_format_context(ast.Str(expl))
- call = ast_Call(app, [expl_format], [])
- self.on_failure.append(ast.Expr(call))
- if i < levels:
- cond = res
- if is_or:
- cond = ast.UnaryOp(ast.Not(), cond)
- inner = []
- self.statements.append(ast.If(cond, inner, []))
- self.statements = body = inner
- self.statements = save
- self.on_failure = fail_save
- expl_template = self.helper("format_boolop", expl_list, ast.Num(is_or))
- expl = self.pop_format_context(expl_template)
- return ast.Name(res_var, ast.Load()), self.explanation_param(expl)
-
- def visit_UnaryOp(self, unary):
- pattern = unary_map[unary.op.__class__]
- operand_res, operand_expl = self.visit(unary.operand)
- res = self.assign(ast.UnaryOp(unary.op, operand_res))
- return res, pattern % (operand_expl,)
-
- def visit_BinOp(self, binop):
- symbol = binop_map[binop.op.__class__]
- left_expr, left_expl = self.visit(binop.left)
- right_expr, right_expl = self.visit(binop.right)
- explanation = "(%s %s %s)" % (left_expl, symbol, right_expl)
- res = self.assign(ast.BinOp(left_expr, binop.op, right_expr))
- return res, explanation
-
- def visit_Call_35(self, call):
- """
- visit `ast.Call` nodes on Python3.5 and after
- """
- new_func, func_expl = self.visit(call.func)
- arg_expls = []
- new_args = []
- new_kwargs = []
- for arg in call.args:
- res, expl = self.visit(arg)
- arg_expls.append(expl)
- new_args.append(res)
- for keyword in call.keywords:
- res, expl = self.visit(keyword.value)
- new_kwargs.append(ast.keyword(keyword.arg, res))
- if keyword.arg:
- arg_expls.append(keyword.arg + "=" + expl)
- else: ## **args have `arg` keywords with an .arg of None
- arg_expls.append("**" + expl)
-
- expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
- new_call = ast.Call(new_func, new_args, new_kwargs)
- res = self.assign(new_call)
- res_expl = self.explanation_param(self.display(res))
- outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
- return res, outer_expl
-
- def visit_Starred(self, starred):
- # From Python 3.5, a Starred node can appear in a function call
- res, expl = self.visit(starred.value)
- return starred, '*' + expl
-
- def visit_Call_legacy(self, call):
- """
- visit `ast.Call nodes on 3.4 and below`
- """
- new_func, func_expl = self.visit(call.func)
- arg_expls = []
- new_args = []
- new_kwargs = []
- new_star = new_kwarg = None
- for arg in call.args:
- res, expl = self.visit(arg)
- new_args.append(res)
- arg_expls.append(expl)
- for keyword in call.keywords:
- res, expl = self.visit(keyword.value)
- new_kwargs.append(ast.keyword(keyword.arg, res))
- arg_expls.append(keyword.arg + "=" + expl)
- if call.starargs:
- new_star, expl = self.visit(call.starargs)
- arg_expls.append("*" + expl)
- if call.kwargs:
- new_kwarg, expl = self.visit(call.kwargs)
- arg_expls.append("**" + expl)
- expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
- new_call = ast.Call(new_func, new_args, new_kwargs,
- new_star, new_kwarg)
- res = self.assign(new_call)
- res_expl = self.explanation_param(self.display(res))
- outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
- return res, outer_expl
-
- # ast.Call signature changed on 3.5,
- # conditionally change which methods is named
- # visit_Call depending on Python version
- if sys.version_info >= (3, 5):
- visit_Call = visit_Call_35
- else:
- visit_Call = visit_Call_legacy
-
-
- def visit_Attribute(self, attr):
- if not isinstance(attr.ctx, ast.Load):
- return self.generic_visit(attr)
- value, value_expl = self.visit(attr.value)
- res = self.assign(ast.Attribute(value, attr.attr, ast.Load()))
- res_expl = self.explanation_param(self.display(res))
- pat = "%s\n{%s = %s.%s\n}"
- expl = pat % (res_expl, res_expl, value_expl, attr.attr)
- return res, expl
-
- def visit_Compare(self, comp):
- self.push_format_context()
- left_res, left_expl = self.visit(comp.left)
- res_variables = [self.variable() for i in range(len(comp.ops))]
- load_names = [ast.Name(v, ast.Load()) for v in res_variables]
- store_names = [ast.Name(v, ast.Store()) for v in res_variables]
- it = zip(range(len(comp.ops)), comp.ops, comp.comparators)
- expls = []
- syms = []
- results = [left_res]
- for i, op, next_operand in it:
- next_res, next_expl = self.visit(next_operand)
- results.append(next_res)
- sym = binop_map[op.__class__]
- syms.append(ast.Str(sym))
- expl = "%s %s %s" % (left_expl, sym, next_expl)
- expls.append(ast.Str(expl))
- res_expr = ast.Compare(left_res, [op], [next_res])
- self.statements.append(ast.Assign([store_names[i]], res_expr))
- left_res, left_expl = next_res, next_expl
- # Use pytest.assertion.util._reprcompare if that's available.
- expl_call = self.helper("call_reprcompare",
- ast.Tuple(syms, ast.Load()),
- ast.Tuple(load_names, ast.Load()),
- ast.Tuple(expls, ast.Load()),
- ast.Tuple(results, ast.Load()))
- if len(comp.ops) > 1:
- res = ast.BoolOp(ast.And(), load_names)
- else:
- res = load_names[0]
- return res, self.explanation_param(self.pop_format_context(expl_call))
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/util.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/util.py
deleted file mode 100644
index f2f23efea27..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/assertion/util.py
+++ /dev/null
@@ -1,332 +0,0 @@
-"""Utilities for assertion debugging"""
-import pprint
-
-import _pytest._code
-import py
-try:
- from collections import Sequence
-except ImportError:
- Sequence = list
-
-BuiltinAssertionError = py.builtin.builtins.AssertionError
-u = py.builtin._totext
-
-# The _reprcompare attribute on the util module is used by the new assertion
-# interpretation code and assertion rewriter to detect this plugin was
-# loaded and in turn call the hooks defined here as part of the
-# DebugInterpreter.
-_reprcompare = None
-
-
-# the re-encoding is needed for python2 repr
-# with non-ascii characters (see issue 877 and 1379)
-def ecu(s):
- try:
- return u(s, 'utf-8', 'replace')
- except TypeError:
- return s
-
-
-def format_explanation(explanation):
- """This formats an explanation
-
- Normally all embedded newlines are escaped, however there are
- three exceptions: \n{, \n} and \n~. The first two are intended
- cover nested explanations, see function and attribute explanations
- for examples (.visit_Call(), visit_Attribute()). The last one is
- for when one explanation needs to span multiple lines, e.g. when
- displaying diffs.
- """
- explanation = ecu(explanation)
- explanation = _collapse_false(explanation)
- lines = _split_explanation(explanation)
- result = _format_lines(lines)
- return u('\n').join(result)
-
-
-def _collapse_false(explanation):
- """Collapse expansions of False
-
- So this strips out any "assert False\n{where False = ...\n}"
- blocks.
- """
- where = 0
- while True:
- start = where = explanation.find("False\n{False = ", where)
- if where == -1:
- break
- level = 0
- prev_c = explanation[start]
- for i, c in enumerate(explanation[start:]):
- if prev_c + c == "\n{":
- level += 1
- elif prev_c + c == "\n}":
- level -= 1
- if not level:
- break
- prev_c = c
- else:
- raise AssertionError("unbalanced braces: %r" % (explanation,))
- end = start + i
- where = end
- if explanation[end - 1] == '\n':
- explanation = (explanation[:start] + explanation[start+15:end-1] +
- explanation[end+1:])
- where -= 17
- return explanation
-
-
-def _split_explanation(explanation):
- """Return a list of individual lines in the explanation
-
- This will return a list of lines split on '\n{', '\n}' and '\n~'.
- Any other newlines will be escaped and appear in the line as the
- literal '\n' characters.
- """
- raw_lines = (explanation or u('')).split('\n')
- lines = [raw_lines[0]]
- for l in raw_lines[1:]:
- if l and l[0] in ['{', '}', '~', '>']:
- lines.append(l)
- else:
- lines[-1] += '\\n' + l
- return lines
-
-
-def _format_lines(lines):
- """Format the individual lines
-
- This will replace the '{', '}' and '~' characters of our mini
- formatting language with the proper 'where ...', 'and ...' and ' +
- ...' text, taking care of indentation along the way.
-
- Return a list of formatted lines.
- """
- result = lines[:1]
- stack = [0]
- stackcnt = [0]
- for line in lines[1:]:
- if line.startswith('{'):
- if stackcnt[-1]:
- s = u('and ')
- else:
- s = u('where ')
- stack.append(len(result))
- stackcnt[-1] += 1
- stackcnt.append(0)
- result.append(u(' +') + u(' ')*(len(stack)-1) + s + line[1:])
- elif line.startswith('}'):
- stack.pop()
- stackcnt.pop()
- result[stack[-1]] += line[1:]
- else:
- assert line[0] in ['~', '>']
- stack[-1] += 1
- indent = len(stack) if line.startswith('~') else len(stack) - 1
- result.append(u(' ')*indent + line[1:])
- assert len(stack) == 1
- return result
-
-
-# Provide basestring in python3
-try:
- basestring = basestring
-except NameError:
- basestring = str
-
-
-def assertrepr_compare(config, op, left, right):
- """Return specialised explanations for some operators/operands"""
- width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op
- left_repr = py.io.saferepr(left, maxsize=int(width/2))
- right_repr = py.io.saferepr(right, maxsize=width-len(left_repr))
-
- summary = u('%s %s %s') % (ecu(left_repr), op, ecu(right_repr))
-
- issequence = lambda x: (isinstance(x, (list, tuple, Sequence)) and
- not isinstance(x, basestring))
- istext = lambda x: isinstance(x, basestring)
- isdict = lambda x: isinstance(x, dict)
- isset = lambda x: isinstance(x, (set, frozenset))
-
- def isiterable(obj):
- try:
- iter(obj)
- return not istext(obj)
- except TypeError:
- return False
-
- verbose = config.getoption('verbose')
- explanation = None
- try:
- if op == '==':
- if istext(left) and istext(right):
- explanation = _diff_text(left, right, verbose)
- else:
- if issequence(left) and issequence(right):
- explanation = _compare_eq_sequence(left, right, verbose)
- elif isset(left) and isset(right):
- explanation = _compare_eq_set(left, right, verbose)
- elif isdict(left) and isdict(right):
- explanation = _compare_eq_dict(left, right, verbose)
- if isiterable(left) and isiterable(right):
- expl = _compare_eq_iterable(left, right, verbose)
- if explanation is not None:
- explanation.extend(expl)
- else:
- explanation = expl
- elif op == 'not in':
- if istext(left) and istext(right):
- explanation = _notin_text(left, right, verbose)
- except Exception:
- explanation = [
- u('(pytest_assertion plugin: representation of details failed. '
- 'Probably an object has a faulty __repr__.)'),
- u(_pytest._code.ExceptionInfo())]
-
- if not explanation:
- return None
-
- return [summary] + explanation
-
-
-def _diff_text(left, right, verbose=False):
- """Return the explanation for the diff between text or bytes
-
- Unless --verbose is used this will skip leading and trailing
- characters which are identical to keep the diff minimal.
-
- If the input are bytes they will be safely converted to text.
- """
- from difflib import ndiff
- explanation = []
- if isinstance(left, py.builtin.bytes):
- left = u(repr(left)[1:-1]).replace(r'\n', '\n')
- if isinstance(right, py.builtin.bytes):
- right = u(repr(right)[1:-1]).replace(r'\n', '\n')
- if not verbose:
- i = 0 # just in case left or right has zero length
- for i in range(min(len(left), len(right))):
- if left[i] != right[i]:
- break
- if i > 42:
- i -= 10 # Provide some context
- explanation = [u('Skipping %s identical leading '
- 'characters in diff, use -v to show') % i]
- left = left[i:]
- right = right[i:]
- if len(left) == len(right):
- for i in range(len(left)):
- if left[-i] != right[-i]:
- break
- if i > 42:
- i -= 10 # Provide some context
- explanation += [u('Skipping %s identical trailing '
- 'characters in diff, use -v to show') % i]
- left = left[:-i]
- right = right[:-i]
- explanation += [line.strip('\n')
- for line in ndiff(left.splitlines(),
- right.splitlines())]
- return explanation
-
-
-def _compare_eq_iterable(left, right, verbose=False):
- if not verbose:
- return [u('Use -v to get the full diff')]
- # dynamic import to speedup pytest
- import difflib
-
- try:
- left_formatting = pprint.pformat(left).splitlines()
- right_formatting = pprint.pformat(right).splitlines()
- explanation = [u('Full diff:')]
- except Exception:
- # hack: PrettyPrinter.pformat() in python 2 fails when formatting items that can't be sorted(), ie, calling
- # sorted() on a list would raise. See issue #718.
- # As a workaround, the full diff is generated by using the repr() string of each item of each container.
- left_formatting = sorted(repr(x) for x in left)
- right_formatting = sorted(repr(x) for x in right)
- explanation = [u('Full diff (fallback to calling repr on each item):')]
- explanation.extend(line.strip() for line in difflib.ndiff(left_formatting, right_formatting))
- return explanation
-
-
-def _compare_eq_sequence(left, right, verbose=False):
- explanation = []
- for i in range(min(len(left), len(right))):
- if left[i] != right[i]:
- explanation += [u('At index %s diff: %r != %r')
- % (i, left[i], right[i])]
- break
- if len(left) > len(right):
- explanation += [u('Left contains more items, first extra item: %s')
- % py.io.saferepr(left[len(right)],)]
- elif len(left) < len(right):
- explanation += [
- u('Right contains more items, first extra item: %s') %
- py.io.saferepr(right[len(left)],)]
- return explanation
-
-
-def _compare_eq_set(left, right, verbose=False):
- explanation = []
- diff_left = left - right
- diff_right = right - left
- if diff_left:
- explanation.append(u('Extra items in the left set:'))
- for item in diff_left:
- explanation.append(py.io.saferepr(item))
- if diff_right:
- explanation.append(u('Extra items in the right set:'))
- for item in diff_right:
- explanation.append(py.io.saferepr(item))
- return explanation
-
-
-def _compare_eq_dict(left, right, verbose=False):
- explanation = []
- common = set(left).intersection(set(right))
- same = dict((k, left[k]) for k in common if left[k] == right[k])
- if same and not verbose:
- explanation += [u('Omitting %s identical items, use -v to show') %
- len(same)]
- elif same:
- explanation += [u('Common items:')]
- explanation += pprint.pformat(same).splitlines()
- diff = set(k for k in common if left[k] != right[k])
- if diff:
- explanation += [u('Differing items:')]
- for k in diff:
- explanation += [py.io.saferepr({k: left[k]}) + ' != ' +
- py.io.saferepr({k: right[k]})]
- extra_left = set(left) - set(right)
- if extra_left:
- explanation.append(u('Left contains more items:'))
- explanation.extend(pprint.pformat(
- dict((k, left[k]) for k in extra_left)).splitlines())
- extra_right = set(right) - set(left)
- if extra_right:
- explanation.append(u('Right contains more items:'))
- explanation.extend(pprint.pformat(
- dict((k, right[k]) for k in extra_right)).splitlines())
- return explanation
-
-
-def _notin_text(term, text, verbose=False):
- index = text.find(term)
- head = text[:index]
- tail = text[index+len(term):]
- correct_text = head + tail
- diff = _diff_text(correct_text, text, verbose)
- newdiff = [u('%s is contained here:') % py.io.saferepr(term, maxsize=42)]
- for line in diff:
- if line.startswith(u('Skipping')):
- continue
- if line.startswith(u('- ')):
- continue
- if line.startswith(u('+ ')):
- newdiff.append(u(' ') + line[2:])
- else:
- newdiff.append(line)
- return newdiff
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/cacheprovider.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/cacheprovider.py
deleted file mode 100755
index 0657001f2d4..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/cacheprovider.py
+++ /dev/null
@@ -1,245 +0,0 @@
-"""
-merged implementation of the cache provider
-
-the name cache was not choosen to ensure pluggy automatically
-ignores the external pytest-cache
-"""
-
-import py
-import pytest
-import json
-from os.path import sep as _sep, altsep as _altsep
-
-
-class Cache(object):
- def __init__(self, config):
- self.config = config
- self._cachedir = config.rootdir.join(".cache")
- self.trace = config.trace.root.get("cache")
- if config.getvalue("cacheclear"):
- self.trace("clearing cachedir")
- if self._cachedir.check():
- self._cachedir.remove()
- self._cachedir.mkdir()
-
- def makedir(self, name):
- """ return a directory path object with the given name. If the
- directory does not yet exist, it will be created. You can use it
- to manage files likes e. g. store/retrieve database
- dumps across test sessions.
-
- :param name: must be a string not containing a ``/`` separator.
- Make sure the name contains your plugin or application
- identifiers to prevent clashes with other cache users.
- """
- if _sep in name or _altsep is not None and _altsep in name:
- raise ValueError("name is not allowed to contain path separators")
- return self._cachedir.ensure_dir("d", name)
-
- def _getvaluepath(self, key):
- return self._cachedir.join('v', *key.split('/'))
-
- def get(self, key, default):
- """ return cached value for the given key. If no value
- was yet cached or the value cannot be read, the specified
- default is returned.
-
- :param key: must be a ``/`` separated value. Usually the first
- name is the name of your plugin or your application.
- :param default: must be provided in case of a cache-miss or
- invalid cache values.
-
- """
- path = self._getvaluepath(key)
- if path.check():
- try:
- with path.open("r") as f:
- return json.load(f)
- except ValueError:
- self.trace("cache-invalid at %s" % (path,))
- return default
-
- def set(self, key, value):
- """ save value for the given key.
-
- :param key: must be a ``/`` separated value. Usually the first
- name is the name of your plugin or your application.
- :param value: must be of any combination of basic
- python types, including nested types
- like e. g. lists of dictionaries.
- """
- path = self._getvaluepath(key)
- try:
- path.dirpath().ensure_dir()
- except (py.error.EEXIST, py.error.EACCES):
- self.config.warn(
- code='I9', message='could not create cache path %s' % (path,)
- )
- return
- try:
- f = path.open('w')
- except py.error.ENOTDIR:
- self.config.warn(
- code='I9', message='cache could not write path %s' % (path,))
- else:
- with f:
- self.trace("cache-write %s: %r" % (key, value,))
- json.dump(value, f, indent=2, sort_keys=True)
-
-
-class LFPlugin:
- """ Plugin which implements the --lf (run last-failing) option """
- def __init__(self, config):
- self.config = config
- active_keys = 'lf', 'failedfirst'
- self.active = any(config.getvalue(key) for key in active_keys)
- if self.active:
- self.lastfailed = config.cache.get("cache/lastfailed", {})
- else:
- self.lastfailed = {}
-
- def pytest_report_header(self):
- if self.active:
- if not self.lastfailed:
- mode = "run all (no recorded failures)"
- else:
- mode = "rerun last %d failures%s" % (
- len(self.lastfailed),
- " first" if self.config.getvalue("failedfirst") else "")
- return "run-last-failure: %s" % mode
-
- def pytest_runtest_logreport(self, report):
- if report.failed and "xfail" not in report.keywords:
- self.lastfailed[report.nodeid] = True
- elif not report.failed:
- if report.when == "call":
- self.lastfailed.pop(report.nodeid, None)
-
- def pytest_collectreport(self, report):
- passed = report.outcome in ('passed', 'skipped')
- if passed:
- if report.nodeid in self.lastfailed:
- self.lastfailed.pop(report.nodeid)
- self.lastfailed.update(
- (item.nodeid, True)
- for item in report.result)
- else:
- self.lastfailed[report.nodeid] = True
-
- def pytest_collection_modifyitems(self, session, config, items):
- if self.active and self.lastfailed:
- previously_failed = []
- previously_passed = []
- for item in items:
- if item.nodeid in self.lastfailed:
- previously_failed.append(item)
- else:
- previously_passed.append(item)
- if not previously_failed and previously_passed:
- # running a subset of all tests with recorded failures outside
- # of the set of tests currently executing
- pass
- elif self.config.getvalue("failedfirst"):
- items[:] = previously_failed + previously_passed
- else:
- items[:] = previously_failed
- config.hook.pytest_deselected(items=previously_passed)
-
- def pytest_sessionfinish(self, session):
- config = self.config
- if config.getvalue("cacheshow") or hasattr(config, "slaveinput"):
- return
- prev_failed = config.cache.get("cache/lastfailed", None) is not None
- if (session.testscollected and prev_failed) or self.lastfailed:
- config.cache.set("cache/lastfailed", self.lastfailed)
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("general")
- group.addoption(
- '--lf', '--last-failed', action='store_true', dest="lf",
- help="rerun only the tests that failed "
- "at the last run (or all if none failed)")
- group.addoption(
- '--ff', '--failed-first', action='store_true', dest="failedfirst",
- help="run all tests but run the last failures first. "
- "This may re-order tests and thus lead to "
- "repeated fixture setup/teardown")
- group.addoption(
- '--cache-show', action='store_true', dest="cacheshow",
- help="show cache contents, don't perform collection or tests")
- group.addoption(
- '--cache-clear', action='store_true', dest="cacheclear",
- help="remove all cache contents at start of test run.")
-
-
-def pytest_cmdline_main(config):
- if config.option.cacheshow:
- from _pytest.main import wrap_session
- return wrap_session(config, cacheshow)
-
-
-
-@pytest.hookimpl(tryfirst=True)
-def pytest_configure(config):
- config.cache = Cache(config)
- config.pluginmanager.register(LFPlugin(config), "lfplugin")
-
-
-@pytest.fixture
-def cache(request):
- """
- Return a cache object that can persist state between testing sessions.
-
- cache.get(key, default)
- cache.set(key, value)
-
- Keys must be a ``/`` separated value, where the first part is usually the
- name of your plugin or application to avoid clashes with other cache users.
-
- Values can be any object handled by the json stdlib module.
- """
- return request.config.cache
-
-
-def pytest_report_header(config):
- if config.option.verbose:
- relpath = py.path.local().bestrelpath(config.cache._cachedir)
- return "cachedir: %s" % relpath
-
-
-def cacheshow(config, session):
- from pprint import pprint
- tw = py.io.TerminalWriter()
- tw.line("cachedir: " + str(config.cache._cachedir))
- if not config.cache._cachedir.check():
- tw.line("cache is empty")
- return 0
- dummy = object()
- basedir = config.cache._cachedir
- vdir = basedir.join("v")
- tw.sep("-", "cache values")
- for valpath in vdir.visit(lambda x: x.isfile()):
- key = valpath.relto(vdir).replace(valpath.sep, "/")
- val = config.cache.get(key, dummy)
- if val is dummy:
- tw.line("%s contains unreadable content, "
- "will be ignored" % key)
- else:
- tw.line("%s contains:" % key)
- stream = py.io.TextIO()
- pprint(val, stream=stream)
- for line in stream.getvalue().splitlines():
- tw.line(" " + line)
-
- ddir = basedir.join("d")
- if ddir.isdir() and ddir.listdir():
- tw.sep("-", "cache directories")
- for p in basedir.join("d").visit():
- #if p.check(dir=1):
- # print("%s/" % p.relto(basedir))
- if p.isfile():
- key = p.relto(basedir)
- tw.line("%s is a file of length %d" % (
- key, p.size()))
- return 0
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/capture.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/capture.py
deleted file mode 100644
index 3895a714aa0..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/capture.py
+++ /dev/null
@@ -1,472 +0,0 @@
-"""
-per-test stdout/stderr capturing mechanism.
-
-"""
-from __future__ import with_statement
-
-import sys
-import os
-from tempfile import TemporaryFile
-
-import py
-import pytest
-
-from py.io import TextIO
-unicode = py.builtin.text
-
-patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'}
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("general")
- group._addoption(
- '--capture', action="store",
- default="fd" if hasattr(os, "dup") else "sys",
- metavar="method", choices=['fd', 'sys', 'no'],
- help="per-test capturing method: one of fd|sys|no.")
- group._addoption(
- '-s', action="store_const", const="no", dest="capture",
- help="shortcut for --capture=no.")
-
-
-@pytest.hookimpl(hookwrapper=True)
-def pytest_load_initial_conftests(early_config, parser, args):
- _readline_workaround()
- ns = early_config.known_args_namespace
- pluginmanager = early_config.pluginmanager
- capman = CaptureManager(ns.capture)
- pluginmanager.register(capman, "capturemanager")
-
- # make sure that capturemanager is properly reset at final shutdown
- early_config.add_cleanup(capman.reset_capturings)
-
- # make sure logging does not raise exceptions at the end
- def silence_logging_at_shutdown():
- if "logging" in sys.modules:
- sys.modules["logging"].raiseExceptions = False
- early_config.add_cleanup(silence_logging_at_shutdown)
-
- # finally trigger conftest loading but while capturing (issue93)
- capman.init_capturings()
- outcome = yield
- out, err = capman.suspendcapture()
- if outcome.excinfo is not None:
- sys.stdout.write(out)
- sys.stderr.write(err)
-
-
-class CaptureManager:
- def __init__(self, method):
- self._method = method
-
- def _getcapture(self, method):
- if method == "fd":
- return MultiCapture(out=True, err=True, Capture=FDCapture)
- elif method == "sys":
- return MultiCapture(out=True, err=True, Capture=SysCapture)
- elif method == "no":
- return MultiCapture(out=False, err=False, in_=False)
- else:
- raise ValueError("unknown capturing method: %r" % method)
-
- def init_capturings(self):
- assert not hasattr(self, "_capturing")
- self._capturing = self._getcapture(self._method)
- self._capturing.start_capturing()
-
- def reset_capturings(self):
- cap = self.__dict__.pop("_capturing", None)
- if cap is not None:
- cap.pop_outerr_to_orig()
- cap.stop_capturing()
-
- def resumecapture(self):
- self._capturing.resume_capturing()
-
- def suspendcapture(self, in_=False):
- self.deactivate_funcargs()
- cap = getattr(self, "_capturing", None)
- if cap is not None:
- try:
- outerr = cap.readouterr()
- finally:
- cap.suspend_capturing(in_=in_)
- return outerr
-
- def activate_funcargs(self, pyfuncitem):
- capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None)
- if capfuncarg is not None:
- capfuncarg._start()
- self._capfuncarg = capfuncarg
-
- def deactivate_funcargs(self):
- capfuncarg = self.__dict__.pop("_capfuncarg", None)
- if capfuncarg is not None:
- capfuncarg.close()
-
- @pytest.hookimpl(hookwrapper=True)
- def pytest_make_collect_report(self, collector):
- if isinstance(collector, pytest.File):
- self.resumecapture()
- outcome = yield
- out, err = self.suspendcapture()
- rep = outcome.get_result()
- if out:
- rep.sections.append(("Captured stdout", out))
- if err:
- rep.sections.append(("Captured stderr", err))
- else:
- yield
-
- @pytest.hookimpl(hookwrapper=True)
- def pytest_runtest_setup(self, item):
- self.resumecapture()
- yield
- self.suspendcapture_item(item, "setup")
-
- @pytest.hookimpl(hookwrapper=True)
- def pytest_runtest_call(self, item):
- self.resumecapture()
- self.activate_funcargs(item)
- yield
- #self.deactivate_funcargs() called from suspendcapture()
- self.suspendcapture_item(item, "call")
-
- @pytest.hookimpl(hookwrapper=True)
- def pytest_runtest_teardown(self, item):
- self.resumecapture()
- yield
- self.suspendcapture_item(item, "teardown")
-
- @pytest.hookimpl(tryfirst=True)
- def pytest_keyboard_interrupt(self, excinfo):
- self.reset_capturings()
-
- @pytest.hookimpl(tryfirst=True)
- def pytest_internalerror(self, excinfo):
- self.reset_capturings()
-
- def suspendcapture_item(self, item, when):
- out, err = self.suspendcapture()
- item.add_report_section(when, "stdout", out)
- item.add_report_section(when, "stderr", err)
-
-error_capsysfderror = "cannot use capsys and capfd at the same time"
-
-
-@pytest.fixture
-def capsys(request):
- """enables capturing of writes to sys.stdout/sys.stderr and makes
- captured output available via ``capsys.readouterr()`` method calls
- which return a ``(out, err)`` tuple.
- """
- if "capfd" in request._funcargs:
- raise request.raiseerror(error_capsysfderror)
- request.node._capfuncarg = c = CaptureFixture(SysCapture)
- return c
-
-@pytest.fixture
-def capfd(request):
- """enables capturing of writes to file descriptors 1 and 2 and makes
- captured output available via ``capfd.readouterr()`` method calls
- which return a ``(out, err)`` tuple.
- """
- if "capsys" in request._funcargs:
- request.raiseerror(error_capsysfderror)
- if not hasattr(os, 'dup'):
- pytest.skip("capfd funcarg needs os.dup")
- request.node._capfuncarg = c = CaptureFixture(FDCapture)
- return c
-
-
-class CaptureFixture:
- def __init__(self, captureclass):
- self.captureclass = captureclass
-
- def _start(self):
- self._capture = MultiCapture(out=True, err=True, in_=False,
- Capture=self.captureclass)
- self._capture.start_capturing()
-
- def close(self):
- cap = self.__dict__.pop("_capture", None)
- if cap is not None:
- self._outerr = cap.pop_outerr_to_orig()
- cap.stop_capturing()
-
- def readouterr(self):
- try:
- return self._capture.readouterr()
- except AttributeError:
- return self._outerr
-
-
-def safe_text_dupfile(f, mode, default_encoding="UTF8"):
- """ return a open text file object that's a duplicate of f on the
- FD-level if possible.
- """
- encoding = getattr(f, "encoding", None)
- try:
- fd = f.fileno()
- except Exception:
- if "b" not in getattr(f, "mode", "") and hasattr(f, "encoding"):
- # we seem to have a text stream, let's just use it
- return f
- else:
- newfd = os.dup(fd)
- if "b" not in mode:
- mode += "b"
- f = os.fdopen(newfd, mode, 0) # no buffering
- return EncodedFile(f, encoding or default_encoding)
-
-
-class EncodedFile(object):
- errors = "strict" # possibly needed by py3 code (issue555)
- def __init__(self, buffer, encoding):
- self.buffer = buffer
- self.encoding = encoding
-
- def write(self, obj):
- if isinstance(obj, unicode):
- obj = obj.encode(self.encoding, "replace")
- self.buffer.write(obj)
-
- def writelines(self, linelist):
- data = ''.join(linelist)
- self.write(data)
-
- def __getattr__(self, name):
- return getattr(object.__getattribute__(self, "buffer"), name)
-
-
-class MultiCapture(object):
- out = err = in_ = None
-
- def __init__(self, out=True, err=True, in_=True, Capture=None):
- if in_:
- self.in_ = Capture(0)
- if out:
- self.out = Capture(1)
- if err:
- self.err = Capture(2)
-
- def start_capturing(self):
- if self.in_:
- self.in_.start()
- if self.out:
- self.out.start()
- if self.err:
- self.err.start()
-
- def pop_outerr_to_orig(self):
- """ pop current snapshot out/err capture and flush to orig streams. """
- out, err = self.readouterr()
- if out:
- self.out.writeorg(out)
- if err:
- self.err.writeorg(err)
- return out, err
-
- def suspend_capturing(self, in_=False):
- if self.out:
- self.out.suspend()
- if self.err:
- self.err.suspend()
- if in_ and self.in_:
- self.in_.suspend()
- self._in_suspended = True
-
- def resume_capturing(self):
- if self.out:
- self.out.resume()
- if self.err:
- self.err.resume()
- if hasattr(self, "_in_suspended"):
- self.in_.resume()
- del self._in_suspended
-
- def stop_capturing(self):
- """ stop capturing and reset capturing streams """
- if hasattr(self, '_reset'):
- raise ValueError("was already stopped")
- self._reset = True
- if self.out:
- self.out.done()
- if self.err:
- self.err.done()
- if self.in_:
- self.in_.done()
-
- def readouterr(self):
- """ return snapshot unicode value of stdout/stderr capturings. """
- return (self.out.snap() if self.out is not None else "",
- self.err.snap() if self.err is not None else "")
-
-class NoCapture:
- __init__ = start = done = suspend = resume = lambda *args: None
-
-class FDCapture:
- """ Capture IO to/from a given os-level filedescriptor. """
-
- def __init__(self, targetfd, tmpfile=None):
- self.targetfd = targetfd
- try:
- self.targetfd_save = os.dup(self.targetfd)
- except OSError:
- self.start = lambda: None
- self.done = lambda: None
- else:
- if targetfd == 0:
- assert not tmpfile, "cannot set tmpfile with stdin"
- tmpfile = open(os.devnull, "r")
- self.syscapture = SysCapture(targetfd)
- else:
- if tmpfile is None:
- f = TemporaryFile()
- with f:
- tmpfile = safe_text_dupfile(f, mode="wb+")
- if targetfd in patchsysdict:
- self.syscapture = SysCapture(targetfd, tmpfile)
- else:
- self.syscapture = NoCapture()
- self.tmpfile = tmpfile
- self.tmpfile_fd = tmpfile.fileno()
-
- def __repr__(self):
- return "<FDCapture %s oldfd=%s>" % (self.targetfd, self.targetfd_save)
-
- def start(self):
- """ Start capturing on targetfd using memorized tmpfile. """
- try:
- os.fstat(self.targetfd_save)
- except (AttributeError, OSError):
- raise ValueError("saved filedescriptor not valid anymore")
- os.dup2(self.tmpfile_fd, self.targetfd)
- self.syscapture.start()
-
- def snap(self):
- f = self.tmpfile
- f.seek(0)
- res = f.read()
- if res:
- enc = getattr(f, "encoding", None)
- if enc and isinstance(res, bytes):
- res = py.builtin._totext(res, enc, "replace")
- f.truncate(0)
- f.seek(0)
- return res
- return ''
-
- def done(self):
- """ stop capturing, restore streams, return original capture file,
- seeked to position zero. """
- targetfd_save = self.__dict__.pop("targetfd_save")
- os.dup2(targetfd_save, self.targetfd)
- os.close(targetfd_save)
- self.syscapture.done()
- self.tmpfile.close()
-
- def suspend(self):
- self.syscapture.suspend()
- os.dup2(self.targetfd_save, self.targetfd)
-
- def resume(self):
- self.syscapture.resume()
- os.dup2(self.tmpfile_fd, self.targetfd)
-
- def writeorg(self, data):
- """ write to original file descriptor. """
- if py.builtin._istext(data):
- data = data.encode("utf8") # XXX use encoding of original stream
- os.write(self.targetfd_save, data)
-
-
-class SysCapture:
- def __init__(self, fd, tmpfile=None):
- name = patchsysdict[fd]
- self._old = getattr(sys, name)
- self.name = name
- if tmpfile is None:
- if name == "stdin":
- tmpfile = DontReadFromInput()
- else:
- tmpfile = TextIO()
- self.tmpfile = tmpfile
-
- def start(self):
- setattr(sys, self.name, self.tmpfile)
-
- def snap(self):
- f = self.tmpfile
- res = f.getvalue()
- f.truncate(0)
- f.seek(0)
- return res
-
- def done(self):
- setattr(sys, self.name, self._old)
- del self._old
- self.tmpfile.close()
-
- def suspend(self):
- setattr(sys, self.name, self._old)
-
- def resume(self):
- setattr(sys, self.name, self.tmpfile)
-
- def writeorg(self, data):
- self._old.write(data)
- self._old.flush()
-
-
-class DontReadFromInput:
- """Temporary stub class. Ideally when stdin is accessed, the
- capturing should be turned off, with possibly all data captured
- so far sent to the screen. This should be configurable, though,
- because in automated test runs it is better to crash than
- hang indefinitely.
- """
-
- encoding = None
-
- def read(self, *args):
- raise IOError("reading from stdin while output is captured")
- readline = read
- readlines = read
- __iter__ = read
-
- def fileno(self):
- raise ValueError("redirected Stdin is pseudofile, has no fileno()")
-
- def isatty(self):
- return False
-
- def close(self):
- pass
-
-
-def _readline_workaround():
- """
- Ensure readline is imported so that it attaches to the correct stdio
- handles on Windows.
-
- Pdb uses readline support where available--when not running from the Python
- prompt, the readline module is not imported until running the pdb REPL. If
- running py.test with the --pdb option this means the readline module is not
- imported until after I/O capture has been started.
-
- This is a problem for pyreadline, which is often used to implement readline
- support on Windows, as it does not attach to the correct handles for stdout
- and/or stdin if they have been redirected by the FDCapture mechanism. This
- workaround ensures that readline is imported before I/O capture is setup so
- that it can attach to the actual stdin/out for the console.
-
- See https://github.com/pytest-dev/pytest/pull/1281
- """
-
- if not sys.platform.startswith('win32'):
- return
- try:
- import readline # noqa
- except ImportError:
- pass
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/config.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/config.py
deleted file mode 100644
index fb7b1774f68..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/config.py
+++ /dev/null
@@ -1,1192 +0,0 @@
-""" command line options, ini-file and conftest.py processing. """
-import argparse
-import shlex
-import traceback
-import types
-import warnings
-
-import py
-# DON't import pytest here because it causes import cycle troubles
-import sys, os
-import _pytest._code
-import _pytest.hookspec # the extension point definitions
-from _pytest._pluggy import PluginManager, HookimplMarker, HookspecMarker
-
-hookimpl = HookimplMarker("pytest")
-hookspec = HookspecMarker("pytest")
-
-# pytest startup
-#
-
-
-class ConftestImportFailure(Exception):
- def __init__(self, path, excinfo):
- Exception.__init__(self, path, excinfo)
- self.path = path
- self.excinfo = excinfo
-
-
-def main(args=None, plugins=None):
- """ return exit code, after performing an in-process test run.
-
- :arg args: list of command line arguments.
-
- :arg plugins: list of plugin objects to be auto-registered during
- initialization.
- """
- try:
- try:
- config = _prepareconfig(args, plugins)
- except ConftestImportFailure as e:
- tw = py.io.TerminalWriter(sys.stderr)
- for line in traceback.format_exception(*e.excinfo):
- tw.line(line.rstrip(), red=True)
- tw.line("ERROR: could not load %s\n" % (e.path), red=True)
- return 4
- else:
- try:
- config.pluginmanager.check_pending()
- return config.hook.pytest_cmdline_main(config=config)
- finally:
- config._ensure_unconfigure()
- except UsageError as e:
- for msg in e.args:
- sys.stderr.write("ERROR: %s\n" %(msg,))
- return 4
-
-class cmdline: # compatibility namespace
- main = staticmethod(main)
-
-class UsageError(Exception):
- """ error in pytest usage or invocation"""
-
-_preinit = []
-
-default_plugins = (
- "mark main terminal runner python pdb unittest capture skipping "
- "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
- "junitxml resultlog doctest cacheprovider").split()
-
-builtin_plugins = set(default_plugins)
-builtin_plugins.add("pytester")
-
-
-def _preloadplugins():
- assert not _preinit
- _preinit.append(get_config())
-
-def get_config():
- if _preinit:
- return _preinit.pop(0)
- # subsequent calls to main will create a fresh instance
- pluginmanager = PytestPluginManager()
- config = Config(pluginmanager)
- for spec in default_plugins:
- pluginmanager.import_plugin(spec)
- return config
-
-def get_plugin_manager():
- """
- Obtain a new instance of the
- :py:class:`_pytest.config.PytestPluginManager`, with default plugins
- already loaded.
-
- This function can be used by integration with other tools, like hooking
- into pytest to run tests into an IDE.
- """
- return get_config().pluginmanager
-
-def _prepareconfig(args=None, plugins=None):
- if args is None:
- args = sys.argv[1:]
- elif isinstance(args, py.path.local):
- args = [str(args)]
- elif not isinstance(args, (tuple, list)):
- if not isinstance(args, str):
- raise ValueError("not a string or argument list: %r" % (args,))
- args = shlex.split(args)
- config = get_config()
- pluginmanager = config.pluginmanager
- try:
- if plugins:
- for plugin in plugins:
- if isinstance(plugin, py.builtin._basestring):
- pluginmanager.consider_pluginarg(plugin)
- else:
- pluginmanager.register(plugin)
- return pluginmanager.hook.pytest_cmdline_parse(
- pluginmanager=pluginmanager, args=args)
- except BaseException:
- config._ensure_unconfigure()
- raise
-
-
-class PytestPluginManager(PluginManager):
- """
- Overwrites :py:class:`pluggy.PluginManager` to add pytest-specific
- functionality:
-
- * loading plugins from the command line, ``PYTEST_PLUGIN`` env variable and
- ``pytest_plugins`` global variables found in plugins being loaded;
- * ``conftest.py`` loading during start-up;
- """
- def __init__(self):
- super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_")
- self._conftest_plugins = set()
-
- # state related to local conftest plugins
- self._path2confmods = {}
- self._conftestpath2mod = {}
- self._confcutdir = None
- self._noconftest = False
-
- self.add_hookspecs(_pytest.hookspec)
- self.register(self)
- if os.environ.get('PYTEST_DEBUG'):
- err = sys.stderr
- encoding = getattr(err, 'encoding', 'utf8')
- try:
- err = py.io.dupfile(err, encoding=encoding)
- except Exception:
- pass
- self.trace.root.setwriter(err.write)
- self.enable_tracing()
-
- def addhooks(self, module_or_class):
- """
- .. deprecated:: 2.8
-
- Use :py:meth:`pluggy.PluginManager.add_hookspecs` instead.
- """
- warning = dict(code="I2",
- fslocation=_pytest._code.getfslineno(sys._getframe(1)),
- nodeid=None,
- message="use pluginmanager.add_hookspecs instead of "
- "deprecated addhooks() method.")
- self._warn(warning)
- return self.add_hookspecs(module_or_class)
-
- def parse_hookimpl_opts(self, plugin, name):
- # pytest hooks are always prefixed with pytest_
- # so we avoid accessing possibly non-readable attributes
- # (see issue #1073)
- if not name.startswith("pytest_"):
- return
- # ignore some historic special names which can not be hooks anyway
- if name == "pytest_plugins" or name.startswith("pytest_funcarg__"):
- return
-
- method = getattr(plugin, name)
- opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
- if opts is not None:
- for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
- opts.setdefault(name, hasattr(method, name))
- return opts
-
- def parse_hookspec_opts(self, module_or_class, name):
- opts = super(PytestPluginManager, self).parse_hookspec_opts(
- module_or_class, name)
- if opts is None:
- method = getattr(module_or_class, name)
- if name.startswith("pytest_"):
- opts = {"firstresult": hasattr(method, "firstresult"),
- "historic": hasattr(method, "historic")}
- return opts
-
- def _verify_hook(self, hook, hookmethod):
- super(PytestPluginManager, self)._verify_hook(hook, hookmethod)
- if "__multicall__" in hookmethod.argnames:
- fslineno = _pytest._code.getfslineno(hookmethod.function)
- warning = dict(code="I1",
- fslocation=fslineno,
- nodeid=None,
- message="%r hook uses deprecated __multicall__ "
- "argument" % (hook.name))
- self._warn(warning)
-
- def register(self, plugin, name=None):
- ret = super(PytestPluginManager, self).register(plugin, name)
- if ret:
- self.hook.pytest_plugin_registered.call_historic(
- kwargs=dict(plugin=plugin, manager=self))
- return ret
-
- def getplugin(self, name):
- # support deprecated naming because plugins (xdist e.g.) use it
- return self.get_plugin(name)
-
- def hasplugin(self, name):
- """Return True if the plugin with the given name is registered."""
- return bool(self.get_plugin(name))
-
- def pytest_configure(self, config):
- # XXX now that the pluginmanager exposes hookimpl(tryfirst...)
- # we should remove tryfirst/trylast as markers
- config.addinivalue_line("markers",
- "tryfirst: mark a hook implementation function such that the "
- "plugin machinery will try to call it first/as early as possible.")
- config.addinivalue_line("markers",
- "trylast: mark a hook implementation function such that the "
- "plugin machinery will try to call it last/as late as possible.")
-
- def _warn(self, message):
- kwargs = message if isinstance(message, dict) else {
- 'code': 'I1',
- 'message': message,
- 'fslocation': None,
- 'nodeid': None,
- }
- self.hook.pytest_logwarning.call_historic(kwargs=kwargs)
-
- #
- # internal API for local conftest plugin handling
- #
- def _set_initial_conftests(self, namespace):
- """ load initial conftest files given a preparsed "namespace".
- As conftest files may add their own command line options
- which have arguments ('--my-opt somepath') we might get some
- false positives. All builtin and 3rd party plugins will have
- been loaded, however, so common options will not confuse our logic
- here.
- """
- current = py.path.local()
- self._confcutdir = current.join(namespace.confcutdir, abs=True) \
- if namespace.confcutdir else None
- self._noconftest = namespace.noconftest
- testpaths = namespace.file_or_dir
- foundanchor = False
- for path in testpaths:
- path = str(path)
- # remove node-id syntax
- i = path.find("::")
- if i != -1:
- path = path[:i]
- anchor = current.join(path, abs=1)
- if exists(anchor): # we found some file object
- self._try_load_conftest(anchor)
- foundanchor = True
- if not foundanchor:
- self._try_load_conftest(current)
-
- def _try_load_conftest(self, anchor):
- self._getconftestmodules(anchor)
- # let's also consider test* subdirs
- if anchor.check(dir=1):
- for x in anchor.listdir("test*"):
- if x.check(dir=1):
- self._getconftestmodules(x)
-
- def _getconftestmodules(self, path):
- if self._noconftest:
- return []
- try:
- return self._path2confmods[path]
- except KeyError:
- if path.isfile():
- clist = self._getconftestmodules(path.dirpath())
- else:
- # XXX these days we may rather want to use config.rootdir
- # and allow users to opt into looking into the rootdir parent
- # directories instead of requiring to specify confcutdir
- clist = []
- for parent in path.parts():
- if self._confcutdir and self._confcutdir.relto(parent):
- continue
- conftestpath = parent.join("conftest.py")
- if conftestpath.isfile():
- mod = self._importconftest(conftestpath)
- clist.append(mod)
-
- self._path2confmods[path] = clist
- return clist
-
- def _rget_with_confmod(self, name, path):
- modules = self._getconftestmodules(path)
- for mod in reversed(modules):
- try:
- return mod, getattr(mod, name)
- except AttributeError:
- continue
- raise KeyError(name)
-
- def _importconftest(self, conftestpath):
- try:
- return self._conftestpath2mod[conftestpath]
- except KeyError:
- pkgpath = conftestpath.pypkgpath()
- if pkgpath is None:
- _ensure_removed_sysmodule(conftestpath.purebasename)
- try:
- mod = conftestpath.pyimport()
- except Exception:
- raise ConftestImportFailure(conftestpath, sys.exc_info())
-
- self._conftest_plugins.add(mod)
- self._conftestpath2mod[conftestpath] = mod
- dirpath = conftestpath.dirpath()
- if dirpath in self._path2confmods:
- for path, mods in self._path2confmods.items():
- if path and path.relto(dirpath) or path == dirpath:
- assert mod not in mods
- mods.append(mod)
- self.trace("loaded conftestmodule %r" %(mod))
- self.consider_conftest(mod)
- return mod
-
- #
- # API for bootstrapping plugin loading
- #
- #
-
- def consider_preparse(self, args):
- for opt1,opt2 in zip(args, args[1:]):
- if opt1 == "-p":
- self.consider_pluginarg(opt2)
-
- def consider_pluginarg(self, arg):
- if arg.startswith("no:"):
- name = arg[3:]
- self.set_blocked(name)
- if not name.startswith("pytest_"):
- self.set_blocked("pytest_" + name)
- else:
- self.import_plugin(arg)
-
- def consider_conftest(self, conftestmodule):
- if self.register(conftestmodule, name=conftestmodule.__file__):
- self.consider_module(conftestmodule)
-
- def consider_env(self):
- self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
-
- def consider_module(self, mod):
- self._import_plugin_specs(getattr(mod, "pytest_plugins", None))
-
- def _import_plugin_specs(self, spec):
- if spec:
- if isinstance(spec, str):
- spec = spec.split(",")
- for import_spec in spec:
- self.import_plugin(import_spec)
-
- def import_plugin(self, modname):
- # most often modname refers to builtin modules, e.g. "pytester",
- # "terminal" or "capture". Those plugins are registered under their
- # basename for historic purposes but must be imported with the
- # _pytest prefix.
- assert isinstance(modname, str)
- if self.get_plugin(modname) is not None:
- return
- if modname in builtin_plugins:
- importspec = "_pytest." + modname
- else:
- importspec = modname
- try:
- __import__(importspec)
- except ImportError as e:
- new_exc = ImportError('Error importing plugin "%s": %s' % (modname, e))
- # copy over name and path attributes
- for attr in ('name', 'path'):
- if hasattr(e, attr):
- setattr(new_exc, attr, getattr(e, attr))
- raise new_exc
- except Exception as e:
- import pytest
- if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
- raise
- self._warn("skipped plugin %r: %s" %((modname, e.msg)))
- else:
- mod = sys.modules[importspec]
- self.register(mod, modname)
- self.consider_module(mod)
-
-
-class Parser:
- """ Parser for command line arguments and ini-file values.
-
- :ivar extra_info: dict of generic param -> value to display in case
- there's an error processing the command line arguments.
- """
-
- def __init__(self, usage=None, processopt=None):
- self._anonymous = OptionGroup("custom options", parser=self)
- self._groups = []
- self._processopt = processopt
- self._usage = usage
- self._inidict = {}
- self._ininames = []
- self.extra_info = {}
-
- def processoption(self, option):
- if self._processopt:
- if option.dest:
- self._processopt(option)
-
- def getgroup(self, name, description="", after=None):
- """ get (or create) a named option Group.
-
- :name: name of the option group.
- :description: long description for --help output.
- :after: name of other group, used for ordering --help output.
-
- The returned group object has an ``addoption`` method with the same
- signature as :py:func:`parser.addoption
- <_pytest.config.Parser.addoption>` but will be shown in the
- respective group in the output of ``pytest. --help``.
- """
- for group in self._groups:
- if group.name == name:
- return group
- group = OptionGroup(name, description, parser=self)
- i = 0
- for i, grp in enumerate(self._groups):
- if grp.name == after:
- break
- self._groups.insert(i+1, group)
- return group
-
- def addoption(self, *opts, **attrs):
- """ register a command line option.
-
- :opts: option names, can be short or long options.
- :attrs: same attributes which the ``add_option()`` function of the
- `argparse library
- <http://docs.python.org/2/library/argparse.html>`_
- accepts.
-
- After command line parsing options are available on the pytest config
- object via ``config.option.NAME`` where ``NAME`` is usually set
- by passing a ``dest`` attribute, for example
- ``addoption("--long", dest="NAME", ...)``.
- """
- self._anonymous.addoption(*opts, **attrs)
-
- def parse(self, args, namespace=None):
- from _pytest._argcomplete import try_argcomplete
- self.optparser = self._getparser()
- try_argcomplete(self.optparser)
- return self.optparser.parse_args([str(x) for x in args], namespace=namespace)
-
- def _getparser(self):
- from _pytest._argcomplete import filescompleter
- optparser = MyOptionParser(self, self.extra_info)
- groups = self._groups + [self._anonymous]
- for group in groups:
- if group.options:
- desc = group.description or group.name
- arggroup = optparser.add_argument_group(desc)
- for option in group.options:
- n = option.names()
- a = option.attrs()
- arggroup.add_argument(*n, **a)
- # bash like autocompletion for dirs (appending '/')
- optparser.add_argument(FILE_OR_DIR, nargs='*').completer=filescompleter
- return optparser
-
- def parse_setoption(self, args, option, namespace=None):
- parsedoption = self.parse(args, namespace=namespace)
- for name, value in parsedoption.__dict__.items():
- setattr(option, name, value)
- return getattr(parsedoption, FILE_OR_DIR)
-
- def parse_known_args(self, args, namespace=None):
- """parses and returns a namespace object with known arguments at this
- point.
- """
- return self.parse_known_and_unknown_args(args, namespace=namespace)[0]
-
- def parse_known_and_unknown_args(self, args, namespace=None):
- """parses and returns a namespace object with known arguments, and
- the remaining arguments unknown at this point.
- """
- optparser = self._getparser()
- args = [str(x) for x in args]
- return optparser.parse_known_args(args, namespace=namespace)
-
- def addini(self, name, help, type=None, default=None):
- """ register an ini-file option.
-
- :name: name of the ini-variable
- :type: type of the variable, can be ``pathlist``, ``args``, ``linelist``
- or ``bool``.
- :default: default value if no ini-file option exists but is queried.
-
- The value of ini-variables can be retrieved via a call to
- :py:func:`config.getini(name) <_pytest.config.Config.getini>`.
- """
- assert type in (None, "pathlist", "args", "linelist", "bool")
- self._inidict[name] = (help, type, default)
- self._ininames.append(name)
-
-
-class ArgumentError(Exception):
- """
- Raised if an Argument instance is created with invalid or
- inconsistent arguments.
- """
-
- def __init__(self, msg, option):
- self.msg = msg
- self.option_id = str(option)
-
- def __str__(self):
- if self.option_id:
- return "option %s: %s" % (self.option_id, self.msg)
- else:
- return self.msg
-
-
-class Argument:
- """class that mimics the necessary behaviour of optparse.Option """
- _typ_map = {
- 'int': int,
- 'string': str,
- }
- # enable after some grace period for plugin writers
- TYPE_WARN = False
-
- def __init__(self, *names, **attrs):
- """store parms in private vars for use in add_argument"""
- self._attrs = attrs
- self._short_opts = []
- self._long_opts = []
- self.dest = attrs.get('dest')
- if self.TYPE_WARN:
- try:
- help = attrs['help']
- if '%default' in help:
- warnings.warn(
- 'pytest now uses argparse. "%default" should be'
- ' changed to "%(default)s" ',
- FutureWarning,
- stacklevel=3)
- except KeyError:
- pass
- try:
- typ = attrs['type']
- except KeyError:
- pass
- else:
- # this might raise a keyerror as well, don't want to catch that
- if isinstance(typ, py.builtin._basestring):
- if typ == 'choice':
- if self.TYPE_WARN:
- warnings.warn(
- 'type argument to addoption() is a string %r.'
- ' For parsearg this is optional and when supplied '
- ' should be a type.'
- ' (options: %s)' % (typ, names),
- FutureWarning,
- stacklevel=3)
- # argparse expects a type here take it from
- # the type of the first element
- attrs['type'] = type(attrs['choices'][0])
- else:
- if self.TYPE_WARN:
- warnings.warn(
- 'type argument to addoption() is a string %r.'
- ' For parsearg this should be a type.'
- ' (options: %s)' % (typ, names),
- FutureWarning,
- stacklevel=3)
- attrs['type'] = Argument._typ_map[typ]
- # used in test_parseopt -> test_parse_defaultgetter
- self.type = attrs['type']
- else:
- self.type = typ
- try:
- # attribute existence is tested in Config._processopt
- self.default = attrs['default']
- except KeyError:
- pass
- self._set_opt_strings(names)
- if not self.dest:
- if self._long_opts:
- self.dest = self._long_opts[0][2:].replace('-', '_')
- else:
- try:
- self.dest = self._short_opts[0][1:]
- except IndexError:
- raise ArgumentError(
- 'need a long or short option', self)
-
- def names(self):
- return self._short_opts + self._long_opts
-
- def attrs(self):
- # update any attributes set by processopt
- attrs = 'default dest help'.split()
- if self.dest:
- attrs.append(self.dest)
- for attr in attrs:
- try:
- self._attrs[attr] = getattr(self, attr)
- except AttributeError:
- pass
- if self._attrs.get('help'):
- a = self._attrs['help']
- a = a.replace('%default', '%(default)s')
- #a = a.replace('%prog', '%(prog)s')
- self._attrs['help'] = a
- return self._attrs
-
- def _set_opt_strings(self, opts):
- """directly from optparse
-
- might not be necessary as this is passed to argparse later on"""
- for opt in opts:
- if len(opt) < 2:
- raise ArgumentError(
- "invalid option string %r: "
- "must be at least two characters long" % opt, self)
- elif len(opt) == 2:
- if not (opt[0] == "-" and opt[1] != "-"):
- raise ArgumentError(
- "invalid short option string %r: "
- "must be of the form -x, (x any non-dash char)" % opt,
- self)
- self._short_opts.append(opt)
- else:
- if not (opt[0:2] == "--" and opt[2] != "-"):
- raise ArgumentError(
- "invalid long option string %r: "
- "must start with --, followed by non-dash" % opt,
- self)
- self._long_opts.append(opt)
-
- def __repr__(self):
- retval = 'Argument('
- if self._short_opts:
- retval += '_short_opts: ' + repr(self._short_opts) + ', '
- if self._long_opts:
- retval += '_long_opts: ' + repr(self._long_opts) + ', '
- retval += 'dest: ' + repr(self.dest) + ', '
- if hasattr(self, 'type'):
- retval += 'type: ' + repr(self.type) + ', '
- if hasattr(self, 'default'):
- retval += 'default: ' + repr(self.default) + ', '
- if retval[-2:] == ', ': # always long enough to test ("Argument(" )
- retval = retval[:-2]
- retval += ')'
- return retval
-
-
-class OptionGroup:
- def __init__(self, name, description="", parser=None):
- self.name = name
- self.description = description
- self.options = []
- self.parser = parser
-
- def addoption(self, *optnames, **attrs):
- """ add an option to this group.
-
- if a shortened version of a long option is specified it will
- be suppressed in the help. addoption('--twowords', '--two-words')
- results in help showing '--two-words' only, but --twowords gets
- accepted **and** the automatic destination is in args.twowords
- """
- option = Argument(*optnames, **attrs)
- self._addoption_instance(option, shortupper=False)
-
- def _addoption(self, *optnames, **attrs):
- option = Argument(*optnames, **attrs)
- self._addoption_instance(option, shortupper=True)
-
- def _addoption_instance(self, option, shortupper=False):
- if not shortupper:
- for opt in option._short_opts:
- if opt[0] == '-' and opt[1].islower():
- raise ValueError("lowercase shortoptions reserved")
- if self.parser:
- self.parser.processoption(option)
- self.options.append(option)
-
-
-class MyOptionParser(argparse.ArgumentParser):
- def __init__(self, parser, extra_info=None):
- if not extra_info:
- extra_info = {}
- self._parser = parser
- argparse.ArgumentParser.__init__(self, usage=parser._usage,
- add_help=False, formatter_class=DropShorterLongHelpFormatter)
- # extra_info is a dict of (param -> value) to display if there's
- # an usage error to provide more contextual information to the user
- self.extra_info = extra_info
-
- def parse_args(self, args=None, namespace=None):
- """allow splitting of positional arguments"""
- args, argv = self.parse_known_args(args, namespace)
- if argv:
- for arg in argv:
- if arg and arg[0] == '-':
- lines = ['unrecognized arguments: %s' % (' '.join(argv))]
- for k, v in sorted(self.extra_info.items()):
- lines.append(' %s: %s' % (k, v))
- self.error('\n'.join(lines))
- getattr(args, FILE_OR_DIR).extend(argv)
- return args
-
-
-class DropShorterLongHelpFormatter(argparse.HelpFormatter):
- """shorten help for long options that differ only in extra hyphens
-
- - collapse **long** options that are the same except for extra hyphens
- - special action attribute map_long_option allows surpressing additional
- long options
- - shortcut if there are only two options and one of them is a short one
- - cache result on action object as this is called at least 2 times
- """
- def _format_action_invocation(self, action):
- orgstr = argparse.HelpFormatter._format_action_invocation(self, action)
- if orgstr and orgstr[0] != '-': # only optional arguments
- return orgstr
- res = getattr(action, '_formatted_action_invocation', None)
- if res:
- return res
- options = orgstr.split(', ')
- if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2):
- # a shortcut for '-h, --help' or '--abc', '-a'
- action._formatted_action_invocation = orgstr
- return orgstr
- return_list = []
- option_map = getattr(action, 'map_long_option', {})
- if option_map is None:
- option_map = {}
- short_long = {}
- for option in options:
- if len(option) == 2 or option[2] == ' ':
- continue
- if not option.startswith('--'):
- raise ArgumentError('long optional argument without "--": [%s]'
- % (option), self)
- xxoption = option[2:]
- if xxoption.split()[0] not in option_map:
- shortened = xxoption.replace('-', '')
- if shortened not in short_long or \
- len(short_long[shortened]) < len(xxoption):
- short_long[shortened] = xxoption
- # now short_long has been filled out to the longest with dashes
- # **and** we keep the right option ordering from add_argument
- for option in options: #
- if len(option) == 2 or option[2] == ' ':
- return_list.append(option)
- if option[2:] == short_long.get(option.replace('-', '')):
- return_list.append(option.replace(' ', '='))
- action._formatted_action_invocation = ', '.join(return_list)
- return action._formatted_action_invocation
-
-
-
-def _ensure_removed_sysmodule(modname):
- try:
- del sys.modules[modname]
- except KeyError:
- pass
-
-class CmdOptions(object):
- """ holds cmdline options as attributes."""
- def __init__(self, values=()):
- self.__dict__.update(values)
- def __repr__(self):
- return "<CmdOptions %r>" %(self.__dict__,)
- def copy(self):
- return CmdOptions(self.__dict__)
-
-class Notset:
- def __repr__(self):
- return "<NOTSET>"
-
-notset = Notset()
-FILE_OR_DIR = 'file_or_dir'
-
-class Config(object):
- """ access to configuration values, pluginmanager and plugin hooks. """
-
- def __init__(self, pluginmanager):
- #: access to command line option as attributes.
- #: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
- self.option = CmdOptions()
- _a = FILE_OR_DIR
- self._parser = Parser(
- usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a),
- processopt=self._processopt,
- )
- #: a pluginmanager instance
- self.pluginmanager = pluginmanager
- self.trace = self.pluginmanager.trace.root.get("config")
- self.hook = self.pluginmanager.hook
- self._inicache = {}
- self._opt2dest = {}
- self._cleanup = []
- self._warn = self.pluginmanager._warn
- self.pluginmanager.register(self, "pytestconfig")
- self._configured = False
- def do_setns(dic):
- import pytest
- setns(pytest, dic)
- self.hook.pytest_namespace.call_historic(do_setns, {})
- self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser))
-
- def add_cleanup(self, func):
- """ Add a function to be called when the config object gets out of
- use (usually coninciding with pytest_unconfigure)."""
- self._cleanup.append(func)
-
- def _do_configure(self):
- assert not self._configured
- self._configured = True
- self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
-
- def _ensure_unconfigure(self):
- if self._configured:
- self._configured = False
- self.hook.pytest_unconfigure(config=self)
- self.hook.pytest_configure._call_history = []
- while self._cleanup:
- fin = self._cleanup.pop()
- fin()
-
- def warn(self, code, message, fslocation=None):
- """ generate a warning for this test session. """
- self.hook.pytest_logwarning.call_historic(kwargs=dict(
- code=code, message=message,
- fslocation=fslocation, nodeid=None))
-
- def get_terminal_writer(self):
- return self.pluginmanager.get_plugin("terminalreporter")._tw
-
- def pytest_cmdline_parse(self, pluginmanager, args):
- # REF1 assert self == pluginmanager.config, (self, pluginmanager.config)
- self.parse(args)
- return self
-
- def notify_exception(self, excinfo, option=None):
- if option and option.fulltrace:
- style = "long"
- else:
- style = "native"
- excrepr = excinfo.getrepr(funcargs=True,
- showlocals=getattr(option, 'showlocals', False),
- style=style,
- )
- res = self.hook.pytest_internalerror(excrepr=excrepr,
- excinfo=excinfo)
- if not py.builtin.any(res):
- for line in str(excrepr).split("\n"):
- sys.stderr.write("INTERNALERROR> %s\n" %line)
- sys.stderr.flush()
-
- def cwd_relative_nodeid(self, nodeid):
- # nodeid's are relative to the rootpath, compute relative to cwd
- if self.invocation_dir != self.rootdir:
- fullpath = self.rootdir.join(nodeid)
- nodeid = self.invocation_dir.bestrelpath(fullpath)
- return nodeid
-
- @classmethod
- def fromdictargs(cls, option_dict, args):
- """ constructor useable for subprocesses. """
- config = get_config()
- config.option.__dict__.update(option_dict)
- config.parse(args, addopts=False)
- for x in config.option.plugins:
- config.pluginmanager.consider_pluginarg(x)
- return config
-
- def _processopt(self, opt):
- for name in opt._short_opts + opt._long_opts:
- self._opt2dest[name] = opt.dest
-
- if hasattr(opt, 'default') and opt.dest:
- if not hasattr(self.option, opt.dest):
- setattr(self.option, opt.dest, opt.default)
-
- @hookimpl(trylast=True)
- def pytest_load_initial_conftests(self, early_config):
- self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
-
- def _initini(self, args):
- ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy())
- r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args)
- self.rootdir, self.inifile, self.inicfg = r
- self._parser.extra_info['rootdir'] = self.rootdir
- self._parser.extra_info['inifile'] = self.inifile
- self.invocation_dir = py.path.local()
- self._parser.addini('addopts', 'extra command line options', 'args')
- self._parser.addini('minversion', 'minimally required pytest version')
-
- def _preparse(self, args, addopts=True):
- self._initini(args)
- if addopts:
- args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args
- args[:] = self.getini("addopts") + args
- self._checkversion()
- self.pluginmanager.consider_preparse(args)
- try:
- self.pluginmanager.load_setuptools_entrypoints("pytest11")
- except ImportError as e:
- self.warn("I2", "could not load setuptools entry import: %s" % (e,))
- self.pluginmanager.consider_env()
- self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
- if self.known_args_namespace.confcutdir is None and self.inifile:
- confcutdir = py.path.local(self.inifile).dirname
- self.known_args_namespace.confcutdir = confcutdir
- try:
- self.hook.pytest_load_initial_conftests(early_config=self,
- args=args, parser=self._parser)
- except ConftestImportFailure:
- e = sys.exc_info()[1]
- if ns.help or ns.version:
- # we don't want to prevent --help/--version to work
- # so just let is pass and print a warning at the end
- self._warn("could not load initial conftests (%s)\n" % e.path)
- else:
- raise
-
- def _checkversion(self):
- import pytest
- minver = self.inicfg.get('minversion', None)
- if minver:
- ver = minver.split(".")
- myver = pytest.__version__.split(".")
- if myver < ver:
- raise pytest.UsageError(
- "%s:%d: requires pytest-%s, actual pytest-%s'" %(
- self.inicfg.config.path, self.inicfg.lineof('minversion'),
- minver, pytest.__version__))
-
- def parse(self, args, addopts=True):
- # parse given cmdline arguments into this config object.
- assert not hasattr(self, 'args'), (
- "can only parse cmdline args at most once per Config object")
- self._origargs = args
- self.hook.pytest_addhooks.call_historic(
- kwargs=dict(pluginmanager=self.pluginmanager))
- self._preparse(args, addopts=addopts)
- # XXX deprecated hook:
- self.hook.pytest_cmdline_preparse(config=self, args=args)
- args = self._parser.parse_setoption(args, self.option, namespace=self.option)
- if not args:
- cwd = os.getcwd()
- if cwd == self.rootdir:
- args = self.getini('testpaths')
- if not args:
- args = [cwd]
- self.args = args
-
- def addinivalue_line(self, name, line):
- """ add a line to an ini-file option. The option must have been
- declared but might not yet be set in which case the line becomes the
- the first line in its value. """
- x = self.getini(name)
- assert isinstance(x, list)
- x.append(line) # modifies the cached list inline
-
- def getini(self, name):
- """ return configuration value from an :ref:`ini file <inifiles>`. If the
- specified name hasn't been registered through a prior
- :py:func:`parser.addini <pytest.config.Parser.addini>`
- call (usually from a plugin), a ValueError is raised. """
- try:
- return self._inicache[name]
- except KeyError:
- self._inicache[name] = val = self._getini(name)
- return val
-
- def _getini(self, name):
- try:
- description, type, default = self._parser._inidict[name]
- except KeyError:
- raise ValueError("unknown configuration value: %r" %(name,))
- try:
- value = self.inicfg[name]
- except KeyError:
- if default is not None:
- return default
- if type is None:
- return ''
- return []
- if type == "pathlist":
- dp = py.path.local(self.inicfg.config.path).dirpath()
- l = []
- for relpath in shlex.split(value):
- l.append(dp.join(relpath, abs=True))
- return l
- elif type == "args":
- return shlex.split(value)
- elif type == "linelist":
- return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
- elif type == "bool":
- return bool(_strtobool(value.strip()))
- else:
- assert type is None
- return value
-
- def _getconftest_pathlist(self, name, path):
- try:
- mod, relroots = self.pluginmanager._rget_with_confmod(name, path)
- except KeyError:
- return None
- modpath = py.path.local(mod.__file__).dirpath()
- l = []
- for relroot in relroots:
- if not isinstance(relroot, py.path.local):
- relroot = relroot.replace("/", py.path.local.sep)
- relroot = modpath.join(relroot, abs=True)
- l.append(relroot)
- return l
-
- def getoption(self, name, default=notset, skip=False):
- """ return command line option value.
-
- :arg name: name of the option. You may also specify
- the literal ``--OPT`` option instead of the "dest" option name.
- :arg default: default value if no option of that name exists.
- :arg skip: if True raise pytest.skip if option does not exists
- or has a None value.
- """
- name = self._opt2dest.get(name, name)
- try:
- val = getattr(self.option, name)
- if val is None and skip:
- raise AttributeError(name)
- return val
- except AttributeError:
- if default is not notset:
- return default
- if skip:
- import pytest
- pytest.skip("no %r option found" %(name,))
- raise ValueError("no option named %r" % (name,))
-
- def getvalue(self, name, path=None):
- """ (deprecated, use getoption()) """
- return self.getoption(name)
-
- def getvalueorskip(self, name, path=None):
- """ (deprecated, use getoption(skip=True)) """
- return self.getoption(name, skip=True)
-
-def exists(path, ignore=EnvironmentError):
- try:
- return path.check()
- except ignore:
- return False
-
-def getcfg(args, inibasenames):
- args = [x for x in args if not str(x).startswith("-")]
- if not args:
- args = [py.path.local()]
- for arg in args:
- arg = py.path.local(arg)
- for base in arg.parts(reverse=True):
- for inibasename in inibasenames:
- p = base.join(inibasename)
- if exists(p):
- iniconfig = py.iniconfig.IniConfig(p)
- if 'pytest' in iniconfig.sections:
- return base, p, iniconfig['pytest']
- elif inibasename == "pytest.ini":
- # allowed to be empty
- return base, p, {}
- return None, None, None
-
-
-def get_common_ancestor(args):
- # args are what we get after early command line parsing (usually
- # strings, but can be py.path.local objects as well)
- common_ancestor = None
- for arg in args:
- if str(arg)[0] == "-":
- continue
- p = py.path.local(arg)
- if common_ancestor is None:
- common_ancestor = p
- else:
- if p.relto(common_ancestor) or p == common_ancestor:
- continue
- elif common_ancestor.relto(p):
- common_ancestor = p
- else:
- shared = p.common(common_ancestor)
- if shared is not None:
- common_ancestor = shared
- if common_ancestor is None:
- common_ancestor = py.path.local()
- elif not common_ancestor.isdir():
- common_ancestor = common_ancestor.dirpath()
- return common_ancestor
-
-
-def determine_setup(inifile, args):
- if inifile:
- iniconfig = py.iniconfig.IniConfig(inifile)
- try:
- inicfg = iniconfig["pytest"]
- except KeyError:
- inicfg = None
- rootdir = get_common_ancestor(args)
- else:
- ancestor = get_common_ancestor(args)
- rootdir, inifile, inicfg = getcfg(
- [ancestor], ["pytest.ini", "tox.ini", "setup.cfg"])
- if rootdir is None:
- for rootdir in ancestor.parts(reverse=True):
- if rootdir.join("setup.py").exists():
- break
- else:
- rootdir = ancestor
- return rootdir, inifile, inicfg or {}
-
-
-def setns(obj, dic):
- import pytest
- for name, value in dic.items():
- if isinstance(value, dict):
- mod = getattr(obj, name, None)
- if mod is None:
- modname = "pytest.%s" % name
- mod = types.ModuleType(modname)
- sys.modules[modname] = mod
- mod.__all__ = []
- setattr(obj, name, mod)
- obj.__all__.append(name)
- setns(mod, value)
- else:
- setattr(obj, name, value)
- obj.__all__.append(name)
- #if obj != pytest:
- # pytest.__all__.append(name)
- setattr(pytest, name, value)
-
-
-def create_terminal_writer(config, *args, **kwargs):
- """Create a TerminalWriter instance configured according to the options
- in the config object. Every code which requires a TerminalWriter object
- and has access to a config object should use this function.
- """
- tw = py.io.TerminalWriter(*args, **kwargs)
- if config.option.color == 'yes':
- tw.hasmarkup = True
- if config.option.color == 'no':
- tw.hasmarkup = False
- return tw
-
-
-def _strtobool(val):
- """Convert a string representation of truth to true (1) or false (0).
-
- True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
- are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
- 'val' is anything else.
-
- .. note:: copied from distutils.util
- """
- val = val.lower()
- if val in ('y', 'yes', 't', 'true', 'on', '1'):
- return 1
- elif val in ('n', 'no', 'f', 'false', 'off', '0'):
- return 0
- else:
- raise ValueError("invalid truth value %r" % (val,))
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/doctest.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/doctest.py
deleted file mode 100644
index a57f7a4949e..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/doctest.py
+++ /dev/null
@@ -1,290 +0,0 @@
-""" discover and run doctests in modules and test files."""
-from __future__ import absolute_import
-
-import traceback
-
-import pytest
-from _pytest._code.code import TerminalRepr, ReprFileLocation, ExceptionInfo
-from _pytest.python import FixtureRequest
-
-
-
-def pytest_addoption(parser):
- parser.addini('doctest_optionflags', 'option flags for doctests',
- type="args", default=["ELLIPSIS"])
- group = parser.getgroup("collect")
- group.addoption("--doctest-modules",
- action="store_true", default=False,
- help="run doctests in all .py modules",
- dest="doctestmodules")
- group.addoption("--doctest-glob",
- action="append", default=[], metavar="pat",
- help="doctests file matching pattern, default: test*.txt",
- dest="doctestglob")
- group.addoption("--doctest-ignore-import-errors",
- action="store_true", default=False,
- help="ignore doctest ImportErrors",
- dest="doctest_ignore_import_errors")
-
-
-def pytest_collect_file(path, parent):
- config = parent.config
- if path.ext == ".py":
- if config.option.doctestmodules:
- return DoctestModule(path, parent)
- elif _is_doctest(config, path, parent):
- return DoctestTextfile(path, parent)
-
-
-def _is_doctest(config, path, parent):
- if path.ext in ('.txt', '.rst') and parent.session.isinitpath(path):
- return True
- globs = config.getoption("doctestglob") or ['test*.txt']
- for glob in globs:
- if path.check(fnmatch=glob):
- return True
- return False
-
-
-class ReprFailDoctest(TerminalRepr):
-
- def __init__(self, reprlocation, lines):
- self.reprlocation = reprlocation
- self.lines = lines
-
- def toterminal(self, tw):
- for line in self.lines:
- tw.line(line)
- self.reprlocation.toterminal(tw)
-
-
-class DoctestItem(pytest.Item):
-
- def __init__(self, name, parent, runner=None, dtest=None):
- super(DoctestItem, self).__init__(name, parent)
- self.runner = runner
- self.dtest = dtest
- self.obj = None
- self.fixture_request = None
-
- def setup(self):
- if self.dtest is not None:
- self.fixture_request = _setup_fixtures(self)
- globs = dict(getfixture=self.fixture_request.getfuncargvalue)
- self.dtest.globs.update(globs)
-
- def runtest(self):
- _check_all_skipped(self.dtest)
- self.runner.run(self.dtest)
-
- def repr_failure(self, excinfo):
- import doctest
- if excinfo.errisinstance((doctest.DocTestFailure,
- doctest.UnexpectedException)):
- doctestfailure = excinfo.value
- example = doctestfailure.example
- test = doctestfailure.test
- filename = test.filename
- if test.lineno is None:
- lineno = None
- else:
- lineno = test.lineno + example.lineno + 1
- message = excinfo.type.__name__
- reprlocation = ReprFileLocation(filename, lineno, message)
- checker = _get_checker()
- REPORT_UDIFF = doctest.REPORT_UDIFF
- if lineno is not None:
- lines = doctestfailure.test.docstring.splitlines(False)
- # add line numbers to the left of the error message
- lines = ["%03d %s" % (i + test.lineno + 1, x)
- for (i, x) in enumerate(lines)]
- # trim docstring error lines to 10
- lines = lines[example.lineno - 9:example.lineno + 1]
- else:
- lines = ['EXAMPLE LOCATION UNKNOWN, not showing all tests of that example']
- indent = '>>>'
- for line in example.source.splitlines():
- lines.append('??? %s %s' % (indent, line))
- indent = '...'
- if excinfo.errisinstance(doctest.DocTestFailure):
- lines += checker.output_difference(example,
- doctestfailure.got, REPORT_UDIFF).split("\n")
- else:
- inner_excinfo = ExceptionInfo(excinfo.value.exc_info)
- lines += ["UNEXPECTED EXCEPTION: %s" %
- repr(inner_excinfo.value)]
- lines += traceback.format_exception(*excinfo.value.exc_info)
- return ReprFailDoctest(reprlocation, lines)
- else:
- return super(DoctestItem, self).repr_failure(excinfo)
-
- def reportinfo(self):
- return self.fspath, None, "[doctest] %s" % self.name
-
-
-def _get_flag_lookup():
- import doctest
- return dict(DONT_ACCEPT_TRUE_FOR_1=doctest.DONT_ACCEPT_TRUE_FOR_1,
- DONT_ACCEPT_BLANKLINE=doctest.DONT_ACCEPT_BLANKLINE,
- NORMALIZE_WHITESPACE=doctest.NORMALIZE_WHITESPACE,
- ELLIPSIS=doctest.ELLIPSIS,
- IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL,
- COMPARISON_FLAGS=doctest.COMPARISON_FLAGS,
- ALLOW_UNICODE=_get_allow_unicode_flag(),
- ALLOW_BYTES=_get_allow_bytes_flag(),
- )
-
-
-def get_optionflags(parent):
- optionflags_str = parent.config.getini("doctest_optionflags")
- flag_lookup_table = _get_flag_lookup()
- flag_acc = 0
- for flag in optionflags_str:
- flag_acc |= flag_lookup_table[flag]
- return flag_acc
-
-
-class DoctestTextfile(DoctestItem, pytest.Module):
-
- def runtest(self):
- import doctest
- fixture_request = _setup_fixtures(self)
-
- # inspired by doctest.testfile; ideally we would use it directly,
- # but it doesn't support passing a custom checker
- text = self.fspath.read()
- filename = str(self.fspath)
- name = self.fspath.basename
- globs = dict(getfixture=fixture_request.getfuncargvalue)
- if '__name__' not in globs:
- globs['__name__'] = '__main__'
-
- optionflags = get_optionflags(self)
- runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
- checker=_get_checker())
-
- parser = doctest.DocTestParser()
- test = parser.get_doctest(text, globs, name, filename, 0)
- _check_all_skipped(test)
- runner.run(test)
-
-
-def _check_all_skipped(test):
- """raises pytest.skip() if all examples in the given DocTest have the SKIP
- option set.
- """
- import doctest
- all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples)
- if all_skipped:
- pytest.skip('all tests skipped by +SKIP option')
-
-
-class DoctestModule(pytest.Module):
- def collect(self):
- import doctest
- if self.fspath.basename == "conftest.py":
- module = self.config.pluginmanager._importconftest(self.fspath)
- else:
- try:
- module = self.fspath.pyimport()
- except ImportError:
- if self.config.getvalue('doctest_ignore_import_errors'):
- pytest.skip('unable to import module %r' % self.fspath)
- else:
- raise
- # uses internal doctest module parsing mechanism
- finder = doctest.DocTestFinder()
- optionflags = get_optionflags(self)
- runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
- checker=_get_checker())
- for test in finder.find(module, module.__name__):
- if test.examples: # skip empty doctests
- yield DoctestItem(test.name, self, runner, test)
-
-
-def _setup_fixtures(doctest_item):
- """
- Used by DoctestTextfile and DoctestItem to setup fixture information.
- """
- def func():
- pass
-
- doctest_item.funcargs = {}
- fm = doctest_item.session._fixturemanager
- doctest_item._fixtureinfo = fm.getfixtureinfo(node=doctest_item, func=func,
- cls=None, funcargs=False)
- fixture_request = FixtureRequest(doctest_item)
- fixture_request._fillfixtures()
- return fixture_request
-
-
-def _get_checker():
- """
- Returns a doctest.OutputChecker subclass that takes in account the
- ALLOW_UNICODE option to ignore u'' prefixes in strings and ALLOW_BYTES
- to strip b'' prefixes.
- Useful when the same doctest should run in Python 2 and Python 3.
-
- An inner class is used to avoid importing "doctest" at the module
- level.
- """
- if hasattr(_get_checker, 'LiteralsOutputChecker'):
- return _get_checker.LiteralsOutputChecker()
-
- import doctest
- import re
-
- class LiteralsOutputChecker(doctest.OutputChecker):
- """
- Copied from doctest_nose_plugin.py from the nltk project:
- https://github.com/nltk/nltk
-
- Further extended to also support byte literals.
- """
-
- _unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE)
- _bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE)
-
- def check_output(self, want, got, optionflags):
- res = doctest.OutputChecker.check_output(self, want, got,
- optionflags)
- if res:
- return True
-
- allow_unicode = optionflags & _get_allow_unicode_flag()
- allow_bytes = optionflags & _get_allow_bytes_flag()
- if not allow_unicode and not allow_bytes:
- return False
-
- else: # pragma: no cover
- def remove_prefixes(regex, txt):
- return re.sub(regex, r'\1\2', txt)
-
- if allow_unicode:
- want = remove_prefixes(self._unicode_literal_re, want)
- got = remove_prefixes(self._unicode_literal_re, got)
- if allow_bytes:
- want = remove_prefixes(self._bytes_literal_re, want)
- got = remove_prefixes(self._bytes_literal_re, got)
- res = doctest.OutputChecker.check_output(self, want, got,
- optionflags)
- return res
-
- _get_checker.LiteralsOutputChecker = LiteralsOutputChecker
- return _get_checker.LiteralsOutputChecker()
-
-
-def _get_allow_unicode_flag():
- """
- Registers and returns the ALLOW_UNICODE flag.
- """
- import doctest
- return doctest.register_optionflag('ALLOW_UNICODE')
-
-
-def _get_allow_bytes_flag():
- """
- Registers and returns the ALLOW_BYTES flag.
- """
- import doctest
- return doctest.register_optionflag('ALLOW_BYTES')
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/genscript.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/genscript.py
deleted file mode 100755
index d2962d8fc82..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/genscript.py
+++ /dev/null
@@ -1,132 +0,0 @@
-""" (deprecated) generate a single-file self-contained version of pytest """
-import os
-import sys
-import pkgutil
-
-import py
-import _pytest
-
-
-
-def find_toplevel(name):
- for syspath in sys.path:
- base = py.path.local(syspath)
- lib = base/name
- if lib.check(dir=1):
- return lib
- mod = base.join("%s.py" % name)
- if mod.check(file=1):
- return mod
- raise LookupError(name)
-
-def pkgname(toplevel, rootpath, path):
- parts = path.parts()[len(rootpath.parts()):]
- return '.'.join([toplevel] + [x.purebasename for x in parts])
-
-def pkg_to_mapping(name):
- toplevel = find_toplevel(name)
- name2src = {}
- if toplevel.check(file=1): # module
- name2src[toplevel.purebasename] = toplevel.read()
- else: # package
- for pyfile in toplevel.visit('*.py'):
- pkg = pkgname(name, toplevel, pyfile)
- name2src[pkg] = pyfile.read()
- # with wheels py source code might be not be installed
- # and the resulting genscript is useless, just bail out.
- assert name2src, "no source code found for %r at %r" %(name, toplevel)
- return name2src
-
-def compress_mapping(mapping):
- import base64, pickle, zlib
- data = pickle.dumps(mapping, 2)
- data = zlib.compress(data, 9)
- data = base64.encodestring(data)
- data = data.decode('ascii')
- return data
-
-
-def compress_packages(names):
- mapping = {}
- for name in names:
- mapping.update(pkg_to_mapping(name))
- return compress_mapping(mapping)
-
-def generate_script(entry, packages):
- data = compress_packages(packages)
- tmpl = py.path.local(__file__).dirpath().join('standalonetemplate.py')
- exe = tmpl.read()
- exe = exe.replace('@SOURCES@', data)
- exe = exe.replace('@ENTRY@', entry)
- return exe
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("debugconfig")
- group.addoption("--genscript", action="store", default=None,
- dest="genscript", metavar="path",
- help="create standalone pytest script at given target path.")
-
-def pytest_cmdline_main(config):
- import _pytest.config
- genscript = config.getvalue("genscript")
- if genscript:
- tw = _pytest.config.create_terminal_writer(config)
- tw.line("WARNING: usage of genscript is deprecated.",
- red=True)
- deps = ['py', '_pytest', 'pytest'] # pluggy is vendored
- if sys.version_info < (2,7):
- deps.append("argparse")
- tw.line("generated script will run on python2.6-python3.3++")
- else:
- tw.line("WARNING: generated script will not run on python2.6 "
- "due to 'argparse' dependency. Use python2.6 "
- "to generate a python2.6 compatible script", red=True)
- script = generate_script(
- 'import pytest; raise SystemExit(pytest.cmdline.main())',
- deps,
- )
- genscript = py.path.local(genscript)
- genscript.write(script)
- tw.line("generated pytest standalone script: %s" % genscript,
- bold=True)
- return 0
-
-
-def pytest_namespace():
- return {'freeze_includes': freeze_includes}
-
-
-def freeze_includes():
- """
- Returns a list of module names used by py.test that should be
- included by cx_freeze.
- """
- result = list(_iter_all_modules(py))
- result += list(_iter_all_modules(_pytest))
- return result
-
-
-def _iter_all_modules(package, prefix=''):
- """
- Iterates over the names of all modules that can be found in the given
- package, recursively.
-
- Example:
- _iter_all_modules(_pytest) ->
- ['_pytest.assertion.newinterpret',
- '_pytest.capture',
- '_pytest.core',
- ...
- ]
- """
- if type(package) is not str:
- path, prefix = package.__path__[0], package.__name__ + '.'
- else:
- path = package
- for _, name, is_package in pkgutil.iter_modules([path]):
- if is_package:
- for m in _iter_all_modules(os.path.join(path, name), prefix=name + '.'):
- yield prefix + m
- else:
- yield prefix + name
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/helpconfig.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/helpconfig.py
deleted file mode 100644
index 1df0c56ac7b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/helpconfig.py
+++ /dev/null
@@ -1,139 +0,0 @@
-""" version info, help messages, tracing configuration. """
-import py
-import pytest
-import os, sys
-
-def pytest_addoption(parser):
- group = parser.getgroup('debugconfig')
- group.addoption('--version', action="store_true",
- help="display pytest lib version and import information.")
- group._addoption("-h", "--help", action="store_true", dest="help",
- help="show help message and configuration info")
- group._addoption('-p', action="append", dest="plugins", default = [],
- metavar="name",
- help="early-load given plugin (multi-allowed). "
- "To avoid loading of plugins, use the `no:` prefix, e.g. "
- "`no:doctest`.")
- group.addoption('--traceconfig', '--trace-config',
- action="store_true", default=False,
- help="trace considerations of conftest.py files."),
- group.addoption('--debug',
- action="store_true", dest="debug", default=False,
- help="store internal tracing debug information in 'pytestdebug.log'.")
-
-
-@pytest.hookimpl(hookwrapper=True)
-def pytest_cmdline_parse():
- outcome = yield
- config = outcome.get_result()
- if config.option.debug:
- path = os.path.abspath("pytestdebug.log")
- debugfile = open(path, 'w')
- debugfile.write("versions pytest-%s, py-%s, "
- "python-%s\ncwd=%s\nargs=%s\n\n" %(
- pytest.__version__, py.__version__,
- ".".join(map(str, sys.version_info)),
- os.getcwd(), config._origargs))
- config.trace.root.setwriter(debugfile.write)
- undo_tracing = config.pluginmanager.enable_tracing()
- sys.stderr.write("writing pytestdebug information to %s\n" % path)
- def unset_tracing():
- debugfile.close()
- sys.stderr.write("wrote pytestdebug information to %s\n" %
- debugfile.name)
- config.trace.root.setwriter(None)
- undo_tracing()
- config.add_cleanup(unset_tracing)
-
-def pytest_cmdline_main(config):
- if config.option.version:
- p = py.path.local(pytest.__file__)
- sys.stderr.write("This is pytest version %s, imported from %s\n" %
- (pytest.__version__, p))
- plugininfo = getpluginversioninfo(config)
- if plugininfo:
- for line in plugininfo:
- sys.stderr.write(line + "\n")
- return 0
- elif config.option.help:
- config._do_configure()
- showhelp(config)
- config._ensure_unconfigure()
- return 0
-
-def showhelp(config):
- reporter = config.pluginmanager.get_plugin('terminalreporter')
- tw = reporter._tw
- tw.write(config._parser.optparser.format_help())
- tw.line()
- tw.line()
- #tw.sep( "=", "config file settings")
- tw.line("[pytest] ini-options in the next "
- "pytest.ini|tox.ini|setup.cfg file:")
- tw.line()
-
- for name in config._parser._ininames:
- help, type, default = config._parser._inidict[name]
- if type is None:
- type = "string"
- spec = "%s (%s)" % (name, type)
- line = " %-24s %s" %(spec, help)
- tw.line(line[:tw.fullwidth])
-
- tw.line()
- tw.line("environment variables:")
- vars = [
- ("PYTEST_ADDOPTS", "extra command line options"),
- ("PYTEST_PLUGINS", "comma-separated plugins to load during startup"),
- ("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals")
- ]
- for name, help in vars:
- tw.line(" %-24s %s" % (name, help))
- tw.line()
- tw.line()
-
- tw.line("to see available markers type: py.test --markers")
- tw.line("to see available fixtures type: py.test --fixtures")
- tw.line("(shown according to specified file_or_dir or current dir "
- "if not specified)")
-
- for warningreport in reporter.stats.get('warnings', []):
- tw.line("warning : " + warningreport.message, red=True)
- return
-
-
-conftest_options = [
- ('pytest_plugins', 'list of plugin names to load'),
-]
-
-def getpluginversioninfo(config):
- lines = []
- plugininfo = config.pluginmanager.list_plugin_distinfo()
- if plugininfo:
- lines.append("setuptools registered plugins:")
- for plugin, dist in plugininfo:
- loc = getattr(plugin, '__file__', repr(plugin))
- content = "%s-%s at %s" % (dist.project_name, dist.version, loc)
- lines.append(" " + content)
- return lines
-
-def pytest_report_header(config):
- lines = []
- if config.option.debug or config.option.traceconfig:
- lines.append("using: pytest-%s pylib-%s" %
- (pytest.__version__,py.__version__))
-
- verinfo = getpluginversioninfo(config)
- if verinfo:
- lines.extend(verinfo)
-
- if config.option.traceconfig:
- lines.append("active plugins:")
- items = config.pluginmanager.list_name_plugin()
- for name, plugin in items:
- if hasattr(plugin, '__file__'):
- r = plugin.__file__
- else:
- r = repr(plugin)
- lines.append(" %-20s: %s" %(name, r))
- return lines
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/hookspec.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/hookspec.py
deleted file mode 100644
index 60e9b47d262..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/hookspec.py
+++ /dev/null
@@ -1,295 +0,0 @@
-""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
-
-from _pytest._pluggy import HookspecMarker
-
-hookspec = HookspecMarker("pytest")
-
-# -------------------------------------------------------------------------
-# Initialization hooks called for every plugin
-# -------------------------------------------------------------------------
-
-@hookspec(historic=True)
-def pytest_addhooks(pluginmanager):
- """called at plugin registration time to allow adding new hooks via a call to
- pluginmanager.add_hookspecs(module_or_class, prefix)."""
-
-
-@hookspec(historic=True)
-def pytest_namespace():
- """return dict of name->object to be made globally available in
- the pytest namespace. This hook is called at plugin registration
- time.
- """
-
-@hookspec(historic=True)
-def pytest_plugin_registered(plugin, manager):
- """ a new pytest plugin got registered. """
-
-
-@hookspec(historic=True)
-def pytest_addoption(parser):
- """register argparse-style options and ini-style config values,
- called once at the beginning of a test run.
-
- .. note::
-
- This function should be implemented only in plugins or ``conftest.py``
- files situated at the tests root directory due to how py.test
- :ref:`discovers plugins during startup <pluginorder>`.
-
- :arg parser: To add command line options, call
- :py:func:`parser.addoption(...) <_pytest.config.Parser.addoption>`.
- To add ini-file values call :py:func:`parser.addini(...)
- <_pytest.config.Parser.addini>`.
-
- Options can later be accessed through the
- :py:class:`config <_pytest.config.Config>` object, respectively:
-
- - :py:func:`config.getoption(name) <_pytest.config.Config.getoption>` to
- retrieve the value of a command line option.
-
- - :py:func:`config.getini(name) <_pytest.config.Config.getini>` to retrieve
- a value read from an ini-style file.
-
- The config object is passed around on many internal objects via the ``.config``
- attribute or can be retrieved as the ``pytestconfig`` fixture or accessed
- via (deprecated) ``pytest.config``.
- """
-
-@hookspec(historic=True)
-def pytest_configure(config):
- """ called after command line options have been parsed
- and all plugins and initial conftest files been loaded.
- This hook is called for every plugin.
- """
-
-# -------------------------------------------------------------------------
-# Bootstrapping hooks called for plugins registered early enough:
-# internal and 3rd party plugins as well as directly
-# discoverable conftest.py local plugins.
-# -------------------------------------------------------------------------
-
-@hookspec(firstresult=True)
-def pytest_cmdline_parse(pluginmanager, args):
- """return initialized config object, parsing the specified args. """
-
-def pytest_cmdline_preparse(config, args):
- """(deprecated) modify command line arguments before option parsing. """
-
-@hookspec(firstresult=True)
-def pytest_cmdline_main(config):
- """ called for performing the main command line action. The default
- implementation will invoke the configure hooks and runtest_mainloop. """
-
-def pytest_load_initial_conftests(early_config, parser, args):
- """ implements the loading of initial conftest files ahead
- of command line option parsing. """
-
-
-# -------------------------------------------------------------------------
-# collection hooks
-# -------------------------------------------------------------------------
-
-@hookspec(firstresult=True)
-def pytest_collection(session):
- """ perform the collection protocol for the given session. """
-
-def pytest_collection_modifyitems(session, config, items):
- """ called after collection has been performed, may filter or re-order
- the items in-place."""
-
-def pytest_collection_finish(session):
- """ called after collection has been performed and modified. """
-
-@hookspec(firstresult=True)
-def pytest_ignore_collect(path, config):
- """ return True to prevent considering this path for collection.
- This hook is consulted for all files and directories prior to calling
- more specific hooks.
- """
-
-@hookspec(firstresult=True)
-def pytest_collect_directory(path, parent):
- """ called before traversing a directory for collection files. """
-
-def pytest_collect_file(path, parent):
- """ return collection Node or None for the given path. Any new node
- needs to have the specified ``parent`` as a parent."""
-
-# logging hooks for collection
-def pytest_collectstart(collector):
- """ collector starts collecting. """
-
-def pytest_itemcollected(item):
- """ we just collected a test item. """
-
-def pytest_collectreport(report):
- """ collector finished collecting. """
-
-def pytest_deselected(items):
- """ called for test items deselected by keyword. """
-
-@hookspec(firstresult=True)
-def pytest_make_collect_report(collector):
- """ perform ``collector.collect()`` and return a CollectReport. """
-
-# -------------------------------------------------------------------------
-# Python test function related hooks
-# -------------------------------------------------------------------------
-
-@hookspec(firstresult=True)
-def pytest_pycollect_makemodule(path, parent):
- """ return a Module collector or None for the given path.
- This hook will be called for each matching test module path.
- The pytest_collect_file hook needs to be used if you want to
- create test modules for files that do not match as a test module.
- """
-
-@hookspec(firstresult=True)
-def pytest_pycollect_makeitem(collector, name, obj):
- """ return custom item/collector for a python object in a module, or None. """
-
-@hookspec(firstresult=True)
-def pytest_pyfunc_call(pyfuncitem):
- """ call underlying test function. """
-
-def pytest_generate_tests(metafunc):
- """ generate (multiple) parametrized calls to a test function."""
-
-# -------------------------------------------------------------------------
-# generic runtest related hooks
-# -------------------------------------------------------------------------
-
-@hookspec(firstresult=True)
-def pytest_runtestloop(session):
- """ called for performing the main runtest loop
- (after collection finished). """
-
-def pytest_itemstart(item, node):
- """ (deprecated, use pytest_runtest_logstart). """
-
-@hookspec(firstresult=True)
-def pytest_runtest_protocol(item, nextitem):
- """ implements the runtest_setup/call/teardown protocol for
- the given test item, including capturing exceptions and calling
- reporting hooks.
-
- :arg item: test item for which the runtest protocol is performed.
-
- :arg nextitem: the scheduled-to-be-next test item (or None if this
- is the end my friend). This argument is passed on to
- :py:func:`pytest_runtest_teardown`.
-
- :return boolean: True if no further hook implementations should be invoked.
- """
-
-def pytest_runtest_logstart(nodeid, location):
- """ signal the start of running a single test item. """
-
-def pytest_runtest_setup(item):
- """ called before ``pytest_runtest_call(item)``. """
-
-def pytest_runtest_call(item):
- """ called to execute the test ``item``. """
-
-def pytest_runtest_teardown(item, nextitem):
- """ called after ``pytest_runtest_call``.
-
- :arg nextitem: the scheduled-to-be-next test item (None if no further
- test item is scheduled). This argument can be used to
- perform exact teardowns, i.e. calling just enough finalizers
- so that nextitem only needs to call setup-functions.
- """
-
-@hookspec(firstresult=True)
-def pytest_runtest_makereport(item, call):
- """ return a :py:class:`_pytest.runner.TestReport` object
- for the given :py:class:`pytest.Item` and
- :py:class:`_pytest.runner.CallInfo`.
- """
-
-def pytest_runtest_logreport(report):
- """ process a test setup/call/teardown report relating to
- the respective phase of executing a test. """
-
-# -------------------------------------------------------------------------
-# test session related hooks
-# -------------------------------------------------------------------------
-
-def pytest_sessionstart(session):
- """ before session.main() is called. """
-
-def pytest_sessionfinish(session, exitstatus):
- """ whole test run finishes. """
-
-def pytest_unconfigure(config):
- """ called before test process is exited. """
-
-
-# -------------------------------------------------------------------------
-# hooks for customising the assert methods
-# -------------------------------------------------------------------------
-
-def pytest_assertrepr_compare(config, op, left, right):
- """return explanation for comparisons in failing assert expressions.
-
- Return None for no custom explanation, otherwise return a list
- of strings. The strings will be joined by newlines but any newlines
- *in* a string will be escaped. Note that all but the first line will
- be indented sligthly, the intention is for the first line to be a summary.
- """
-
-# -------------------------------------------------------------------------
-# hooks for influencing reporting (invoked from _pytest_terminal)
-# -------------------------------------------------------------------------
-
-def pytest_report_header(config, startdir):
- """ return a string to be displayed as header info for terminal reporting."""
-
-@hookspec(firstresult=True)
-def pytest_report_teststatus(report):
- """ return result-category, shortletter and verbose word for reporting."""
-
-def pytest_terminal_summary(terminalreporter):
- """ add additional section in terminal summary reporting. """
-
-
-@hookspec(historic=True)
-def pytest_logwarning(message, code, nodeid, fslocation):
- """ process a warning specified by a message, a code string,
- a nodeid and fslocation (both of which may be None
- if the warning is not tied to a partilar node/location)."""
-
-# -------------------------------------------------------------------------
-# doctest hooks
-# -------------------------------------------------------------------------
-
-@hookspec(firstresult=True)
-def pytest_doctest_prepare_content(content):
- """ return processed content for a given doctest"""
-
-# -------------------------------------------------------------------------
-# error handling and internal debugging hooks
-# -------------------------------------------------------------------------
-
-def pytest_internalerror(excrepr, excinfo):
- """ called for internal errors. """
-
-def pytest_keyboard_interrupt(excinfo):
- """ called for keyboard interrupt. """
-
-def pytest_exception_interact(node, call, report):
- """called when an exception was raised which can potentially be
- interactively handled.
-
- This hook is only called if an exception was raised
- that is not an internal exception like ``skip.Exception``.
- """
-
-def pytest_enter_pdb(config):
- """ called upon pdb.set_trace(), can be used by plugins to take special
- action just before the python debugger enters in interactive mode.
-
- :arg config: pytest config object
- :type config: _pytest.config.Config
- """
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/impl b/tests/wpt/web-platform-tests/tools/pytest/_pytest/impl
deleted file mode 100644
index 889e37e5abc..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/impl
+++ /dev/null
@@ -1,254 +0,0 @@
-Sorting per-resource
------------------------------
-
-for any given set of items:
-
-- collect items per session-scoped parametrized funcarg
-- re-order until items no parametrizations are mixed
-
- examples:
-
- test()
- test1(s1)
- test1(s2)
- test2()
- test3(s1)
- test3(s2)
-
- gets sorted to:
-
- test()
- test2()
- test1(s1)
- test3(s1)
- test1(s2)
- test3(s2)
-
-
-the new @setup functions
---------------------------------------
-
-Consider a given @setup-marked function::
-
- @pytest.mark.setup(maxscope=SCOPE)
- def mysetup(request, arg1, arg2, ...)
- ...
- request.addfinalizer(fin)
- ...
-
-then FUNCARGSET denotes the set of (arg1, arg2, ...) funcargs and
-all of its dependent funcargs. The mysetup function will execute
-for any matching test item once per scope.
-
-The scope is determined as the minimum scope of all scopes of the args
-in FUNCARGSET and the given "maxscope".
-
-If mysetup has been called and no finalizers have been called it is
-called "active".
-
-Furthermore the following rules apply:
-
-- if an arg value in FUNCARGSET is about to be torn down, the
- mysetup-registered finalizers will execute as well.
-
-- There will never be two active mysetup invocations.
-
-Example 1, session scope::
-
- @pytest.mark.funcarg(scope="session", params=[1,2])
- def db(request):
- request.addfinalizer(db_finalize)
-
- @pytest.mark.setup
- def mysetup(request, db):
- request.addfinalizer(mysetup_finalize)
- ...
-
-And a given test module:
-
- def test_something():
- ...
- def test_otherthing():
- pass
-
-Here is what happens::
-
- db(request) executes with request.param == 1
- mysetup(request, db) executes
- test_something() executes
- test_otherthing() executes
- mysetup_finalize() executes
- db_finalize() executes
- db(request) executes with request.param == 2
- mysetup(request, db) executes
- test_something() executes
- test_otherthing() executes
- mysetup_finalize() executes
- db_finalize() executes
-
-Example 2, session/function scope::
-
- @pytest.mark.funcarg(scope="session", params=[1,2])
- def db(request):
- request.addfinalizer(db_finalize)
-
- @pytest.mark.setup(scope="function")
- def mysetup(request, db):
- ...
- request.addfinalizer(mysetup_finalize)
- ...
-
-And a given test module:
-
- def test_something():
- ...
- def test_otherthing():
- pass
-
-Here is what happens::
-
- db(request) executes with request.param == 1
- mysetup(request, db) executes
- test_something() executes
- mysetup_finalize() executes
- mysetup(request, db) executes
- test_otherthing() executes
- mysetup_finalize() executes
- db_finalize() executes
- db(request) executes with request.param == 2
- mysetup(request, db) executes
- test_something() executes
- mysetup_finalize() executes
- mysetup(request, db) executes
- test_otherthing() executes
- mysetup_finalize() executes
- db_finalize() executes
-
-
-Example 3 - funcargs session-mix
-----------------------------------------
-
-Similar with funcargs, an example::
-
- @pytest.mark.funcarg(scope="session", params=[1,2])
- def db(request):
- request.addfinalizer(db_finalize)
-
- @pytest.mark.funcarg(scope="function")
- def table(request, db):
- ...
- request.addfinalizer(table_finalize)
- ...
-
-And a given test module:
-
- def test_something(table):
- ...
- def test_otherthing(table):
- pass
- def test_thirdthing():
- pass
-
-Here is what happens::
-
- db(request) executes with param == 1
- table(request, db)
- test_something(table)
- table_finalize()
- table(request, db)
- test_otherthing(table)
- table_finalize()
- db_finalize
- db(request) executes with param == 2
- table(request, db)
- test_something(table)
- table_finalize()
- table(request, db)
- test_otherthing(table)
- table_finalize()
- db_finalize
- test_thirdthing()
-
-Data structures
---------------------
-
-pytest internally maintains a dict of active funcargs with cache, param,
-finalizer, (scopeitem?) information:
-
- active_funcargs = dict()
-
-if a parametrized "db" is activated:
-
- active_funcargs["db"] = FuncargInfo(dbvalue, paramindex,
- FuncargFinalize(...), scopeitem)
-
-if a test is torn down and the next test requires a differently
-parametrized "db":
-
- for argname in item.callspec.params:
- if argname in active_funcargs:
- funcarginfo = active_funcargs[argname]
- if funcarginfo.param != item.callspec.params[argname]:
- funcarginfo.callfinalizer()
- del node2funcarg[funcarginfo.scopeitem]
- del active_funcargs[argname]
- nodes_to_be_torn_down = ...
- for node in nodes_to_be_torn_down:
- if node in node2funcarg:
- argname = node2funcarg[node]
- active_funcargs[argname].callfinalizer()
- del node2funcarg[node]
- del active_funcargs[argname]
-
-if a test is setup requiring a "db" funcarg:
-
- if "db" in active_funcargs:
- return active_funcargs["db"][0]
- funcarginfo = setup_funcarg()
- active_funcargs["db"] = funcarginfo
- node2funcarg[funcarginfo.scopeitem] = "db"
-
-Implementation plan for resources
-------------------------------------------
-
-1. Revert FuncargRequest to the old form, unmerge item/request
- (done)
-2. make funcarg factories be discovered at collection time
-3. Introduce funcarg marker
-4. Introduce funcarg scope parameter
-5. Introduce funcarg parametrize parameter
-6. make setup functions be discovered at collection time
-7. (Introduce a pytest_fixture_protocol/setup_funcargs hook)
-
-methods and data structures
---------------------------------
-
-A FuncarcManager holds all information about funcarg definitions
-including parametrization and scope definitions. It implements
-a pytest_generate_tests hook which performs parametrization as appropriate.
-
-as a simple example, let's consider a tree where a test function requires
-a "abc" funcarg and its factory defines it as parametrized and scoped
-for Modules. When collections hits the function item, it creates
-the metafunc object, and calls funcargdb.pytest_generate_tests(metafunc)
-which looks up available funcarg factories and their scope and parametrization.
-This information is equivalent to what can be provided today directly
-at the function site and it should thus be relatively straight forward
-to implement the additional way of defining parametrization/scoping.
-
-conftest loading:
- each funcarg-factory will populate the session.funcargmanager
-
-When a test item is collected, it grows a dictionary
-(funcargname2factorycalllist). A factory lookup is performed
-for each required funcarg. The resulting factory call is stored
-with the item. If a function is parametrized multiple items are
-created with respective factory calls. Else if a factory is parametrized
-multiple items and calls to the factory function are created as well.
-
-At setup time, an item populates a funcargs mapping, mapping names
-to values. If a value is funcarg factories are queried for a given item
-test functions and setup functions are put in a class
-which looks up required funcarg factories.
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/junitxml.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/junitxml.py
deleted file mode 100644
index 660d718a6a8..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/junitxml.py
+++ /dev/null
@@ -1,387 +0,0 @@
-"""
- report test results in JUnit-XML format,
- for use with Jenkins and build integration servers.
-
-
-Based on initial code from Ross Lawley.
-"""
-# Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/
-# src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
-
-import py
-import os
-import re
-import sys
-import time
-import pytest
-
-# Python 2.X and 3.X compatibility
-if sys.version_info[0] < 3:
- from codecs import open
-else:
- unichr = chr
- unicode = str
- long = int
-
-
-class Junit(py.xml.Namespace):
- pass
-
-# We need to get the subset of the invalid unicode ranges according to
-# XML 1.0 which are valid in this python build. Hence we calculate
-# this dynamically instead of hardcoding it. The spec range of valid
-# chars is: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
-# | [#x10000-#x10FFFF]
-_legal_chars = (0x09, 0x0A, 0x0d)
-_legal_ranges = (
- (0x20, 0x7E), (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF),
-)
-_legal_xml_re = [
- unicode("%s-%s") % (unichr(low), unichr(high))
- for (low, high) in _legal_ranges if low < sys.maxunicode
-]
-_legal_xml_re = [unichr(x) for x in _legal_chars] + _legal_xml_re
-illegal_xml_re = re.compile(unicode('[^%s]') % unicode('').join(_legal_xml_re))
-del _legal_chars
-del _legal_ranges
-del _legal_xml_re
-
-_py_ext_re = re.compile(r"\.py$")
-
-
-def bin_xml_escape(arg):
- def repl(matchobj):
- i = ord(matchobj.group())
- if i <= 0xFF:
- return unicode('#x%02X') % i
- else:
- return unicode('#x%04X') % i
-
- return py.xml.raw(illegal_xml_re.sub(repl, py.xml.escape(arg)))
-
-
-class _NodeReporter(object):
- def __init__(self, nodeid, xml):
-
- self.id = nodeid
- self.xml = xml
- self.add_stats = self.xml.add_stats
- self.duration = 0
- self.properties = []
- self.nodes = []
- self.testcase = None
- self.attrs = {}
-
- def append(self, node):
- self.xml.add_stats(type(node).__name__)
- self.nodes.append(node)
-
- def add_property(self, name, value):
- self.properties.append((str(name), bin_xml_escape(value)))
-
- def make_properties_node(self):
- """Return a Junit node containing custom properties, if any.
- """
- if self.properties:
- return Junit.properties([
- Junit.property(name=name, value=value)
- for name, value in self.properties
- ])
- return ''
-
- def record_testreport(self, testreport):
- assert not self.testcase
- names = mangle_test_address(testreport.nodeid)
- classnames = names[:-1]
- if self.xml.prefix:
- classnames.insert(0, self.xml.prefix)
- attrs = {
- "classname": ".".join(classnames),
- "name": bin_xml_escape(names[-1]),
- "file": testreport.location[0],
- }
- if testreport.location[1] is not None:
- attrs["line"] = testreport.location[1]
- self.attrs = attrs
-
- def to_xml(self):
- testcase = Junit.testcase(time=self.duration, **self.attrs)
- testcase.append(self.make_properties_node())
- for node in self.nodes:
- testcase.append(node)
- return testcase
-
- def _add_simple(self, kind, message, data=None):
- data = bin_xml_escape(data)
- node = kind(data, message=message)
- self.append(node)
-
- def _write_captured_output(self, report):
- for capname in ('out', 'err'):
- allcontent = ""
- for name, content in report.get_sections("Captured std%s" %
- capname):
- allcontent += content
- if allcontent:
- tag = getattr(Junit, 'system-' + capname)
- self.append(tag(bin_xml_escape(allcontent)))
-
- def append_pass(self, report):
- self.add_stats('passed')
- self._write_captured_output(report)
-
- def append_failure(self, report):
- # msg = str(report.longrepr.reprtraceback.extraline)
- if hasattr(report, "wasxfail"):
- self._add_simple(
- Junit.skipped,
- "xfail-marked test passes unexpectedly")
- else:
- if hasattr(report.longrepr, "reprcrash"):
- message = report.longrepr.reprcrash.message
- elif isinstance(report.longrepr, (unicode, str)):
- message = report.longrepr
- else:
- message = str(report.longrepr)
- message = bin_xml_escape(message)
- fail = Junit.failure(message=message)
- fail.append(bin_xml_escape(report.longrepr))
- self.append(fail)
- self._write_captured_output(report)
-
- def append_collect_error(self, report):
- # msg = str(report.longrepr.reprtraceback.extraline)
- self.append(Junit.error(bin_xml_escape(report.longrepr),
- message="collection failure"))
-
- def append_collect_skipped(self, report):
- self._add_simple(
- Junit.skipped, "collection skipped", report.longrepr)
-
- def append_error(self, report):
- self._add_simple(
- Junit.error, "test setup failure", report.longrepr)
- self._write_captured_output(report)
-
- def append_skipped(self, report):
- if hasattr(report, "wasxfail"):
- self._add_simple(
- Junit.skipped, "expected test failure", report.wasxfail
- )
- else:
- filename, lineno, skipreason = report.longrepr
- if skipreason.startswith("Skipped: "):
- skipreason = bin_xml_escape(skipreason[9:])
- self.append(
- Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason),
- type="pytest.skip",
- message=skipreason))
- self._write_captured_output(report)
-
- def finalize(self):
- data = self.to_xml().unicode(indent=0)
- self.__dict__.clear()
- self.to_xml = lambda: py.xml.raw(data)
-
-
-@pytest.fixture
-def record_xml_property(request):
- """Fixture that adds extra xml properties to the tag for the calling test.
- The fixture is callable with (name, value), with value being automatically
- xml-encoded.
- """
- request.node.warn(
- code='C3',
- message='record_xml_property is an experimental feature',
- )
- xml = getattr(request.config, "_xml", None)
- if xml is not None:
- node_reporter = xml.node_reporter(request.node.nodeid)
- return node_reporter.add_property
- else:
- def add_property_noop(name, value):
- pass
-
- return add_property_noop
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("terminal reporting")
- group.addoption(
- '--junitxml', '--junit-xml',
- action="store",
- dest="xmlpath",
- metavar="path",
- default=None,
- help="create junit-xml style report file at given path.")
- group.addoption(
- '--junitprefix', '--junit-prefix',
- action="store",
- metavar="str",
- default=None,
- help="prepend prefix to classnames in junit-xml output")
-
-
-def pytest_configure(config):
- xmlpath = config.option.xmlpath
- # prevent opening xmllog on slave nodes (xdist)
- if xmlpath and not hasattr(config, 'slaveinput'):
- config._xml = LogXML(xmlpath, config.option.junitprefix)
- config.pluginmanager.register(config._xml)
-
-
-def pytest_unconfigure(config):
- xml = getattr(config, '_xml', None)
- if xml:
- del config._xml
- config.pluginmanager.unregister(xml)
-
-
-def mangle_test_address(address):
- path, possible_open_bracket, params = address.partition('[')
- names = path.split("::")
- try:
- names.remove('()')
- except ValueError:
- pass
- # convert file path to dotted path
- names[0] = names[0].replace("/", '.')
- names[0] = _py_ext_re.sub("", names[0])
- # put any params back
- names[-1] += possible_open_bracket + params
- return names
-
-
-class LogXML(object):
- def __init__(self, logfile, prefix):
- logfile = os.path.expanduser(os.path.expandvars(logfile))
- self.logfile = os.path.normpath(os.path.abspath(logfile))
- self.prefix = prefix
- self.stats = dict.fromkeys([
- 'error',
- 'passed',
- 'failure',
- 'skipped',
- ], 0)
- self.node_reporters = {} # nodeid -> _NodeReporter
- self.node_reporters_ordered = []
-
- def finalize(self, report):
- nodeid = getattr(report, 'nodeid', report)
- # local hack to handle xdist report order
- slavenode = getattr(report, 'node', None)
- reporter = self.node_reporters.pop((nodeid, slavenode))
- if reporter is not None:
- reporter.finalize()
-
- def node_reporter(self, report):
- nodeid = getattr(report, 'nodeid', report)
- # local hack to handle xdist report order
- slavenode = getattr(report, 'node', None)
-
- key = nodeid, slavenode
-
- if key in self.node_reporters:
- # TODO: breasks for --dist=each
- return self.node_reporters[key]
- reporter = _NodeReporter(nodeid, self)
- self.node_reporters[key] = reporter
- self.node_reporters_ordered.append(reporter)
- return reporter
-
- def add_stats(self, key):
- if key in self.stats:
- self.stats[key] += 1
-
- def _opentestcase(self, report):
- reporter = self.node_reporter(report)
- reporter.record_testreport(report)
- return reporter
-
- def pytest_runtest_logreport(self, report):
- """handle a setup/call/teardown report, generating the appropriate
- xml tags as necessary.
-
- note: due to plugins like xdist, this hook may be called in interlaced
- order with reports from other nodes. for example:
-
- usual call order:
- -> setup node1
- -> call node1
- -> teardown node1
- -> setup node2
- -> call node2
- -> teardown node2
-
- possible call order in xdist:
- -> setup node1
- -> call node1
- -> setup node2
- -> call node2
- -> teardown node2
- -> teardown node1
- """
- if report.passed:
- if report.when == "call": # ignore setup/teardown
- reporter = self._opentestcase(report)
- reporter.append_pass(report)
- elif report.failed:
- reporter = self._opentestcase(report)
- if report.when == "call":
- reporter.append_failure(report)
- else:
- reporter.append_error(report)
- elif report.skipped:
- reporter = self._opentestcase(report)
- reporter.append_skipped(report)
- self.update_testcase_duration(report)
- if report.when == "teardown":
- self.finalize(report)
-
- def update_testcase_duration(self, report):
- """accumulates total duration for nodeid from given report and updates
- the Junit.testcase with the new total if already created.
- """
- reporter = self.node_reporter(report)
- reporter.duration += getattr(report, 'duration', 0.0)
-
- def pytest_collectreport(self, report):
- if not report.passed:
- reporter = self._opentestcase(report)
- if report.failed:
- reporter.append_collect_error(report)
- else:
- reporter.append_collect_skipped(report)
-
- def pytest_internalerror(self, excrepr):
- reporter = self.node_reporter('internal')
- reporter.attrs.update(classname="pytest", name='internal')
- reporter._add_simple(Junit.error, 'internal error', excrepr)
-
- def pytest_sessionstart(self):
- self.suite_start_time = time.time()
-
- def pytest_sessionfinish(self):
- dirname = os.path.dirname(os.path.abspath(self.logfile))
- if not os.path.isdir(dirname):
- os.makedirs(dirname)
- logfile = open(self.logfile, 'w', encoding='utf-8')
- suite_stop_time = time.time()
- suite_time_delta = suite_stop_time - self.suite_start_time
-
- numtests = self.stats['passed'] + self.stats['failure']
-
- logfile.write('<?xml version="1.0" encoding="utf-8"?>')
- logfile.write(Junit.testsuite(
- [x.to_xml() for x in self.node_reporters_ordered],
- name="pytest",
- errors=self.stats['error'],
- failures=self.stats['failure'],
- skips=self.stats['skipped'],
- tests=numtests,
- time="%.3f" % suite_time_delta, ).unicode(indent=0))
- logfile.close()
-
- def pytest_terminal_summary(self, terminalreporter):
- terminalreporter.write_sep("-",
- "generated xml file: %s" % (self.logfile))
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/main.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/main.py
deleted file mode 100644
index 8654d7af627..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/main.py
+++ /dev/null
@@ -1,744 +0,0 @@
-""" core implementation of testing process: init, session, runtest loop. """
-import imp
-import os
-import re
-import sys
-
-import _pytest
-import _pytest._code
-import py
-import pytest
-try:
- from collections import MutableMapping as MappingMixin
-except ImportError:
- from UserDict import DictMixin as MappingMixin
-
-from _pytest.runner import collect_one_node
-
-tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
-
-# exitcodes for the command line
-EXIT_OK = 0
-EXIT_TESTSFAILED = 1
-EXIT_INTERRUPTED = 2
-EXIT_INTERNALERROR = 3
-EXIT_USAGEERROR = 4
-EXIT_NOTESTSCOLLECTED = 5
-
-name_re = re.compile("^[a-zA-Z_]\w*$")
-
-def pytest_addoption(parser):
- parser.addini("norecursedirs", "directory patterns to avoid for recursion",
- type="args", default=['.*', 'CVS', '_darcs', '{arch}', '*.egg'])
- parser.addini("testpaths", "directories to search for tests when no files or directories are given in the command line.",
- type="args", default=[])
- #parser.addini("dirpatterns",
- # "patterns specifying possible locations of test files",
- # type="linelist", default=["**/test_*.txt",
- # "**/test_*.py", "**/*_test.py"]
- #)
- group = parser.getgroup("general", "running and selection options")
- group._addoption('-x', '--exitfirst', action="store_true", default=False,
- dest="exitfirst",
- help="exit instantly on first error or failed test."),
- group._addoption('--maxfail', metavar="num",
- action="store", type=int, dest="maxfail", default=0,
- help="exit after first num failures or errors.")
- group._addoption('--strict', action="store_true",
- help="run pytest in strict mode, warnings become errors.")
- group._addoption("-c", metavar="file", type=str, dest="inifilename",
- help="load configuration from `file` instead of trying to locate one of the implicit configuration files.")
-
- group = parser.getgroup("collect", "collection")
- group.addoption('--collectonly', '--collect-only', action="store_true",
- help="only collect tests, don't execute them."),
- group.addoption('--pyargs', action="store_true",
- help="try to interpret all arguments as python packages.")
- group.addoption("--ignore", action="append", metavar="path",
- help="ignore path during collection (multi-allowed).")
- # when changing this to --conf-cut-dir, config.py Conftest.setinitial
- # needs upgrading as well
- group.addoption('--confcutdir', dest="confcutdir", default=None,
- metavar="dir",
- help="only load conftest.py's relative to specified dir.")
- group.addoption('--noconftest', action="store_true",
- dest="noconftest", default=False,
- help="Don't load any conftest.py files.")
-
- group = parser.getgroup("debugconfig",
- "test session debugging and configuration")
- group.addoption('--basetemp', dest="basetemp", default=None, metavar="dir",
- help="base temporary directory for this test run.")
-
-
-def pytest_namespace():
- collect = dict(Item=Item, Collector=Collector, File=File, Session=Session)
- return dict(collect=collect)
-
-def pytest_configure(config):
- pytest.config = config # compatibiltiy
- if config.option.exitfirst:
- config.option.maxfail = 1
-
-def wrap_session(config, doit):
- """Skeleton command line program"""
- session = Session(config)
- session.exitstatus = EXIT_OK
- initstate = 0
- try:
- try:
- config._do_configure()
- initstate = 1
- config.hook.pytest_sessionstart(session=session)
- initstate = 2
- session.exitstatus = doit(config, session) or 0
- except pytest.UsageError:
- raise
- except KeyboardInterrupt:
- excinfo = _pytest._code.ExceptionInfo()
- config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
- session.exitstatus = EXIT_INTERRUPTED
- except:
- excinfo = _pytest._code.ExceptionInfo()
- config.notify_exception(excinfo, config.option)
- session.exitstatus = EXIT_INTERNALERROR
- if excinfo.errisinstance(SystemExit):
- sys.stderr.write("mainloop: caught Spurious SystemExit!\n")
-
- finally:
- excinfo = None # Explicitly break reference cycle.
- session.startdir.chdir()
- if initstate >= 2:
- config.hook.pytest_sessionfinish(
- session=session,
- exitstatus=session.exitstatus)
- config._ensure_unconfigure()
- return session.exitstatus
-
-def pytest_cmdline_main(config):
- return wrap_session(config, _main)
-
-def _main(config, session):
- """ default command line protocol for initialization, session,
- running tests and reporting. """
- config.hook.pytest_collection(session=session)
- config.hook.pytest_runtestloop(session=session)
-
- if session.testsfailed:
- return EXIT_TESTSFAILED
- elif session.testscollected == 0:
- return EXIT_NOTESTSCOLLECTED
-
-def pytest_collection(session):
- return session.perform_collect()
-
-def pytest_runtestloop(session):
- if session.config.option.collectonly:
- return True
-
- def getnextitem(i):
- # this is a function to avoid python2
- # keeping sys.exc_info set when calling into a test
- # python2 keeps sys.exc_info till the frame is left
- try:
- return session.items[i+1]
- except IndexError:
- return None
-
- for i, item in enumerate(session.items):
- nextitem = getnextitem(i)
- item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
- if session.shouldstop:
- raise session.Interrupted(session.shouldstop)
- return True
-
-def pytest_ignore_collect(path, config):
- p = path.dirpath()
- ignore_paths = config._getconftest_pathlist("collect_ignore", path=p)
- ignore_paths = ignore_paths or []
- excludeopt = config.getoption("ignore")
- if excludeopt:
- ignore_paths.extend([py.path.local(x) for x in excludeopt])
- return path in ignore_paths
-
-class FSHookProxy:
- def __init__(self, fspath, pm, remove_mods):
- self.fspath = fspath
- self.pm = pm
- self.remove_mods = remove_mods
-
- def __getattr__(self, name):
- x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods)
- self.__dict__[name] = x
- return x
-
-def compatproperty(name):
- def fget(self):
- # deprecated - use pytest.name
- return getattr(pytest, name)
-
- return property(fget)
-
-class NodeKeywords(MappingMixin):
- def __init__(self, node):
- self.node = node
- self.parent = node.parent
- self._markers = {node.name: True}
-
- def __getitem__(self, key):
- try:
- return self._markers[key]
- except KeyError:
- if self.parent is None:
- raise
- return self.parent.keywords[key]
-
- def __setitem__(self, key, value):
- self._markers[key] = value
-
- def __delitem__(self, key):
- raise ValueError("cannot delete key in keywords dict")
-
- def __iter__(self):
- seen = set(self._markers)
- if self.parent is not None:
- seen.update(self.parent.keywords)
- return iter(seen)
-
- def __len__(self):
- return len(self.__iter__())
-
- def keys(self):
- return list(self)
-
- def __repr__(self):
- return "<NodeKeywords for node %s>" % (self.node, )
-
-
-class Node(object):
- """ base class for Collector and Item the test collection tree.
- Collector subclasses have children, Items are terminal nodes."""
-
- def __init__(self, name, parent=None, config=None, session=None):
- #: a unique name within the scope of the parent node
- self.name = name
-
- #: the parent collector node.
- self.parent = parent
-
- #: the pytest config object
- self.config = config or parent.config
-
- #: the session this node is part of
- self.session = session or parent.session
-
- #: filesystem path where this node was collected from (can be None)
- self.fspath = getattr(parent, 'fspath', None)
-
- #: keywords/markers collected from all scopes
- self.keywords = NodeKeywords(self)
-
- #: allow adding of extra keywords to use for matching
- self.extra_keyword_matches = set()
-
- # used for storing artificial fixturedefs for direct parametrization
- self._name2pseudofixturedef = {}
-
- @property
- def ihook(self):
- """ fspath sensitive hook proxy used to call pytest hooks"""
- return self.session.gethookproxy(self.fspath)
-
- Module = compatproperty("Module")
- Class = compatproperty("Class")
- Instance = compatproperty("Instance")
- Function = compatproperty("Function")
- File = compatproperty("File")
- Item = compatproperty("Item")
-
- def _getcustomclass(self, name):
- cls = getattr(self, name)
- if cls != getattr(pytest, name):
- py.log._apiwarn("2.0", "use of node.%s is deprecated, "
- "use pytest_pycollect_makeitem(...) to create custom "
- "collection nodes" % name)
- return cls
-
- def __repr__(self):
- return "<%s %r>" %(self.__class__.__name__,
- getattr(self, 'name', None))
-
- def warn(self, code, message):
- """ generate a warning with the given code and message for this
- item. """
- assert isinstance(code, str)
- fslocation = getattr(self, "location", None)
- if fslocation is None:
- fslocation = getattr(self, "fspath", None)
- else:
- fslocation = "%s:%s" % fslocation[:2]
-
- self.ihook.pytest_logwarning.call_historic(kwargs=dict(
- code=code, message=message,
- nodeid=self.nodeid, fslocation=fslocation))
-
- # methods for ordering nodes
- @property
- def nodeid(self):
- """ a ::-separated string denoting its collection tree address. """
- try:
- return self._nodeid
- except AttributeError:
- self._nodeid = x = self._makeid()
- return x
-
- def _makeid(self):
- return self.parent.nodeid + "::" + self.name
-
- def __hash__(self):
- return hash(self.nodeid)
-
- def setup(self):
- pass
-
- def teardown(self):
- pass
-
- def _memoizedcall(self, attrname, function):
- exattrname = "_ex_" + attrname
- failure = getattr(self, exattrname, None)
- if failure is not None:
- py.builtin._reraise(failure[0], failure[1], failure[2])
- if hasattr(self, attrname):
- return getattr(self, attrname)
- try:
- res = function()
- except py.builtin._sysex:
- raise
- except:
- failure = sys.exc_info()
- setattr(self, exattrname, failure)
- raise
- setattr(self, attrname, res)
- return res
-
- def listchain(self):
- """ return list of all parent collectors up to self,
- starting from root of collection tree. """
- chain = []
- item = self
- while item is not None:
- chain.append(item)
- item = item.parent
- chain.reverse()
- return chain
-
- def add_marker(self, marker):
- """ dynamically add a marker object to the node.
-
- ``marker`` can be a string or pytest.mark.* instance.
- """
- from _pytest.mark import MarkDecorator
- if isinstance(marker, py.builtin._basestring):
- marker = MarkDecorator(marker)
- elif not isinstance(marker, MarkDecorator):
- raise ValueError("is not a string or pytest.mark.* Marker")
- self.keywords[marker.name] = marker
-
- def get_marker(self, name):
- """ get a marker object from this node or None if
- the node doesn't have a marker with that name. """
- val = self.keywords.get(name, None)
- if val is not None:
- from _pytest.mark import MarkInfo, MarkDecorator
- if isinstance(val, (MarkDecorator, MarkInfo)):
- return val
-
- def listextrakeywords(self):
- """ Return a set of all extra keywords in self and any parents."""
- extra_keywords = set()
- item = self
- for item in self.listchain():
- extra_keywords.update(item.extra_keyword_matches)
- return extra_keywords
-
- def listnames(self):
- return [x.name for x in self.listchain()]
-
- def addfinalizer(self, fin):
- """ register a function to be called when this node is finalized.
-
- This method can only be called when this node is active
- in a setup chain, for example during self.setup().
- """
- self.session._setupstate.addfinalizer(fin, self)
-
- def getparent(self, cls):
- """ get the next parent node (including ourself)
- which is an instance of the given class"""
- current = self
- while current and not isinstance(current, cls):
- current = current.parent
- return current
-
- def _prunetraceback(self, excinfo):
- pass
-
- def _repr_failure_py(self, excinfo, style=None):
- fm = self.session._fixturemanager
- if excinfo.errisinstance(fm.FixtureLookupError):
- return excinfo.value.formatrepr()
- tbfilter = True
- if self.config.option.fulltrace:
- style="long"
- else:
- self._prunetraceback(excinfo)
- tbfilter = False # prunetraceback already does it
- if style == "auto":
- style = "long"
- # XXX should excinfo.getrepr record all data and toterminal() process it?
- if style is None:
- if self.config.option.tbstyle == "short":
- style = "short"
- else:
- style = "long"
-
- return excinfo.getrepr(funcargs=True,
- showlocals=self.config.option.showlocals,
- style=style, tbfilter=tbfilter)
-
- repr_failure = _repr_failure_py
-
-class Collector(Node):
- """ Collector instances create children through collect()
- and thus iteratively build a tree.
- """
-
- class CollectError(Exception):
- """ an error during collection, contains a custom message. """
-
- def collect(self):
- """ returns a list of children (items and collectors)
- for this collection node.
- """
- raise NotImplementedError("abstract")
-
- def repr_failure(self, excinfo):
- """ represent a collection failure. """
- if excinfo.errisinstance(self.CollectError):
- exc = excinfo.value
- return str(exc.args[0])
- return self._repr_failure_py(excinfo, style="short")
-
- def _memocollect(self):
- """ internal helper method to cache results of calling collect(). """
- return self._memoizedcall('_collected', lambda: list(self.collect()))
-
- def _prunetraceback(self, excinfo):
- if hasattr(self, 'fspath'):
- traceback = excinfo.traceback
- ntraceback = traceback.cut(path=self.fspath)
- if ntraceback == traceback:
- ntraceback = ntraceback.cut(excludepath=tracebackcutdir)
- excinfo.traceback = ntraceback.filter()
-
-class FSCollector(Collector):
- def __init__(self, fspath, parent=None, config=None, session=None):
- fspath = py.path.local(fspath) # xxx only for test_resultlog.py?
- name = fspath.basename
- if parent is not None:
- rel = fspath.relto(parent.fspath)
- if rel:
- name = rel
- name = name.replace(os.sep, "/")
- super(FSCollector, self).__init__(name, parent, config, session)
- self.fspath = fspath
-
- def _makeid(self):
- relpath = self.fspath.relto(self.config.rootdir)
- if os.sep != "/":
- relpath = relpath.replace(os.sep, "/")
- return relpath
-
-class File(FSCollector):
- """ base class for collecting tests from a file. """
-
-class Item(Node):
- """ a basic test invocation item. Note that for a single function
- there might be multiple test invocation items.
- """
- nextitem = None
-
- def __init__(self, name, parent=None, config=None, session=None):
- super(Item, self).__init__(name, parent, config, session)
- self._report_sections = []
-
- def add_report_section(self, when, key, content):
- if content:
- self._report_sections.append((when, key, content))
-
- def reportinfo(self):
- return self.fspath, None, ""
-
- @property
- def location(self):
- try:
- return self._location
- except AttributeError:
- location = self.reportinfo()
- # bestrelpath is a quite slow function
- cache = self.config.__dict__.setdefault("_bestrelpathcache", {})
- try:
- fspath = cache[location[0]]
- except KeyError:
- fspath = self.session.fspath.bestrelpath(location[0])
- cache[location[0]] = fspath
- location = (fspath, location[1], str(location[2]))
- self._location = location
- return location
-
-class NoMatch(Exception):
- """ raised if matching cannot locate a matching names. """
-
-class Interrupted(KeyboardInterrupt):
- """ signals an interrupted test run. """
- __module__ = 'builtins' # for py3
-
-class Session(FSCollector):
- Interrupted = Interrupted
-
- def __init__(self, config):
- FSCollector.__init__(self, config.rootdir, parent=None,
- config=config, session=self)
- self._fs2hookproxy = {}
- self.testsfailed = 0
- self.testscollected = 0
- self.shouldstop = False
- self.trace = config.trace.root.get("collection")
- self._norecursepatterns = config.getini("norecursedirs")
- self.startdir = py.path.local()
- self.config.pluginmanager.register(self, name="session")
-
- def _makeid(self):
- return ""
-
- @pytest.hookimpl(tryfirst=True)
- def pytest_collectstart(self):
- if self.shouldstop:
- raise self.Interrupted(self.shouldstop)
-
- @pytest.hookimpl(tryfirst=True)
- def pytest_runtest_logreport(self, report):
- if report.failed and not hasattr(report, 'wasxfail'):
- self.testsfailed += 1
- maxfail = self.config.getvalue("maxfail")
- if maxfail and self.testsfailed >= maxfail:
- self.shouldstop = "stopping after %d failures" % (
- self.testsfailed)
- pytest_collectreport = pytest_runtest_logreport
-
- def isinitpath(self, path):
- return path in self._initialpaths
-
- def gethookproxy(self, fspath):
- try:
- return self._fs2hookproxy[fspath]
- except KeyError:
- # check if we have the common case of running
- # hooks with all conftest.py filesall conftest.py
- pm = self.config.pluginmanager
- my_conftestmodules = pm._getconftestmodules(fspath)
- remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
- if remove_mods:
- # one or more conftests are not in use at this fspath
- proxy = FSHookProxy(fspath, pm, remove_mods)
- else:
- # all plugis are active for this fspath
- proxy = self.config.hook
-
- self._fs2hookproxy[fspath] = proxy
- return proxy
-
- def perform_collect(self, args=None, genitems=True):
- hook = self.config.hook
- try:
- items = self._perform_collect(args, genitems)
- hook.pytest_collection_modifyitems(session=self,
- config=self.config, items=items)
- finally:
- hook.pytest_collection_finish(session=self)
- self.testscollected = len(items)
- return items
-
- def _perform_collect(self, args, genitems):
- if args is None:
- args = self.config.args
- self.trace("perform_collect", self, args)
- self.trace.root.indent += 1
- self._notfound = []
- self._initialpaths = set()
- self._initialparts = []
- self.items = items = []
- for arg in args:
- parts = self._parsearg(arg)
- self._initialparts.append(parts)
- self._initialpaths.add(parts[0])
- rep = collect_one_node(self)
- self.ihook.pytest_collectreport(report=rep)
- self.trace.root.indent -= 1
- if self._notfound:
- errors = []
- for arg, exc in self._notfound:
- line = "(no name %r in any of %r)" % (arg, exc.args[0])
- errors.append("not found: %s\n%s" % (arg, line))
- #XXX: test this
- raise pytest.UsageError(*errors)
- if not genitems:
- return rep.result
- else:
- if rep.passed:
- for node in rep.result:
- self.items.extend(self.genitems(node))
- return items
-
- def collect(self):
- for parts in self._initialparts:
- arg = "::".join(map(str, parts))
- self.trace("processing argument", arg)
- self.trace.root.indent += 1
- try:
- for x in self._collect(arg):
- yield x
- except NoMatch:
- # we are inside a make_report hook so
- # we cannot directly pass through the exception
- self._notfound.append((arg, sys.exc_info()[1]))
-
- self.trace.root.indent -= 1
-
- def _collect(self, arg):
- names = self._parsearg(arg)
- path = names.pop(0)
- if path.check(dir=1):
- assert not names, "invalid arg %r" %(arg,)
- for path in path.visit(fil=lambda x: x.check(file=1),
- rec=self._recurse, bf=True, sort=True):
- for x in self._collectfile(path):
- yield x
- else:
- assert path.check(file=1)
- for x in self.matchnodes(self._collectfile(path), names):
- yield x
-
- def _collectfile(self, path):
- ihook = self.gethookproxy(path)
- if not self.isinitpath(path):
- if ihook.pytest_ignore_collect(path=path, config=self.config):
- return ()
- return ihook.pytest_collect_file(path=path, parent=self)
-
- def _recurse(self, path):
- ihook = self.gethookproxy(path.dirpath())
- if ihook.pytest_ignore_collect(path=path, config=self.config):
- return
- for pat in self._norecursepatterns:
- if path.check(fnmatch=pat):
- return False
- ihook = self.gethookproxy(path)
- ihook.pytest_collect_directory(path=path, parent=self)
- return True
-
- def _tryconvertpyarg(self, x):
- mod = None
- path = [os.path.abspath('.')] + sys.path
- for name in x.split('.'):
- # ignore anything that's not a proper name here
- # else something like --pyargs will mess up '.'
- # since imp.find_module will actually sometimes work for it
- # but it's supposed to be considered a filesystem path
- # not a package
- if name_re.match(name) is None:
- return x
- try:
- fd, mod, type_ = imp.find_module(name, path)
- except ImportError:
- return x
- else:
- if fd is not None:
- fd.close()
-
- if type_[2] != imp.PKG_DIRECTORY:
- path = [os.path.dirname(mod)]
- else:
- path = [mod]
- return mod
-
- def _parsearg(self, arg):
- """ return (fspath, names) tuple after checking the file exists. """
- arg = str(arg)
- if self.config.option.pyargs:
- arg = self._tryconvertpyarg(arg)
- parts = str(arg).split("::")
- relpath = parts[0].replace("/", os.sep)
- path = self.config.invocation_dir.join(relpath, abs=True)
- if not path.check():
- if self.config.option.pyargs:
- msg = "file or package not found: "
- else:
- msg = "file not found: "
- raise pytest.UsageError(msg + arg)
- parts[0] = path
- return parts
-
- def matchnodes(self, matching, names):
- self.trace("matchnodes", matching, names)
- self.trace.root.indent += 1
- nodes = self._matchnodes(matching, names)
- num = len(nodes)
- self.trace("matchnodes finished -> ", num, "nodes")
- self.trace.root.indent -= 1
- if num == 0:
- raise NoMatch(matching, names[:1])
- return nodes
-
- def _matchnodes(self, matching, names):
- if not matching or not names:
- return matching
- name = names[0]
- assert name
- nextnames = names[1:]
- resultnodes = []
- for node in matching:
- if isinstance(node, pytest.Item):
- if not names:
- resultnodes.append(node)
- continue
- assert isinstance(node, pytest.Collector)
- rep = collect_one_node(node)
- if rep.passed:
- has_matched = False
- for x in rep.result:
- # TODO: remove parametrized workaround once collection structure contains parametrization
- if x.name == name or x.name.split("[")[0] == name:
- resultnodes.extend(self.matchnodes([x], nextnames))
- has_matched = True
- # XXX accept IDs that don't have "()" for class instances
- if not has_matched and len(rep.result) == 1 and x.name == "()":
- nextnames.insert(0, name)
- resultnodes.extend(self.matchnodes([x], nextnames))
- node.ihook.pytest_collectreport(report=rep)
- return resultnodes
-
- def genitems(self, node):
- self.trace("genitems", node)
- if isinstance(node, pytest.Item):
- node.ihook.pytest_itemcollected(item=node)
- yield node
- else:
- assert isinstance(node, pytest.Collector)
- rep = collect_one_node(node)
- if rep.passed:
- for subnode in rep.result:
- for x in self.genitems(subnode):
- yield x
- node.ihook.pytest_collectreport(report=rep)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/mark.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/mark.py
deleted file mode 100644
index 1a763540240..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/mark.py
+++ /dev/null
@@ -1,311 +0,0 @@
-""" generic mechanism for marking and selecting python functions. """
-import inspect
-
-
-class MarkerError(Exception):
-
- """Error in use of a pytest marker/attribute."""
-
-
-def pytest_namespace():
- return {'mark': MarkGenerator()}
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("general")
- group._addoption(
- '-k',
- action="store", dest="keyword", default='', metavar="EXPRESSION",
- help="only run tests which match the given substring expression. "
- "An expression is a python evaluatable expression "
- "where all names are substring-matched against test names "
- "and their parent classes. Example: -k 'test_method or test "
- "other' matches all test functions and classes whose name "
- "contains 'test_method' or 'test_other'. "
- "Additionally keywords are matched to classes and functions "
- "containing extra names in their 'extra_keyword_matches' set, "
- "as well as functions which have names assigned directly to them."
- )
-
- group._addoption(
- "-m",
- action="store", dest="markexpr", default="", metavar="MARKEXPR",
- help="only run tests matching given mark expression. "
- "example: -m 'mark1 and not mark2'."
- )
-
- group.addoption(
- "--markers", action="store_true",
- help="show markers (builtin, plugin and per-project ones)."
- )
-
- parser.addini("markers", "markers for test functions", 'linelist')
-
-
-def pytest_cmdline_main(config):
- import _pytest.config
- if config.option.markers:
- config._do_configure()
- tw = _pytest.config.create_terminal_writer(config)
- for line in config.getini("markers"):
- name, rest = line.split(":", 1)
- tw.write("@pytest.mark.%s:" % name, bold=True)
- tw.line(rest)
- tw.line()
- config._ensure_unconfigure()
- return 0
-pytest_cmdline_main.tryfirst = True
-
-
-def pytest_collection_modifyitems(items, config):
- keywordexpr = config.option.keyword
- matchexpr = config.option.markexpr
- if not keywordexpr and not matchexpr:
- return
- # pytest used to allow "-" for negating
- # but today we just allow "-" at the beginning, use "not" instead
- # we probably remove "-" alltogether soon
- if keywordexpr.startswith("-"):
- keywordexpr = "not " + keywordexpr[1:]
- selectuntil = False
- if keywordexpr[-1:] == ":":
- selectuntil = True
- keywordexpr = keywordexpr[:-1]
-
- remaining = []
- deselected = []
- for colitem in items:
- if keywordexpr and not matchkeyword(colitem, keywordexpr):
- deselected.append(colitem)
- else:
- if selectuntil:
- keywordexpr = None
- if matchexpr:
- if not matchmark(colitem, matchexpr):
- deselected.append(colitem)
- continue
- remaining.append(colitem)
-
- if deselected:
- config.hook.pytest_deselected(items=deselected)
- items[:] = remaining
-
-
-class MarkMapping:
- """Provides a local mapping for markers where item access
- resolves to True if the marker is present. """
- def __init__(self, keywords):
- mymarks = set()
- for key, value in keywords.items():
- if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator):
- mymarks.add(key)
- self._mymarks = mymarks
-
- def __getitem__(self, name):
- return name in self._mymarks
-
-
-class KeywordMapping:
- """Provides a local mapping for keywords.
- Given a list of names, map any substring of one of these names to True.
- """
- def __init__(self, names):
- self._names = names
-
- def __getitem__(self, subname):
- for name in self._names:
- if subname in name:
- return True
- return False
-
-
-def matchmark(colitem, markexpr):
- """Tries to match on any marker names, attached to the given colitem."""
- return eval(markexpr, {}, MarkMapping(colitem.keywords))
-
-
-def matchkeyword(colitem, keywordexpr):
- """Tries to match given keyword expression to given collector item.
-
- Will match on the name of colitem, including the names of its parents.
- Only matches names of items which are either a :class:`Class` or a
- :class:`Function`.
- Additionally, matches on names in the 'extra_keyword_matches' set of
- any item, as well as names directly assigned to test functions.
- """
- mapped_names = set()
-
- # Add the names of the current item and any parent items
- import pytest
- for item in colitem.listchain():
- if not isinstance(item, pytest.Instance):
- mapped_names.add(item.name)
-
- # Add the names added as extra keywords to current or parent items
- for name in colitem.listextrakeywords():
- mapped_names.add(name)
-
- # Add the names attached to the current function through direct assignment
- if hasattr(colitem, 'function'):
- for name in colitem.function.__dict__:
- mapped_names.add(name)
-
- mapping = KeywordMapping(mapped_names)
- if " " not in keywordexpr:
- # special case to allow for simple "-k pass" and "-k 1.3"
- return mapping[keywordexpr]
- elif keywordexpr.startswith("not ") and " " not in keywordexpr[4:]:
- return not mapping[keywordexpr[4:]]
- return eval(keywordexpr, {}, mapping)
-
-
-def pytest_configure(config):
- import pytest
- if config.option.strict:
- pytest.mark._config = config
-
-
-class MarkGenerator:
- """ Factory for :class:`MarkDecorator` objects - exposed as
- a ``pytest.mark`` singleton instance. Example::
-
- import pytest
- @pytest.mark.slowtest
- def test_function():
- pass
-
- will set a 'slowtest' :class:`MarkInfo` object
- on the ``test_function`` object. """
-
- def __getattr__(self, name):
- if name[0] == "_":
- raise AttributeError("Marker name must NOT start with underscore")
- if hasattr(self, '_config'):
- self._check(name)
- return MarkDecorator(name)
-
- def _check(self, name):
- try:
- if name in self._markers:
- return
- except AttributeError:
- pass
- self._markers = l = set()
- for line in self._config.getini("markers"):
- beginning = line.split(":", 1)
- x = beginning[0].split("(", 1)[0]
- l.add(x)
- if name not in self._markers:
- raise AttributeError("%r not a registered marker" % (name,))
-
-def istestfunc(func):
- return hasattr(func, "__call__") and \
- getattr(func, "__name__", "<lambda>") != "<lambda>"
-
-class MarkDecorator:
- """ A decorator for test functions and test classes. When applied
- it will create :class:`MarkInfo` objects which may be
- :ref:`retrieved by hooks as item keywords <excontrolskip>`.
- MarkDecorator instances are often created like this::
-
- mark1 = pytest.mark.NAME # simple MarkDecorator
- mark2 = pytest.mark.NAME(name1=value) # parametrized MarkDecorator
-
- and can then be applied as decorators to test functions::
-
- @mark2
- def test_function():
- pass
-
- When a MarkDecorator instance is called it does the following:
- 1. If called with a single class as its only positional argument and no
- additional keyword arguments, it attaches itself to the class so it
- gets applied automatically to all test cases found in that class.
- 2. If called with a single function as its only positional argument and
- no additional keyword arguments, it attaches a MarkInfo object to the
- function, containing all the arguments already stored internally in
- the MarkDecorator.
- 3. When called in any other case, it performs a 'fake construction' call,
- i.e. it returns a new MarkDecorator instance with the original
- MarkDecorator's content updated with the arguments passed to this
- call.
-
- Note: The rules above prevent MarkDecorator objects from storing only a
- single function or class reference as their positional argument with no
- additional keyword or positional arguments.
-
- """
- def __init__(self, name, args=None, kwargs=None):
- self.name = name
- self.args = args or ()
- self.kwargs = kwargs or {}
-
- @property
- def markname(self):
- return self.name # for backward-compat (2.4.1 had this attr)
-
- def __repr__(self):
- d = self.__dict__.copy()
- name = d.pop('name')
- return "<MarkDecorator %r %r>" % (name, d)
-
- def __call__(self, *args, **kwargs):
- """ if passed a single callable argument: decorate it with mark info.
- otherwise add *args/**kwargs in-place to mark information. """
- if args and not kwargs:
- func = args[0]
- is_class = inspect.isclass(func)
- if len(args) == 1 and (istestfunc(func) or is_class):
- if is_class:
- if hasattr(func, 'pytestmark'):
- mark_list = func.pytestmark
- if not isinstance(mark_list, list):
- mark_list = [mark_list]
- # always work on a copy to avoid updating pytestmark
- # from a superclass by accident
- mark_list = mark_list + [self]
- func.pytestmark = mark_list
- else:
- func.pytestmark = [self]
- else:
- holder = getattr(func, self.name, None)
- if holder is None:
- holder = MarkInfo(
- self.name, self.args, self.kwargs
- )
- setattr(func, self.name, holder)
- else:
- holder.add(self.args, self.kwargs)
- return func
- kw = self.kwargs.copy()
- kw.update(kwargs)
- args = self.args + args
- return self.__class__(self.name, args=args, kwargs=kw)
-
-
-class MarkInfo:
- """ Marking object created by :class:`MarkDecorator` instances. """
- def __init__(self, name, args, kwargs):
- #: name of attribute
- self.name = name
- #: positional argument list, empty if none specified
- self.args = args
- #: keyword argument dictionary, empty if nothing specified
- self.kwargs = kwargs.copy()
- self._arglist = [(args, kwargs.copy())]
-
- def __repr__(self):
- return "<MarkInfo %r args=%r kwargs=%r>" % (
- self.name, self.args, self.kwargs
- )
-
- def add(self, args, kwargs):
- """ add a MarkInfo with the given args and kwargs. """
- self._arglist.append((args, kwargs))
- self.args += args
- self.kwargs.update(kwargs)
-
- def __iter__(self):
- """ yield MarkInfo objects each relating to a marking-call. """
- for args, kwargs in self._arglist:
- yield MarkInfo(self.name, args, kwargs)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/monkeypatch.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/monkeypatch.py
deleted file mode 100644
index d4c169d37a9..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/monkeypatch.py
+++ /dev/null
@@ -1,254 +0,0 @@
-""" monkeypatching and mocking functionality. """
-
-import os, sys
-import re
-
-from py.builtin import _basestring
-
-RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
-
-
-def pytest_funcarg__monkeypatch(request):
- """The returned ``monkeypatch`` funcarg provides these
- helper methods to modify objects, dictionaries or os.environ::
-
- monkeypatch.setattr(obj, name, value, raising=True)
- monkeypatch.delattr(obj, name, raising=True)
- monkeypatch.setitem(mapping, name, value)
- monkeypatch.delitem(obj, name, raising=True)
- monkeypatch.setenv(name, value, prepend=False)
- monkeypatch.delenv(name, value, raising=True)
- monkeypatch.syspath_prepend(path)
- monkeypatch.chdir(path)
-
- All modifications will be undone after the requesting
- test function has finished. The ``raising``
- parameter determines if a KeyError or AttributeError
- will be raised if the set/deletion operation has no target.
- """
- mpatch = monkeypatch()
- request.addfinalizer(mpatch.undo)
- return mpatch
-
-
-def resolve(name):
- # simplified from zope.dottedname
- parts = name.split('.')
-
- used = parts.pop(0)
- found = __import__(used)
- for part in parts:
- used += '.' + part
- try:
- found = getattr(found, part)
- except AttributeError:
- pass
- else:
- continue
- # we use explicit un-nesting of the handling block in order
- # to avoid nested exceptions on python 3
- try:
- __import__(used)
- except ImportError as ex:
- # str is used for py2 vs py3
- expected = str(ex).split()[-1]
- if expected == used:
- raise
- else:
- raise ImportError(
- 'import error in %s: %s' % (used, ex)
- )
- found = annotated_getattr(found, part, used)
- return found
-
-
-def annotated_getattr(obj, name, ann):
- try:
- obj = getattr(obj, name)
- except AttributeError:
- raise AttributeError(
- '%r object at %s has no attribute %r' % (
- type(obj).__name__, ann, name
- )
- )
- return obj
-
-
-def derive_importpath(import_path, raising):
- if not isinstance(import_path, _basestring) or "." not in import_path:
- raise TypeError("must be absolute import path string, not %r" %
- (import_path,))
- module, attr = import_path.rsplit('.', 1)
- target = resolve(module)
- if raising:
- annotated_getattr(target, attr, ann=module)
- return attr, target
-
-
-class Notset:
- def __repr__(self):
- return "<notset>"
-
-
-notset = Notset()
-
-
-class monkeypatch:
- """ Object keeping a record of setattr/item/env/syspath changes. """
-
- def __init__(self):
- self._setattr = []
- self._setitem = []
- self._cwd = None
- self._savesyspath = None
-
- def setattr(self, target, name, value=notset, raising=True):
- """ Set attribute value on target, memorizing the old value.
- By default raise AttributeError if the attribute did not exist.
-
- For convenience you can specify a string as ``target`` which
- will be interpreted as a dotted import path, with the last part
- being the attribute name. Example:
- ``monkeypatch.setattr("os.getcwd", lambda x: "/")``
- would set the ``getcwd`` function of the ``os`` module.
-
- The ``raising`` value determines if the setattr should fail
- if the attribute is not already present (defaults to True
- which means it will raise).
- """
- __tracebackhide__ = True
- import inspect
-
- if value is notset:
- if not isinstance(target, _basestring):
- raise TypeError("use setattr(target, name, value) or "
- "setattr(target, value) with target being a dotted "
- "import string")
- value = name
- name, target = derive_importpath(target, raising)
-
- oldval = getattr(target, name, notset)
- if raising and oldval is notset:
- raise AttributeError("%r has no attribute %r" % (target, name))
-
- # avoid class descriptors like staticmethod/classmethod
- if inspect.isclass(target):
- oldval = target.__dict__.get(name, notset)
- self._setattr.append((target, name, oldval))
- setattr(target, name, value)
-
- def delattr(self, target, name=notset, raising=True):
- """ Delete attribute ``name`` from ``target``, by default raise
- AttributeError it the attribute did not previously exist.
-
- If no ``name`` is specified and ``target`` is a string
- it will be interpreted as a dotted import path with the
- last part being the attribute name.
-
- If ``raising`` is set to False, no exception will be raised if the
- attribute is missing.
- """
- __tracebackhide__ = True
- if name is notset:
- if not isinstance(target, _basestring):
- raise TypeError("use delattr(target, name) or "
- "delattr(target) with target being a dotted "
- "import string")
- name, target = derive_importpath(target, raising)
-
- if not hasattr(target, name):
- if raising:
- raise AttributeError(name)
- else:
- self._setattr.append((target, name, getattr(target, name, notset)))
- delattr(target, name)
-
- def setitem(self, dic, name, value):
- """ Set dictionary entry ``name`` to value. """
- self._setitem.append((dic, name, dic.get(name, notset)))
- dic[name] = value
-
- def delitem(self, dic, name, raising=True):
- """ Delete ``name`` from dict. Raise KeyError if it doesn't exist.
-
- If ``raising`` is set to False, no exception will be raised if the
- key is missing.
- """
- if name not in dic:
- if raising:
- raise KeyError(name)
- else:
- self._setitem.append((dic, name, dic.get(name, notset)))
- del dic[name]
-
- def setenv(self, name, value, prepend=None):
- """ Set environment variable ``name`` to ``value``. If ``prepend``
- is a character, read the current environment variable value
- and prepend the ``value`` adjoined with the ``prepend`` character."""
- value = str(value)
- if prepend and name in os.environ:
- value = value + prepend + os.environ[name]
- self.setitem(os.environ, name, value)
-
- def delenv(self, name, raising=True):
- """ Delete ``name`` from the environment. Raise KeyError it does not
- exist.
-
- If ``raising`` is set to False, no exception will be raised if the
- environment variable is missing.
- """
- self.delitem(os.environ, name, raising=raising)
-
- def syspath_prepend(self, path):
- """ Prepend ``path`` to ``sys.path`` list of import locations. """
- if self._savesyspath is None:
- self._savesyspath = sys.path[:]
- sys.path.insert(0, str(path))
-
- def chdir(self, path):
- """ Change the current working directory to the specified path.
- Path can be a string or a py.path.local object.
- """
- if self._cwd is None:
- self._cwd = os.getcwd()
- if hasattr(path, "chdir"):
- path.chdir()
- else:
- os.chdir(path)
-
- def undo(self):
- """ Undo previous changes. This call consumes the
- undo stack. Calling it a second time has no effect unless
- you do more monkeypatching after the undo call.
-
- There is generally no need to call `undo()`, since it is
- called automatically during tear-down.
-
- Note that the same `monkeypatch` fixture is used across a
- single test function invocation. If `monkeypatch` is used both by
- the test function itself and one of the test fixtures,
- calling `undo()` will undo all of the changes made in
- both functions.
- """
- for obj, name, value in reversed(self._setattr):
- if value is not notset:
- setattr(obj, name, value)
- else:
- delattr(obj, name)
- self._setattr[:] = []
- for dictionary, name, value in reversed(self._setitem):
- if value is notset:
- try:
- del dictionary[name]
- except KeyError:
- pass # was already deleted, so we have the desired state
- else:
- dictionary[name] = value
- self._setitem[:] = []
- if self._savesyspath is not None:
- sys.path[:] = self._savesyspath
- self._savesyspath = None
-
- if self._cwd is not None:
- os.chdir(self._cwd)
- self._cwd = None
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/nose.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/nose.py
deleted file mode 100644
index 03874686860..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/nose.py
+++ /dev/null
@@ -1,71 +0,0 @@
-""" run test suites written for nose. """
-
-import sys
-
-import py
-import pytest
-from _pytest import unittest
-
-
-def get_skip_exceptions():
- skip_classes = set()
- for module_name in ('unittest', 'unittest2', 'nose'):
- mod = sys.modules.get(module_name)
- if hasattr(mod, 'SkipTest'):
- skip_classes.add(mod.SkipTest)
- return tuple(skip_classes)
-
-
-def pytest_runtest_makereport(item, call):
- if call.excinfo and call.excinfo.errisinstance(get_skip_exceptions()):
- # let's substitute the excinfo with a pytest.skip one
- call2 = call.__class__(lambda:
- pytest.skip(str(call.excinfo.value)), call.when)
- call.excinfo = call2.excinfo
-
-
-@pytest.hookimpl(trylast=True)
-def pytest_runtest_setup(item):
- if is_potential_nosetest(item):
- if isinstance(item.parent, pytest.Generator):
- gen = item.parent
- if not hasattr(gen, '_nosegensetup'):
- call_optional(gen.obj, 'setup')
- if isinstance(gen.parent, pytest.Instance):
- call_optional(gen.parent.obj, 'setup')
- gen._nosegensetup = True
- if not call_optional(item.obj, 'setup'):
- # call module level setup if there is no object level one
- call_optional(item.parent.obj, 'setup')
- #XXX this implies we only call teardown when setup worked
- item.session._setupstate.addfinalizer((lambda: teardown_nose(item)), item)
-
-def teardown_nose(item):
- if is_potential_nosetest(item):
- if not call_optional(item.obj, 'teardown'):
- call_optional(item.parent.obj, 'teardown')
- #if hasattr(item.parent, '_nosegensetup'):
- # #call_optional(item._nosegensetup, 'teardown')
- # del item.parent._nosegensetup
-
-
-def pytest_make_collect_report(collector):
- if isinstance(collector, pytest.Generator):
- call_optional(collector.obj, 'setup')
-
-
-def is_potential_nosetest(item):
- # extra check needed since we do not do nose style setup/teardown
- # on direct unittest style classes
- return isinstance(item, pytest.Function) and \
- not isinstance(item, unittest.TestCaseFunction)
-
-
-def call_optional(obj, name):
- method = getattr(obj, name, None)
- isfixture = hasattr(method, "_pytestfixturefunction")
- if method is not None and not isfixture and py.builtin.callable(method):
- # If there's any problems allow the exception to raise rather than
- # silently ignoring them
- method()
- return True
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/pastebin.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/pastebin.py
deleted file mode 100644
index 4ec62d02280..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/pastebin.py
+++ /dev/null
@@ -1,92 +0,0 @@
-""" submit failure or test session information to a pastebin service. """
-import pytest
-import sys
-import tempfile
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("terminal reporting")
- group._addoption('--pastebin', metavar="mode",
- action='store', dest="pastebin", default=None,
- choices=['failed', 'all'],
- help="send failed|all info to bpaste.net pastebin service.")
-
-@pytest.hookimpl(trylast=True)
-def pytest_configure(config):
- import py
- if config.option.pastebin == "all":
- tr = config.pluginmanager.getplugin('terminalreporter')
- # if no terminal reporter plugin is present, nothing we can do here;
- # this can happen when this function executes in a slave node
- # when using pytest-xdist, for example
- if tr is not None:
- # pastebin file will be utf-8 encoded binary file
- config._pastebinfile = tempfile.TemporaryFile('w+b')
- oldwrite = tr._tw.write
- def tee_write(s, **kwargs):
- oldwrite(s, **kwargs)
- if py.builtin._istext(s):
- s = s.encode('utf-8')
- config._pastebinfile.write(s)
- tr._tw.write = tee_write
-
-def pytest_unconfigure(config):
- if hasattr(config, '_pastebinfile'):
- # get terminal contents and delete file
- config._pastebinfile.seek(0)
- sessionlog = config._pastebinfile.read()
- config._pastebinfile.close()
- del config._pastebinfile
- # undo our patching in the terminal reporter
- tr = config.pluginmanager.getplugin('terminalreporter')
- del tr._tw.__dict__['write']
- # write summary
- tr.write_sep("=", "Sending information to Paste Service")
- pastebinurl = create_new_paste(sessionlog)
- tr.write_line("pastebin session-log: %s\n" % pastebinurl)
-
-def create_new_paste(contents):
- """
- Creates a new paste using bpaste.net service.
-
- :contents: paste contents as utf-8 encoded bytes
- :returns: url to the pasted contents
- """
- import re
- if sys.version_info < (3, 0):
- from urllib import urlopen, urlencode
- else:
- from urllib.request import urlopen
- from urllib.parse import urlencode
-
- params = {
- 'code': contents,
- 'lexer': 'python3' if sys.version_info[0] == 3 else 'python',
- 'expiry': '1week',
- }
- url = 'https://bpaste.net'
- response = urlopen(url, data=urlencode(params).encode('ascii')).read()
- m = re.search(r'href="/raw/(\w+)"', response.decode('utf-8'))
- if m:
- return '%s/show/%s' % (url, m.group(1))
- else:
- return 'bad response: ' + response
-
-def pytest_terminal_summary(terminalreporter):
- import _pytest.config
- if terminalreporter.config.option.pastebin != "failed":
- return
- tr = terminalreporter
- if 'failed' in tr.stats:
- terminalreporter.write_sep("=", "Sending information to Paste Service")
- for rep in terminalreporter.stats.get('failed'):
- try:
- msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc
- except AttributeError:
- msg = tr._getfailureheadline(rep)
- tw = _pytest.config.create_terminal_writer(terminalreporter.config, stringio=True)
- rep.toterminal(tw)
- s = tw.stringio.getvalue()
- assert len(s)
- pastebinurl = create_new_paste(s)
- tr.write_line("%s --> %s" %(msg, pastebinurl))
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/pdb.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/pdb.py
deleted file mode 100644
index 84c920d172c..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/pdb.py
+++ /dev/null
@@ -1,109 +0,0 @@
-""" interactive debugging with PDB, the Python Debugger. """
-from __future__ import absolute_import
-import pdb
-import sys
-
-import pytest
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("general")
- group._addoption('--pdb',
- action="store_true", dest="usepdb", default=False,
- help="start the interactive Python debugger on errors.")
-
-def pytest_namespace():
- return {'set_trace': pytestPDB().set_trace}
-
-def pytest_configure(config):
- if config.getvalue("usepdb"):
- config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
-
- old = (pdb.set_trace, pytestPDB._pluginmanager)
- def fin():
- pdb.set_trace, pytestPDB._pluginmanager = old
- pytestPDB._config = None
- pdb.set_trace = pytest.set_trace
- pytestPDB._pluginmanager = config.pluginmanager
- pytestPDB._config = config
- config._cleanup.append(fin)
-
-class pytestPDB:
- """ Pseudo PDB that defers to the real pdb. """
- _pluginmanager = None
- _config = None
-
- def set_trace(self):
- """ invoke PDB set_trace debugging, dropping any IO capturing. """
- import _pytest.config
- frame = sys._getframe().f_back
- if self._pluginmanager is not None:
- capman = self._pluginmanager.getplugin("capturemanager")
- if capman:
- capman.suspendcapture(in_=True)
- tw = _pytest.config.create_terminal_writer(self._config)
- tw.line()
- tw.sep(">", "PDB set_trace (IO-capturing turned off)")
- self._pluginmanager.hook.pytest_enter_pdb(config=self._config)
- pdb.Pdb().set_trace(frame)
-
-
-class PdbInvoke:
- def pytest_exception_interact(self, node, call, report):
- capman = node.config.pluginmanager.getplugin("capturemanager")
- if capman:
- out, err = capman.suspendcapture(in_=True)
- sys.stdout.write(out)
- sys.stdout.write(err)
- _enter_pdb(node, call.excinfo, report)
-
- def pytest_internalerror(self, excrepr, excinfo):
- for line in str(excrepr).split("\n"):
- sys.stderr.write("INTERNALERROR> %s\n" %line)
- sys.stderr.flush()
- tb = _postmortem_traceback(excinfo)
- post_mortem(tb)
-
-
-def _enter_pdb(node, excinfo, rep):
- # XXX we re-use the TerminalReporter's terminalwriter
- # because this seems to avoid some encoding related troubles
- # for not completely clear reasons.
- tw = node.config.pluginmanager.getplugin("terminalreporter")._tw
- tw.line()
- tw.sep(">", "traceback")
- rep.toterminal(tw)
- tw.sep(">", "entering PDB")
- tb = _postmortem_traceback(excinfo)
- post_mortem(tb)
- rep._pdbshown = True
- return rep
-
-
-def _postmortem_traceback(excinfo):
- # A doctest.UnexpectedException is not useful for post_mortem.
- # Use the underlying exception instead:
- from doctest import UnexpectedException
- if isinstance(excinfo.value, UnexpectedException):
- return excinfo.value.exc_info[2]
- else:
- return excinfo._excinfo[2]
-
-
-def _find_last_non_hidden_frame(stack):
- i = max(0, len(stack) - 1)
- while i and stack[i][0].f_locals.get("__tracebackhide__", False):
- i -= 1
- return i
-
-
-def post_mortem(t):
- class Pdb(pdb.Pdb):
- def get_stack(self, f, t):
- stack, i = pdb.Pdb.get_stack(self, f, t)
- if f is None:
- i = _find_last_non_hidden_frame(stack)
- return stack, i
- p = Pdb()
- p.reset()
- p.interaction(None, t)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/pytester.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/pytester.py
deleted file mode 100644
index faed7f581c9..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/pytester.py
+++ /dev/null
@@ -1,1110 +0,0 @@
-""" (disabled by default) support for testing pytest and pytest plugins. """
-import codecs
-import gc
-import os
-import platform
-import re
-import subprocess
-import sys
-import time
-import traceback
-from fnmatch import fnmatch
-
-from py.builtin import print_
-
-from _pytest._code import Source
-import py
-import pytest
-from _pytest.main import Session, EXIT_OK
-
-
-def pytest_addoption(parser):
- # group = parser.getgroup("pytester", "pytester (self-tests) options")
- parser.addoption('--lsof',
- action="store_true", dest="lsof", default=False,
- help=("run FD checks if lsof is available"))
-
- parser.addoption('--runpytest', default="inprocess", dest="runpytest",
- choices=("inprocess", "subprocess", ),
- help=("run pytest sub runs in tests using an 'inprocess' "
- "or 'subprocess' (python -m main) method"))
-
-
-def pytest_configure(config):
- # This might be called multiple times. Only take the first.
- global _pytest_fullpath
- try:
- _pytest_fullpath
- except NameError:
- _pytest_fullpath = os.path.abspath(pytest.__file__.rstrip("oc"))
- _pytest_fullpath = _pytest_fullpath.replace("$py.class", ".py")
-
- if config.getvalue("lsof"):
- checker = LsofFdLeakChecker()
- if checker.matching_platform():
- config.pluginmanager.register(checker)
-
-
-class LsofFdLeakChecker(object):
- def get_open_files(self):
- out = self._exec_lsof()
- open_files = self._parse_lsof_output(out)
- return open_files
-
- def _exec_lsof(self):
- pid = os.getpid()
- return py.process.cmdexec("lsof -Ffn0 -p %d" % pid)
-
- def _parse_lsof_output(self, out):
- def isopen(line):
- return line.startswith('f') and ("deleted" not in line and
- 'mem' not in line and "txt" not in line and 'cwd' not in line)
-
- open_files = []
-
- for line in out.split("\n"):
- if isopen(line):
- fields = line.split('\0')
- fd = fields[0][1:]
- filename = fields[1][1:]
- if filename.startswith('/'):
- open_files.append((fd, filename))
-
- return open_files
-
- def matching_platform(self):
- try:
- py.process.cmdexec("lsof -v")
- except (py.process.cmdexec.Error, UnicodeDecodeError):
- # cmdexec may raise UnicodeDecodeError on Windows systems
- # with locale other than english:
- # https://bitbucket.org/pytest-dev/py/issues/66
- return False
- else:
- return True
-
- @pytest.hookimpl(hookwrapper=True, tryfirst=True)
- def pytest_runtest_item(self, item):
- lines1 = self.get_open_files()
- yield
- if hasattr(sys, "pypy_version_info"):
- gc.collect()
- lines2 = self.get_open_files()
-
- new_fds = set([t[0] for t in lines2]) - set([t[0] for t in lines1])
- leaked_files = [t for t in lines2 if t[0] in new_fds]
- if leaked_files:
- error = []
- error.append("***** %s FD leakage detected" % len(leaked_files))
- error.extend([str(f) for f in leaked_files])
- error.append("*** Before:")
- error.extend([str(f) for f in lines1])
- error.append("*** After:")
- error.extend([str(f) for f in lines2])
- error.append(error[0])
- error.append("*** function %s:%s: %s " % item.location)
- pytest.fail("\n".join(error), pytrace=False)
-
-
-# XXX copied from execnet's conftest.py - needs to be merged
-winpymap = {
- 'python2.7': r'C:\Python27\python.exe',
- 'python2.6': r'C:\Python26\python.exe',
- 'python3.1': r'C:\Python31\python.exe',
- 'python3.2': r'C:\Python32\python.exe',
- 'python3.3': r'C:\Python33\python.exe',
- 'python3.4': r'C:\Python34\python.exe',
- 'python3.5': r'C:\Python35\python.exe',
-}
-
-def getexecutable(name, cache={}):
- try:
- return cache[name]
- except KeyError:
- executable = py.path.local.sysfind(name)
- if executable:
- if name == "jython":
- import subprocess
- popen = subprocess.Popen([str(executable), "--version"],
- universal_newlines=True, stderr=subprocess.PIPE)
- out, err = popen.communicate()
- if not err or "2.5" not in err:
- executable = None
- if "2.5.2" in err:
- executable = None # http://bugs.jython.org/issue1790
- cache[name] = executable
- return executable
-
-@pytest.fixture(params=['python2.6', 'python2.7', 'python3.3', "python3.4",
- 'pypy', 'pypy3'])
-def anypython(request):
- name = request.param
- executable = getexecutable(name)
- if executable is None:
- if sys.platform == "win32":
- executable = winpymap.get(name, None)
- if executable:
- executable = py.path.local(executable)
- if executable.check():
- return executable
- pytest.skip("no suitable %s found" % (name,))
- return executable
-
-# used at least by pytest-xdist plugin
-@pytest.fixture
-def _pytest(request):
- """ Return a helper which offers a gethookrecorder(hook)
- method which returns a HookRecorder instance which helps
- to make assertions about called hooks.
- """
- return PytestArg(request)
-
-class PytestArg:
- def __init__(self, request):
- self.request = request
-
- def gethookrecorder(self, hook):
- hookrecorder = HookRecorder(hook._pm)
- self.request.addfinalizer(hookrecorder.finish_recording)
- return hookrecorder
-
-
-def get_public_names(l):
- """Only return names from iterator l without a leading underscore."""
- return [x for x in l if x[0] != "_"]
-
-
-class ParsedCall:
- def __init__(self, name, kwargs):
- self.__dict__.update(kwargs)
- self._name = name
-
- def __repr__(self):
- d = self.__dict__.copy()
- del d['_name']
- return "<ParsedCall %r(**%r)>" %(self._name, d)
-
-
-class HookRecorder:
- """Record all hooks called in a plugin manager.
-
- This wraps all the hook calls in the plugin manager, recording
- each call before propagating the normal calls.
-
- """
-
- def __init__(self, pluginmanager):
- self._pluginmanager = pluginmanager
- self.calls = []
-
- def before(hook_name, hook_impls, kwargs):
- self.calls.append(ParsedCall(hook_name, kwargs))
-
- def after(outcome, hook_name, hook_impls, kwargs):
- pass
-
- self._undo_wrapping = pluginmanager.add_hookcall_monitoring(before, after)
-
- def finish_recording(self):
- self._undo_wrapping()
-
- def getcalls(self, names):
- if isinstance(names, str):
- names = names.split()
- return [call for call in self.calls if call._name in names]
-
- def assert_contains(self, entries):
- __tracebackhide__ = True
- i = 0
- entries = list(entries)
- backlocals = sys._getframe(1).f_locals
- while entries:
- name, check = entries.pop(0)
- for ind, call in enumerate(self.calls[i:]):
- if call._name == name:
- print_("NAMEMATCH", name, call)
- if eval(check, backlocals, call.__dict__):
- print_("CHECKERMATCH", repr(check), "->", call)
- else:
- print_("NOCHECKERMATCH", repr(check), "-", call)
- continue
- i += ind + 1
- break
- print_("NONAMEMATCH", name, "with", call)
- else:
- pytest.fail("could not find %r check %r" % (name, check))
-
- def popcall(self, name):
- __tracebackhide__ = True
- for i, call in enumerate(self.calls):
- if call._name == name:
- del self.calls[i]
- return call
- lines = ["could not find call %r, in:" % (name,)]
- lines.extend([" %s" % str(x) for x in self.calls])
- pytest.fail("\n".join(lines))
-
- def getcall(self, name):
- l = self.getcalls(name)
- assert len(l) == 1, (name, l)
- return l[0]
-
- # functionality for test reports
-
- def getreports(self,
- names="pytest_runtest_logreport pytest_collectreport"):
- return [x.report for x in self.getcalls(names)]
-
- def matchreport(self, inamepart="",
- names="pytest_runtest_logreport pytest_collectreport", when=None):
- """ return a testreport whose dotted import path matches """
- l = []
- for rep in self.getreports(names=names):
- try:
- if not when and rep.when != "call" and rep.passed:
- # setup/teardown passing reports - let's ignore those
- continue
- except AttributeError:
- pass
- if when and getattr(rep, 'when', None) != when:
- continue
- if not inamepart or inamepart in rep.nodeid.split("::"):
- l.append(rep)
- if not l:
- raise ValueError("could not find test report matching %r: "
- "no test reports at all!" % (inamepart,))
- if len(l) > 1:
- raise ValueError(
- "found 2 or more testreports matching %r: %s" %(inamepart, l))
- return l[0]
-
- def getfailures(self,
- names='pytest_runtest_logreport pytest_collectreport'):
- return [rep for rep in self.getreports(names) if rep.failed]
-
- def getfailedcollections(self):
- return self.getfailures('pytest_collectreport')
-
- def listoutcomes(self):
- passed = []
- skipped = []
- failed = []
- for rep in self.getreports(
- "pytest_collectreport pytest_runtest_logreport"):
- if rep.passed:
- if getattr(rep, "when", None) == "call":
- passed.append(rep)
- elif rep.skipped:
- skipped.append(rep)
- elif rep.failed:
- failed.append(rep)
- return passed, skipped, failed
-
- def countoutcomes(self):
- return [len(x) for x in self.listoutcomes()]
-
- def assertoutcome(self, passed=0, skipped=0, failed=0):
- realpassed, realskipped, realfailed = self.listoutcomes()
- assert passed == len(realpassed)
- assert skipped == len(realskipped)
- assert failed == len(realfailed)
-
- def clear(self):
- self.calls[:] = []
-
-
-@pytest.fixture
-def linecomp(request):
- return LineComp()
-
-
-def pytest_funcarg__LineMatcher(request):
- return LineMatcher
-
-
-@pytest.fixture
-def testdir(request, tmpdir_factory):
- return Testdir(request, tmpdir_factory)
-
-
-rex_outcome = re.compile("(\d+) ([\w-]+)")
-class RunResult:
- """The result of running a command.
-
- Attributes:
-
- :ret: The return value.
- :outlines: List of lines captured from stdout.
- :errlines: List of lines captures from stderr.
- :stdout: :py:class:`LineMatcher` of stdout, use ``stdout.str()`` to
- reconstruct stdout or the commonly used
- ``stdout.fnmatch_lines()`` method.
- :stderrr: :py:class:`LineMatcher` of stderr.
- :duration: Duration in seconds.
-
- """
- def __init__(self, ret, outlines, errlines, duration):
- self.ret = ret
- self.outlines = outlines
- self.errlines = errlines
- self.stdout = LineMatcher(outlines)
- self.stderr = LineMatcher(errlines)
- self.duration = duration
-
- def parseoutcomes(self):
- """ Return a dictionary of outcomestring->num from parsing
- the terminal output that the test process produced."""
- for line in reversed(self.outlines):
- if 'seconds' in line:
- outcomes = rex_outcome.findall(line)
- if outcomes:
- d = {}
- for num, cat in outcomes:
- d[cat] = int(num)
- return d
-
- def assert_outcomes(self, passed=0, skipped=0, failed=0):
- """ assert that the specified outcomes appear with the respective
- numbers (0 means it didn't occur) in the text output from a test run."""
- d = self.parseoutcomes()
- assert passed == d.get("passed", 0)
- assert skipped == d.get("skipped", 0)
- assert failed == d.get("failed", 0)
-
-
-
-class Testdir:
- """Temporary test directory with tools to test/run py.test itself.
-
- This is based on the ``tmpdir`` fixture but provides a number of
- methods which aid with testing py.test itself. Unless
- :py:meth:`chdir` is used all methods will use :py:attr:`tmpdir` as
- current working directory.
-
- Attributes:
-
- :tmpdir: The :py:class:`py.path.local` instance of the temporary
- directory.
-
- :plugins: A list of plugins to use with :py:meth:`parseconfig` and
- :py:meth:`runpytest`. Initially this is an empty list but
- plugins can be added to the list. The type of items to add to
- the list depend on the method which uses them so refer to them
- for details.
-
- """
-
- def __init__(self, request, tmpdir_factory):
- self.request = request
- # XXX remove duplication with tmpdir plugin
- basetmp = tmpdir_factory.ensuretemp("testdir")
- name = request.function.__name__
- for i in range(100):
- try:
- tmpdir = basetmp.mkdir(name + str(i))
- except py.error.EEXIST:
- continue
- break
- self.tmpdir = tmpdir
- self.plugins = []
- self._savesyspath = (list(sys.path), list(sys.meta_path))
- self._savemodulekeys = set(sys.modules)
- self.chdir() # always chdir
- self.request.addfinalizer(self.finalize)
- method = self.request.config.getoption("--runpytest")
- if method == "inprocess":
- self._runpytest_method = self.runpytest_inprocess
- elif method == "subprocess":
- self._runpytest_method = self.runpytest_subprocess
-
- def __repr__(self):
- return "<Testdir %r>" % (self.tmpdir,)
-
- def finalize(self):
- """Clean up global state artifacts.
-
- Some methods modify the global interpreter state and this
- tries to clean this up. It does not remove the temporary
- directory however so it can be looked at after the test run
- has finished.
-
- """
- sys.path[:], sys.meta_path[:] = self._savesyspath
- if hasattr(self, '_olddir'):
- self._olddir.chdir()
- self.delete_loaded_modules()
-
- def delete_loaded_modules(self):
- """Delete modules that have been loaded during a test.
-
- This allows the interpreter to catch module changes in case
- the module is re-imported.
- """
- for name in set(sys.modules).difference(self._savemodulekeys):
- # it seems zope.interfaces is keeping some state
- # (used by twisted related tests)
- if name != "zope.interface":
- del sys.modules[name]
-
- def make_hook_recorder(self, pluginmanager):
- """Create a new :py:class:`HookRecorder` for a PluginManager."""
- assert not hasattr(pluginmanager, "reprec")
- pluginmanager.reprec = reprec = HookRecorder(pluginmanager)
- self.request.addfinalizer(reprec.finish_recording)
- return reprec
-
- def chdir(self):
- """Cd into the temporary directory.
-
- This is done automatically upon instantiation.
-
- """
- old = self.tmpdir.chdir()
- if not hasattr(self, '_olddir'):
- self._olddir = old
-
- def _makefile(self, ext, args, kwargs):
- items = list(kwargs.items())
- if args:
- source = py.builtin._totext("\n").join(
- map(py.builtin._totext, args)) + py.builtin._totext("\n")
- basename = self.request.function.__name__
- items.insert(0, (basename, source))
- ret = None
- for name, value in items:
- p = self.tmpdir.join(name).new(ext=ext)
- source = Source(value)
- def my_totext(s, encoding="utf-8"):
- if py.builtin._isbytes(s):
- s = py.builtin._totext(s, encoding=encoding)
- return s
- source_unicode = "\n".join([my_totext(line) for line in source.lines])
- source = py.builtin._totext(source_unicode)
- content = source.strip().encode("utf-8") # + "\n"
- #content = content.rstrip() + "\n"
- p.write(content, "wb")
- if ret is None:
- ret = p
- return ret
-
- def makefile(self, ext, *args, **kwargs):
- """Create a new file in the testdir.
-
- ext: The extension the file should use, including the dot.
- E.g. ".py".
-
- args: All args will be treated as strings and joined using
- newlines. The result will be written as contents to the
- file. The name of the file will be based on the test
- function requesting this fixture.
- E.g. "testdir.makefile('.txt', 'line1', 'line2')"
-
- kwargs: Each keyword is the name of a file, while the value of
- it will be written as contents of the file.
- E.g. "testdir.makefile('.ini', pytest='[pytest]\naddopts=-rs\n')"
-
- """
- return self._makefile(ext, args, kwargs)
-
- def makeconftest(self, source):
- """Write a contest.py file with 'source' as contents."""
- return self.makepyfile(conftest=source)
-
- def makeini(self, source):
- """Write a tox.ini file with 'source' as contents."""
- return self.makefile('.ini', tox=source)
-
- def getinicfg(self, source):
- """Return the pytest section from the tox.ini config file."""
- p = self.makeini(source)
- return py.iniconfig.IniConfig(p)['pytest']
-
- def makepyfile(self, *args, **kwargs):
- """Shortcut for .makefile() with a .py extension."""
- return self._makefile('.py', args, kwargs)
-
- def maketxtfile(self, *args, **kwargs):
- """Shortcut for .makefile() with a .txt extension."""
- return self._makefile('.txt', args, kwargs)
-
- def syspathinsert(self, path=None):
- """Prepend a directory to sys.path, defaults to :py:attr:`tmpdir`.
-
- This is undone automatically after the test.
- """
- if path is None:
- path = self.tmpdir
- sys.path.insert(0, str(path))
- # a call to syspathinsert() usually means that the caller
- # wants to import some dynamically created files.
- # with python3 we thus invalidate import caches.
- self._possibly_invalidate_import_caches()
-
- def _possibly_invalidate_import_caches(self):
- # invalidate caches if we can (py33 and above)
- try:
- import importlib
- except ImportError:
- pass
- else:
- if hasattr(importlib, "invalidate_caches"):
- importlib.invalidate_caches()
-
- def mkdir(self, name):
- """Create a new (sub)directory."""
- return self.tmpdir.mkdir(name)
-
- def mkpydir(self, name):
- """Create a new python package.
-
- This creates a (sub)direcotry with an empty ``__init__.py``
- file so that is recognised as a python package.
-
- """
- p = self.mkdir(name)
- p.ensure("__init__.py")
- return p
-
- Session = Session
- def getnode(self, config, arg):
- """Return the collection node of a file.
-
- :param config: :py:class:`_pytest.config.Config` instance, see
- :py:meth:`parseconfig` and :py:meth:`parseconfigure` to
- create the configuration.
-
- :param arg: A :py:class:`py.path.local` instance of the file.
-
- """
- session = Session(config)
- assert '::' not in str(arg)
- p = py.path.local(arg)
- config.hook.pytest_sessionstart(session=session)
- res = session.perform_collect([str(p)], genitems=False)[0]
- config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK)
- return res
-
- def getpathnode(self, path):
- """Return the collection node of a file.
-
- This is like :py:meth:`getnode` but uses
- :py:meth:`parseconfigure` to create the (configured) py.test
- Config instance.
-
- :param path: A :py:class:`py.path.local` instance of the file.
-
- """
- config = self.parseconfigure(path)
- session = Session(config)
- x = session.fspath.bestrelpath(path)
- config.hook.pytest_sessionstart(session=session)
- res = session.perform_collect([x], genitems=False)[0]
- config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK)
- return res
-
- def genitems(self, colitems):
- """Generate all test items from a collection node.
-
- This recurses into the collection node and returns a list of
- all the test items contained within.
-
- """
- session = colitems[0].session
- result = []
- for colitem in colitems:
- result.extend(session.genitems(colitem))
- return result
-
- def runitem(self, source):
- """Run the "test_func" Item.
-
- The calling test instance (the class which contains the test
- method) must provide a ``.getrunner()`` method which should
- return a runner which can run the test protocol for a single
- item, like e.g. :py:func:`_pytest.runner.runtestprotocol`.
-
- """
- # used from runner functional tests
- item = self.getitem(source)
- # the test class where we are called from wants to provide the runner
- testclassinstance = self.request.instance
- runner = testclassinstance.getrunner()
- return runner(item)
-
- def inline_runsource(self, source, *cmdlineargs):
- """Run a test module in process using ``pytest.main()``.
-
- This run writes "source" into a temporary file and runs
- ``pytest.main()`` on it, returning a :py:class:`HookRecorder`
- instance for the result.
-
- :param source: The source code of the test module.
-
- :param cmdlineargs: Any extra command line arguments to use.
-
- :return: :py:class:`HookRecorder` instance of the result.
-
- """
- p = self.makepyfile(source)
- l = list(cmdlineargs) + [p]
- return self.inline_run(*l)
-
- def inline_genitems(self, *args):
- """Run ``pytest.main(['--collectonly'])`` in-process.
-
- Retuns a tuple of the collected items and a
- :py:class:`HookRecorder` instance.
-
- This runs the :py:func:`pytest.main` function to run all of
- py.test inside the test process itself like
- :py:meth:`inline_run`. However the return value is a tuple of
- the collection items and a :py:class:`HookRecorder` instance.
-
- """
- rec = self.inline_run("--collect-only", *args)
- items = [x.item for x in rec.getcalls("pytest_itemcollected")]
- return items, rec
-
- def inline_run(self, *args, **kwargs):
- """Run ``pytest.main()`` in-process, returning a HookRecorder.
-
- This runs the :py:func:`pytest.main` function to run all of
- py.test inside the test process itself. This means it can
- return a :py:class:`HookRecorder` instance which gives more
- detailed results from then run then can be done by matching
- stdout/stderr from :py:meth:`runpytest`.
-
- :param args: Any command line arguments to pass to
- :py:func:`pytest.main`.
-
- :param plugin: (keyword-only) Extra plugin instances the
- ``pytest.main()`` instance should use.
-
- :return: A :py:class:`HookRecorder` instance.
-
- """
- rec = []
- class Collect:
- def pytest_configure(x, config):
- rec.append(self.make_hook_recorder(config.pluginmanager))
-
- plugins = kwargs.get("plugins") or []
- plugins.append(Collect())
- ret = pytest.main(list(args), plugins=plugins)
- self.delete_loaded_modules()
- if len(rec) == 1:
- reprec = rec.pop()
- else:
- class reprec:
- pass
- reprec.ret = ret
-
- # typically we reraise keyboard interrupts from the child run
- # because it's our user requesting interruption of the testing
- if ret == 2 and not kwargs.get("no_reraise_ctrlc"):
- calls = reprec.getcalls("pytest_keyboard_interrupt")
- if calls and calls[-1].excinfo.type == KeyboardInterrupt:
- raise KeyboardInterrupt()
- return reprec
-
- def runpytest_inprocess(self, *args, **kwargs):
- """ Return result of running pytest in-process, providing a similar
- interface to what self.runpytest() provides. """
- if kwargs.get("syspathinsert"):
- self.syspathinsert()
- now = time.time()
- capture = py.io.StdCapture()
- try:
- try:
- reprec = self.inline_run(*args, **kwargs)
- except SystemExit as e:
- class reprec:
- ret = e.args[0]
- except Exception:
- traceback.print_exc()
- class reprec:
- ret = 3
- finally:
- out, err = capture.reset()
- sys.stdout.write(out)
- sys.stderr.write(err)
-
- res = RunResult(reprec.ret,
- out.split("\n"), err.split("\n"),
- time.time()-now)
- res.reprec = reprec
- return res
-
- def runpytest(self, *args, **kwargs):
- """ Run pytest inline or in a subprocess, depending on the command line
- option "--runpytest" and return a :py:class:`RunResult`.
-
- """
- args = self._ensure_basetemp(args)
- return self._runpytest_method(*args, **kwargs)
-
- def _ensure_basetemp(self, args):
- args = [str(x) for x in args]
- for x in args:
- if str(x).startswith('--basetemp'):
- #print ("basedtemp exists: %s" %(args,))
- break
- else:
- args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
- #print ("added basetemp: %s" %(args,))
- return args
-
- def parseconfig(self, *args):
- """Return a new py.test Config instance from given commandline args.
-
- This invokes the py.test bootstrapping code in _pytest.config
- to create a new :py:class:`_pytest.core.PluginManager` and
- call the pytest_cmdline_parse hook to create new
- :py:class:`_pytest.config.Config` instance.
-
- If :py:attr:`plugins` has been populated they should be plugin
- modules which will be registered with the PluginManager.
-
- """
- args = self._ensure_basetemp(args)
-
- import _pytest.config
- config = _pytest.config._prepareconfig(args, self.plugins)
- # we don't know what the test will do with this half-setup config
- # object and thus we make sure it gets unconfigured properly in any
- # case (otherwise capturing could still be active, for example)
- self.request.addfinalizer(config._ensure_unconfigure)
- return config
-
- def parseconfigure(self, *args):
- """Return a new py.test configured Config instance.
-
- This returns a new :py:class:`_pytest.config.Config` instance
- like :py:meth:`parseconfig`, but also calls the
- pytest_configure hook.
-
- """
- config = self.parseconfig(*args)
- config._do_configure()
- self.request.addfinalizer(config._ensure_unconfigure)
- return config
-
- def getitem(self, source, funcname="test_func"):
- """Return the test item for a test function.
-
- This writes the source to a python file and runs py.test's
- collection on the resulting module, returning the test item
- for the requested function name.
-
- :param source: The module source.
-
- :param funcname: The name of the test function for which the
- Item must be returned.
-
- """
- items = self.getitems(source)
- for item in items:
- if item.name == funcname:
- return item
- assert 0, "%r item not found in module:\n%s\nitems: %s" %(
- funcname, source, items)
-
- def getitems(self, source):
- """Return all test items collected from the module.
-
- This writes the source to a python file and runs py.test's
- collection on the resulting module, returning all test items
- contained within.
-
- """
- modcol = self.getmodulecol(source)
- return self.genitems([modcol])
-
- def getmodulecol(self, source, configargs=(), withinit=False):
- """Return the module collection node for ``source``.
-
- This writes ``source`` to a file using :py:meth:`makepyfile`
- and then runs the py.test collection on it, returning the
- collection node for the test module.
-
- :param source: The source code of the module to collect.
-
- :param configargs: Any extra arguments to pass to
- :py:meth:`parseconfigure`.
-
- :param withinit: Whether to also write a ``__init__.py`` file
- to the temporarly directory to ensure it is a package.
-
- """
- kw = {self.request.function.__name__: Source(source).strip()}
- path = self.makepyfile(**kw)
- if withinit:
- self.makepyfile(__init__ = "#")
- self.config = config = self.parseconfigure(path, *configargs)
- node = self.getnode(config, path)
- return node
-
- def collect_by_name(self, modcol, name):
- """Return the collection node for name from the module collection.
-
- This will search a module collection node for a collection
- node matching the given name.
-
- :param modcol: A module collection node, see
- :py:meth:`getmodulecol`.
-
- :param name: The name of the node to return.
-
- """
- for colitem in modcol._memocollect():
- if colitem.name == name:
- return colitem
-
- def popen(self, cmdargs, stdout, stderr, **kw):
- """Invoke subprocess.Popen.
-
- This calls subprocess.Popen making sure the current working
- directory is the PYTHONPATH.
-
- You probably want to use :py:meth:`run` instead.
-
- """
- env = os.environ.copy()
- env['PYTHONPATH'] = os.pathsep.join(filter(None, [
- str(os.getcwd()), env.get('PYTHONPATH', '')]))
- kw['env'] = env
- return subprocess.Popen(cmdargs,
- stdout=stdout, stderr=stderr, **kw)
-
- def run(self, *cmdargs):
- """Run a command with arguments.
-
- Run a process using subprocess.Popen saving the stdout and
- stderr.
-
- Returns a :py:class:`RunResult`.
-
- """
- return self._run(*cmdargs)
-
- def _run(self, *cmdargs):
- cmdargs = [str(x) for x in cmdargs]
- p1 = self.tmpdir.join("stdout")
- p2 = self.tmpdir.join("stderr")
- print_("running:", ' '.join(cmdargs))
- print_(" in:", str(py.path.local()))
- f1 = codecs.open(str(p1), "w", encoding="utf8")
- f2 = codecs.open(str(p2), "w", encoding="utf8")
- try:
- now = time.time()
- popen = self.popen(cmdargs, stdout=f1, stderr=f2,
- close_fds=(sys.platform != "win32"))
- ret = popen.wait()
- finally:
- f1.close()
- f2.close()
- f1 = codecs.open(str(p1), "r", encoding="utf8")
- f2 = codecs.open(str(p2), "r", encoding="utf8")
- try:
- out = f1.read().splitlines()
- err = f2.read().splitlines()
- finally:
- f1.close()
- f2.close()
- self._dump_lines(out, sys.stdout)
- self._dump_lines(err, sys.stderr)
- return RunResult(ret, out, err, time.time()-now)
-
- def _dump_lines(self, lines, fp):
- try:
- for line in lines:
- py.builtin.print_(line, file=fp)
- except UnicodeEncodeError:
- print("couldn't print to %s because of encoding" % (fp,))
-
- def _getpytestargs(self):
- # we cannot use "(sys.executable,script)"
- # because on windows the script is e.g. a py.test.exe
- return (sys.executable, _pytest_fullpath,) # noqa
-
- def runpython(self, script):
- """Run a python script using sys.executable as interpreter.
-
- Returns a :py:class:`RunResult`.
- """
- return self.run(sys.executable, script)
-
- def runpython_c(self, command):
- """Run python -c "command", return a :py:class:`RunResult`."""
- return self.run(sys.executable, "-c", command)
-
- def runpytest_subprocess(self, *args, **kwargs):
- """Run py.test as a subprocess with given arguments.
-
- Any plugins added to the :py:attr:`plugins` list will added
- using the ``-p`` command line option. Addtionally
- ``--basetemp`` is used put any temporary files and directories
- in a numbered directory prefixed with "runpytest-" so they do
- not conflict with the normal numberd pytest location for
- temporary files and directories.
-
- Returns a :py:class:`RunResult`.
-
- """
- p = py.path.local.make_numbered_dir(prefix="runpytest-",
- keep=None, rootdir=self.tmpdir)
- args = ('--basetemp=%s' % p, ) + args
- #for x in args:
- # if '--confcutdir' in str(x):
- # break
- #else:
- # pass
- # args = ('--confcutdir=.',) + args
- plugins = [x for x in self.plugins if isinstance(x, str)]
- if plugins:
- args = ('-p', plugins[0]) + args
- args = self._getpytestargs() + args
- return self.run(*args)
-
- def spawn_pytest(self, string, expect_timeout=10.0):
- """Run py.test using pexpect.
-
- This makes sure to use the right py.test and sets up the
- temporary directory locations.
-
- The pexpect child is returned.
-
- """
- basetemp = self.tmpdir.mkdir("pexpect")
- invoke = " ".join(map(str, self._getpytestargs()))
- cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string)
- return self.spawn(cmd, expect_timeout=expect_timeout)
-
- def spawn(self, cmd, expect_timeout=10.0):
- """Run a command using pexpect.
-
- The pexpect child is returned.
- """
- pexpect = pytest.importorskip("pexpect", "3.0")
- if hasattr(sys, 'pypy_version_info') and '64' in platform.machine():
- pytest.skip("pypy-64 bit not supported")
- if sys.platform == "darwin":
- pytest.xfail("pexpect does not work reliably on darwin?!")
- if sys.platform.startswith("freebsd"):
- pytest.xfail("pexpect does not work reliably on freebsd")
- logfile = self.tmpdir.join("spawn.out").open("wb")
- child = pexpect.spawn(cmd, logfile=logfile)
- self.request.addfinalizer(logfile.close)
- child.timeout = expect_timeout
- return child
-
-def getdecoded(out):
- try:
- return out.decode("utf-8")
- except UnicodeDecodeError:
- return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % (
- py.io.saferepr(out),)
-
-
-class LineComp:
- def __init__(self):
- self.stringio = py.io.TextIO()
-
- def assert_contains_lines(self, lines2):
- """ assert that lines2 are contained (linearly) in lines1.
- return a list of extralines found.
- """
- __tracebackhide__ = True
- val = self.stringio.getvalue()
- self.stringio.truncate(0)
- self.stringio.seek(0)
- lines1 = val.split("\n")
- return LineMatcher(lines1).fnmatch_lines(lines2)
-
-
-class LineMatcher:
- """Flexible matching of text.
-
- This is a convenience class to test large texts like the output of
- commands.
-
- The constructor takes a list of lines without their trailing
- newlines, i.e. ``text.splitlines()``.
-
- """
-
- def __init__(self, lines):
- self.lines = lines
-
- def str(self):
- """Return the entire original text."""
- return "\n".join(self.lines)
-
- def _getlines(self, lines2):
- if isinstance(lines2, str):
- lines2 = Source(lines2)
- if isinstance(lines2, Source):
- lines2 = lines2.strip().lines
- return lines2
-
- def fnmatch_lines_random(self, lines2):
- """Check lines exist in the output.
-
- The argument is a list of lines which have to occur in the
- output, in any order. Each line can contain glob whildcards.
-
- """
- lines2 = self._getlines(lines2)
- for line in lines2:
- for x in self.lines:
- if line == x or fnmatch(x, line):
- print_("matched: ", repr(line))
- break
- else:
- raise ValueError("line %r not found in output" % line)
-
- def get_lines_after(self, fnline):
- """Return all lines following the given line in the text.
-
- The given line can contain glob wildcards.
- """
- for i, line in enumerate(self.lines):
- if fnline == line or fnmatch(line, fnline):
- return self.lines[i+1:]
- raise ValueError("line %r not found in output" % fnline)
-
- def fnmatch_lines(self, lines2):
- """Search the text for matching lines.
-
- The argument is a list of lines which have to match and can
- use glob wildcards. If they do not match an pytest.fail() is
- called. The matches and non-matches are also printed on
- stdout.
-
- """
- def show(arg1, arg2):
- py.builtin.print_(arg1, arg2, file=sys.stderr)
- lines2 = self._getlines(lines2)
- lines1 = self.lines[:]
- nextline = None
- extralines = []
- __tracebackhide__ = True
- for line in lines2:
- nomatchprinted = False
- while lines1:
- nextline = lines1.pop(0)
- if line == nextline:
- show("exact match:", repr(line))
- break
- elif fnmatch(nextline, line):
- show("fnmatch:", repr(line))
- show(" with:", repr(nextline))
- break
- else:
- if not nomatchprinted:
- show("nomatch:", repr(line))
- nomatchprinted = True
- show(" and:", repr(nextline))
- extralines.append(nextline)
- else:
- pytest.fail("remains unmatched: %r, see stderr" % (line,))
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/python.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/python.py
deleted file mode 100644
index 3580eae074f..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/python.py
+++ /dev/null
@@ -1,2302 +0,0 @@
-""" Python test discovery, setup and run of test functions. """
-import fnmatch
-import functools
-import inspect
-import re
-import types
-import sys
-
-import py
-import pytest
-from _pytest._code.code import TerminalRepr
-from _pytest.mark import MarkDecorator, MarkerError
-
-try:
- import enum
-except ImportError: # pragma: no cover
- # Only available in Python 3.4+ or as a backport
- enum = None
-
-import _pytest
-import _pytest._pluggy as pluggy
-
-cutdir2 = py.path.local(_pytest.__file__).dirpath()
-cutdir1 = py.path.local(pluggy.__file__.rstrip("oc"))
-
-
-NoneType = type(None)
-NOTSET = object()
-isfunction = inspect.isfunction
-isclass = inspect.isclass
-callable = py.builtin.callable
-# used to work around a python2 exception info leak
-exc_clear = getattr(sys, 'exc_clear', lambda: None)
-# The type of re.compile objects is not exposed in Python.
-REGEX_TYPE = type(re.compile(''))
-
-_PY3 = sys.version_info > (3, 0)
-_PY2 = not _PY3
-
-
-if hasattr(inspect, 'signature'):
- def _format_args(func):
- return str(inspect.signature(func))
-else:
- def _format_args(func):
- return inspect.formatargspec(*inspect.getargspec(func))
-
-if sys.version_info[:2] == (2, 6):
- def isclass(object):
- """ Return true if the object is a class. Overrides inspect.isclass for
- python 2.6 because it will return True for objects which always return
- something on __getattr__ calls (see #1035).
- Backport of https://hg.python.org/cpython/rev/35bf8f7a8edc
- """
- return isinstance(object, (type, types.ClassType))
-
-def _has_positional_arg(func):
- return func.__code__.co_argcount
-
-
-def filter_traceback(entry):
- # entry.path might sometimes return a str object when the entry
- # points to dynamically generated code
- # see https://bitbucket.org/pytest-dev/py/issues/71
- raw_filename = entry.frame.code.raw.co_filename
- is_generated = '<' in raw_filename and '>' in raw_filename
- if is_generated:
- return False
- # entry.path might point to an inexisting file, in which case it will
- # alsso return a str object. see #1133
- p = py.path.local(entry.path)
- return p != cutdir1 and not p.relto(cutdir2)
-
-
-def get_real_func(obj):
- """ gets the real function object of the (possibly) wrapped object by
- functools.wraps or functools.partial.
- """
- while hasattr(obj, "__wrapped__"):
- obj = obj.__wrapped__
- if isinstance(obj, functools.partial):
- obj = obj.func
- return obj
-
-def getfslineno(obj):
- # xxx let decorators etc specify a sane ordering
- obj = get_real_func(obj)
- if hasattr(obj, 'place_as'):
- obj = obj.place_as
- fslineno = _pytest._code.getfslineno(obj)
- assert isinstance(fslineno[1], int), obj
- return fslineno
-
-def getimfunc(func):
- try:
- return func.__func__
- except AttributeError:
- try:
- return func.im_func
- except AttributeError:
- return func
-
-def safe_getattr(object, name, default):
- """ Like getattr but return default upon any Exception.
-
- Attribute access can potentially fail for 'evil' Python objects.
- See issue214
- """
- try:
- return getattr(object, name, default)
- except Exception:
- return default
-
-
-class FixtureFunctionMarker:
- def __init__(self, scope, params,
- autouse=False, yieldctx=False, ids=None):
- self.scope = scope
- self.params = params
- self.autouse = autouse
- self.yieldctx = yieldctx
- self.ids = ids
-
- def __call__(self, function):
- if isclass(function):
- raise ValueError(
- "class fixtures not supported (may be in the future)")
- function._pytestfixturefunction = self
- return function
-
-
-def fixture(scope="function", params=None, autouse=False, ids=None):
- """ (return a) decorator to mark a fixture factory function.
-
- This decorator can be used (with or or without parameters) to define
- a fixture function. The name of the fixture function can later be
- referenced to cause its invocation ahead of running tests: test
- modules or classes can use the pytest.mark.usefixtures(fixturename)
- marker. Test functions can directly use fixture names as input
- arguments in which case the fixture instance returned from the fixture
- function will be injected.
-
- :arg scope: the scope for which this fixture is shared, one of
- "function" (default), "class", "module", "session".
-
- :arg params: an optional list of parameters which will cause multiple
- invocations of the fixture function and all of the tests
- using it.
-
- :arg autouse: if True, the fixture func is activated for all tests that
- can see it. If False (the default) then an explicit
- reference is needed to activate the fixture.
-
- :arg ids: list of string ids each corresponding to the params
- so that they are part of the test id. If no ids are provided
- they will be generated automatically from the params.
-
- """
- if callable(scope) and params is None and autouse == False:
- # direct decoration
- return FixtureFunctionMarker(
- "function", params, autouse)(scope)
- if params is not None and not isinstance(params, (list, tuple)):
- params = list(params)
- return FixtureFunctionMarker(scope, params, autouse, ids=ids)
-
-def yield_fixture(scope="function", params=None, autouse=False, ids=None):
- """ (return a) decorator to mark a yield-fixture factory function
- (EXPERIMENTAL).
-
- This takes the same arguments as :py:func:`pytest.fixture` but
- expects a fixture function to use a ``yield`` instead of a ``return``
- statement to provide a fixture. See
- http://pytest.org/en/latest/yieldfixture.html for more info.
- """
- if callable(scope) and params is None and autouse == False:
- # direct decoration
- return FixtureFunctionMarker(
- "function", params, autouse, yieldctx=True)(scope)
- else:
- return FixtureFunctionMarker(scope, params, autouse,
- yieldctx=True, ids=ids)
-
-defaultfuncargprefixmarker = fixture()
-
-def pyobj_property(name):
- def get(self):
- node = self.getparent(getattr(pytest, name))
- if node is not None:
- return node.obj
- doc = "python %s object this node was collected from (can be None)." % (
- name.lower(),)
- return property(get, None, None, doc)
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("general")
- group.addoption('--fixtures', '--funcargs',
- action="store_true", dest="showfixtures", default=False,
- help="show available fixtures, sorted by plugin appearance")
- parser.addini("usefixtures", type="args", default=[],
- help="list of default fixtures to be used with this project")
- parser.addini("python_files", type="args",
- default=['test_*.py', '*_test.py'],
- help="glob-style file patterns for Python test module discovery")
- parser.addini("python_classes", type="args", default=["Test",],
- help="prefixes or glob names for Python test class discovery")
- parser.addini("python_functions", type="args", default=["test",],
- help="prefixes or glob names for Python test function and "
- "method discovery")
-
- group.addoption("--import-mode", default="prepend",
- choices=["prepend", "append"], dest="importmode",
- help="prepend/append to sys.path when importing test modules, "
- "default is to prepend.")
-
-
-def pytest_cmdline_main(config):
- if config.option.showfixtures:
- showfixtures(config)
- return 0
-
-
-def pytest_generate_tests(metafunc):
- # those alternative spellings are common - raise a specific error to alert
- # the user
- alt_spellings = ['parameterize', 'parametrise', 'parameterise']
- for attr in alt_spellings:
- if hasattr(metafunc.function, attr):
- msg = "{0} has '{1}', spelling should be 'parametrize'"
- raise MarkerError(msg.format(metafunc.function.__name__, attr))
- try:
- markers = metafunc.function.parametrize
- except AttributeError:
- return
- for marker in markers:
- metafunc.parametrize(*marker.args, **marker.kwargs)
-
-def pytest_configure(config):
- config.addinivalue_line("markers",
- "parametrize(argnames, argvalues): call a test function multiple "
- "times passing in different arguments in turn. argvalues generally "
- "needs to be a list of values if argnames specifies only one name "
- "or a list of tuples of values if argnames specifies multiple names. "
- "Example: @parametrize('arg1', [1,2]) would lead to two calls of the "
- "decorated test function, one with arg1=1 and another with arg1=2."
- "see http://pytest.org/latest/parametrize.html for more info and "
- "examples."
- )
- config.addinivalue_line("markers",
- "usefixtures(fixturename1, fixturename2, ...): mark tests as needing "
- "all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures "
- )
-
-def pytest_sessionstart(session):
- session._fixturemanager = FixtureManager(session)
-
-@pytest.hookimpl(trylast=True)
-def pytest_namespace():
- raises.Exception = pytest.fail.Exception
- return {
- 'fixture': fixture,
- 'yield_fixture': yield_fixture,
- 'raises' : raises,
- 'collect': {
- 'Module': Module, 'Class': Class, 'Instance': Instance,
- 'Function': Function, 'Generator': Generator,
- '_fillfuncargs': fillfixtures}
- }
-
-@fixture(scope="session")
-def pytestconfig(request):
- """ the pytest config object with access to command line opts."""
- return request.config
-
-
-@pytest.hookimpl(trylast=True)
-def pytest_pyfunc_call(pyfuncitem):
- testfunction = pyfuncitem.obj
- if pyfuncitem._isyieldedfunction():
- testfunction(*pyfuncitem._args)
- else:
- funcargs = pyfuncitem.funcargs
- testargs = {}
- for arg in pyfuncitem._fixtureinfo.argnames:
- testargs[arg] = funcargs[arg]
- testfunction(**testargs)
- return True
-
-def pytest_collect_file(path, parent):
- ext = path.ext
- if ext == ".py":
- if not parent.session.isinitpath(path):
- for pat in parent.config.getini('python_files'):
- if path.fnmatch(pat):
- break
- else:
- return
- ihook = parent.session.gethookproxy(path)
- return ihook.pytest_pycollect_makemodule(path=path, parent=parent)
-
-def pytest_pycollect_makemodule(path, parent):
- return Module(path, parent)
-
-@pytest.hookimpl(hookwrapper=True)
-def pytest_pycollect_makeitem(collector, name, obj):
- outcome = yield
- res = outcome.get_result()
- if res is not None:
- raise StopIteration
- # nothing was collected elsewhere, let's do it here
- if isclass(obj):
- if collector.istestclass(obj, name):
- Class = collector._getcustomclass("Class")
- outcome.force_result(Class(name, parent=collector))
- elif collector.istestfunction(obj, name):
- # mock seems to store unbound methods (issue473), normalize it
- obj = getattr(obj, "__func__", obj)
- # We need to try and unwrap the function if it's a functools.partial
- # or a funtools.wrapped.
- # We musn't if it's been wrapped with mock.patch (python 2 only)
- if not (isfunction(obj) or isfunction(get_real_func(obj))):
- collector.warn(code="C2", message=
- "cannot collect %r because it is not a function."
- % name, )
- elif getattr(obj, "__test__", True):
- if is_generator(obj):
- res = Generator(name, parent=collector)
- else:
- res = list(collector._genfunctions(name, obj))
- outcome.force_result(res)
-
-def is_generator(func):
- try:
- return _pytest._code.getrawcode(func).co_flags & 32 # generator function
- except AttributeError: # builtin functions have no bytecode
- # assume them to not be generators
- return False
-
-class PyobjContext(object):
- module = pyobj_property("Module")
- cls = pyobj_property("Class")
- instance = pyobj_property("Instance")
-
-class PyobjMixin(PyobjContext):
- def obj():
- def fget(self):
- try:
- return self._obj
- except AttributeError:
- self._obj = obj = self._getobj()
- return obj
- def fset(self, value):
- self._obj = value
- return property(fget, fset, None, "underlying python object")
- obj = obj()
-
- def _getobj(self):
- return getattr(self.parent.obj, self.name)
-
- def getmodpath(self, stopatmodule=True, includemodule=False):
- """ return python path relative to the containing module. """
- chain = self.listchain()
- chain.reverse()
- parts = []
- for node in chain:
- if isinstance(node, Instance):
- continue
- name = node.name
- if isinstance(node, Module):
- assert name.endswith(".py")
- name = name[:-3]
- if stopatmodule:
- if includemodule:
- parts.append(name)
- break
- parts.append(name)
- parts.reverse()
- s = ".".join(parts)
- return s.replace(".[", "[")
-
- def _getfslineno(self):
- return getfslineno(self.obj)
-
- def reportinfo(self):
- # XXX caching?
- obj = self.obj
- compat_co_firstlineno = getattr(obj, 'compat_co_firstlineno', None)
- if isinstance(compat_co_firstlineno, int):
- # nose compatibility
- fspath = sys.modules[obj.__module__].__file__
- if fspath.endswith(".pyc"):
- fspath = fspath[:-1]
- lineno = compat_co_firstlineno
- else:
- fspath, lineno = getfslineno(obj)
- modpath = self.getmodpath()
- assert isinstance(lineno, int)
- return fspath, lineno, modpath
-
-class PyCollector(PyobjMixin, pytest.Collector):
-
- def funcnamefilter(self, name):
- return self._matches_prefix_or_glob_option('python_functions', name)
-
- def isnosetest(self, obj):
- """ Look for the __test__ attribute, which is applied by the
- @nose.tools.istest decorator
- """
- # We explicitly check for "is True" here to not mistakenly treat
- # classes with a custom __getattr__ returning something truthy (like a
- # function) as test classes.
- return safe_getattr(obj, '__test__', False) is True
-
- def classnamefilter(self, name):
- return self._matches_prefix_or_glob_option('python_classes', name)
-
- def istestfunction(self, obj, name):
- return (
- (self.funcnamefilter(name) or self.isnosetest(obj)) and
- safe_getattr(obj, "__call__", False) and getfixturemarker(obj) is None
- )
-
- def istestclass(self, obj, name):
- return self.classnamefilter(name) or self.isnosetest(obj)
-
- def _matches_prefix_or_glob_option(self, option_name, name):
- """
- checks if the given name matches the prefix or glob-pattern defined
- in ini configuration.
- """
- for option in self.config.getini(option_name):
- if name.startswith(option):
- return True
- # check that name looks like a glob-string before calling fnmatch
- # because this is called for every name in each collected module,
- # and fnmatch is somewhat expensive to call
- elif ('*' in option or '?' in option or '[' in option) and \
- fnmatch.fnmatch(name, option):
- return True
- return False
-
- def collect(self):
- if not getattr(self.obj, "__test__", True):
- return []
-
- # NB. we avoid random getattrs and peek in the __dict__ instead
- # (XXX originally introduced from a PyPy need, still true?)
- dicts = [getattr(self.obj, '__dict__', {})]
- for basecls in inspect.getmro(self.obj.__class__):
- dicts.append(basecls.__dict__)
- seen = {}
- l = []
- for dic in dicts:
- for name, obj in list(dic.items()):
- if name in seen:
- continue
- seen[name] = True
- res = self.makeitem(name, obj)
- if res is None:
- continue
- if not isinstance(res, list):
- res = [res]
- l.extend(res)
- l.sort(key=lambda item: item.reportinfo()[:2])
- return l
-
- def makeitem(self, name, obj):
- #assert self.ihook.fspath == self.fspath, self
- return self.ihook.pytest_pycollect_makeitem(
- collector=self, name=name, obj=obj)
-
- def _genfunctions(self, name, funcobj):
- module = self.getparent(Module).obj
- clscol = self.getparent(Class)
- cls = clscol and clscol.obj or None
- transfer_markers(funcobj, cls, module)
- fm = self.session._fixturemanager
- fixtureinfo = fm.getfixtureinfo(self, funcobj, cls)
- metafunc = Metafunc(funcobj, fixtureinfo, self.config,
- cls=cls, module=module)
- methods = []
- if hasattr(module, "pytest_generate_tests"):
- methods.append(module.pytest_generate_tests)
- if hasattr(cls, "pytest_generate_tests"):
- methods.append(cls().pytest_generate_tests)
- if methods:
- self.ihook.pytest_generate_tests.call_extra(methods,
- dict(metafunc=metafunc))
- else:
- self.ihook.pytest_generate_tests(metafunc=metafunc)
-
- Function = self._getcustomclass("Function")
- if not metafunc._calls:
- yield Function(name, parent=self, fixtureinfo=fixtureinfo)
- else:
- # add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs
- add_funcarg_pseudo_fixture_def(self, metafunc, fm)
-
- for callspec in metafunc._calls:
- subname = "%s[%s]" %(name, callspec.id)
- yield Function(name=subname, parent=self,
- callspec=callspec, callobj=funcobj,
- fixtureinfo=fixtureinfo,
- keywords={callspec.id:True})
-
-def add_funcarg_pseudo_fixture_def(collector, metafunc, fixturemanager):
- # this function will transform all collected calls to a functions
- # if they use direct funcargs (i.e. direct parametrization)
- # because we want later test execution to be able to rely on
- # an existing FixtureDef structure for all arguments.
- # XXX we can probably avoid this algorithm if we modify CallSpec2
- # to directly care for creating the fixturedefs within its methods.
- if not metafunc._calls[0].funcargs:
- return # this function call does not have direct parametrization
- # collect funcargs of all callspecs into a list of values
- arg2params = {}
- arg2scope = {}
- for callspec in metafunc._calls:
- for argname, argvalue in callspec.funcargs.items():
- assert argname not in callspec.params
- callspec.params[argname] = argvalue
- arg2params_list = arg2params.setdefault(argname, [])
- callspec.indices[argname] = len(arg2params_list)
- arg2params_list.append(argvalue)
- if argname not in arg2scope:
- scopenum = callspec._arg2scopenum.get(argname,
- scopenum_function)
- arg2scope[argname] = scopes[scopenum]
- callspec.funcargs.clear()
-
- # register artificial FixtureDef's so that later at test execution
- # time we can rely on a proper FixtureDef to exist for fixture setup.
- arg2fixturedefs = metafunc._arg2fixturedefs
- for argname, valuelist in arg2params.items():
- # if we have a scope that is higher than function we need
- # to make sure we only ever create an according fixturedef on
- # a per-scope basis. We thus store and cache the fixturedef on the
- # node related to the scope.
- scope = arg2scope[argname]
- node = None
- if scope != "function":
- node = get_scope_node(collector, scope)
- if node is None:
- assert scope == "class" and isinstance(collector, Module)
- # use module-level collector for class-scope (for now)
- node = collector
- if node and argname in node._name2pseudofixturedef:
- arg2fixturedefs[argname] = [node._name2pseudofixturedef[argname]]
- else:
- fixturedef = FixtureDef(fixturemanager, '', argname,
- get_direct_param_fixture_func,
- arg2scope[argname],
- valuelist, False, False)
- arg2fixturedefs[argname] = [fixturedef]
- if node is not None:
- node._name2pseudofixturedef[argname] = fixturedef
-
-
-def get_direct_param_fixture_func(request):
- return request.param
-
-class FuncFixtureInfo:
- def __init__(self, argnames, names_closure, name2fixturedefs):
- self.argnames = argnames
- self.names_closure = names_closure
- self.name2fixturedefs = name2fixturedefs
-
-
-def _marked(func, mark):
- """ Returns True if :func: is already marked with :mark:, False otherwise.
- This can happen if marker is applied to class and the test file is
- invoked more than once.
- """
- try:
- func_mark = getattr(func, mark.name)
- except AttributeError:
- return False
- return mark.args == func_mark.args and mark.kwargs == func_mark.kwargs
-
-
-def transfer_markers(funcobj, cls, mod):
- # XXX this should rather be code in the mark plugin or the mark
- # plugin should merge with the python plugin.
- for holder in (cls, mod):
- try:
- pytestmark = holder.pytestmark
- except AttributeError:
- continue
- if isinstance(pytestmark, list):
- for mark in pytestmark:
- if not _marked(funcobj, mark):
- mark(funcobj)
- else:
- if not _marked(funcobj, pytestmark):
- pytestmark(funcobj)
-
-class Module(pytest.File, PyCollector):
- """ Collector for test classes and functions. """
- def _getobj(self):
- return self._memoizedcall('_obj', self._importtestmodule)
-
- def collect(self):
- self.session._fixturemanager.parsefactories(self)
- return super(Module, self).collect()
-
- def _importtestmodule(self):
- # we assume we are only called once per module
- importmode = self.config.getoption("--import-mode")
- try:
- mod = self.fspath.pyimport(ensuresyspath=importmode)
- except SyntaxError:
- raise self.CollectError(
- _pytest._code.ExceptionInfo().getrepr(style="short"))
- except self.fspath.ImportMismatchError:
- e = sys.exc_info()[1]
- raise self.CollectError(
- "import file mismatch:\n"
- "imported module %r has this __file__ attribute:\n"
- " %s\n"
- "which is not the same as the test file we want to collect:\n"
- " %s\n"
- "HINT: remove __pycache__ / .pyc files and/or use a "
- "unique basename for your test file modules"
- % e.args
- )
- #print "imported test module", mod
- self.config.pluginmanager.consider_module(mod)
- return mod
-
- def setup(self):
- setup_module = xunitsetup(self.obj, "setUpModule")
- if setup_module is None:
- setup_module = xunitsetup(self.obj, "setup_module")
- if setup_module is not None:
- #XXX: nose compat hack, move to nose plugin
- # if it takes a positional arg, its probably a pytest style one
- # so we pass the current module object
- if _has_positional_arg(setup_module):
- setup_module(self.obj)
- else:
- setup_module()
- fin = getattr(self.obj, 'tearDownModule', None)
- if fin is None:
- fin = getattr(self.obj, 'teardown_module', None)
- if fin is not None:
- #XXX: nose compat hack, move to nose plugin
- # if it takes a positional arg, it's probably a pytest style one
- # so we pass the current module object
- if _has_positional_arg(fin):
- finalizer = lambda: fin(self.obj)
- else:
- finalizer = fin
- self.addfinalizer(finalizer)
-
-
-class Class(PyCollector):
- """ Collector for test methods. """
- def collect(self):
- if hasinit(self.obj):
- self.warn("C1", "cannot collect test class %r because it has a "
- "__init__ constructor" % self.obj.__name__)
- return []
- return [self._getcustomclass("Instance")(name="()", parent=self)]
-
- def setup(self):
- setup_class = xunitsetup(self.obj, 'setup_class')
- if setup_class is not None:
- setup_class = getattr(setup_class, 'im_func', setup_class)
- setup_class = getattr(setup_class, '__func__', setup_class)
- setup_class(self.obj)
-
- fin_class = getattr(self.obj, 'teardown_class', None)
- if fin_class is not None:
- fin_class = getattr(fin_class, 'im_func', fin_class)
- fin_class = getattr(fin_class, '__func__', fin_class)
- self.addfinalizer(lambda: fin_class(self.obj))
-
-class Instance(PyCollector):
- def _getobj(self):
- obj = self.parent.obj()
- return obj
-
- def collect(self):
- self.session._fixturemanager.parsefactories(self)
- return super(Instance, self).collect()
-
- def newinstance(self):
- self.obj = self._getobj()
- return self.obj
-
-class FunctionMixin(PyobjMixin):
- """ mixin for the code common to Function and Generator.
- """
-
- def setup(self):
- """ perform setup for this test function. """
- if hasattr(self, '_preservedparent'):
- obj = self._preservedparent
- elif isinstance(self.parent, Instance):
- obj = self.parent.newinstance()
- self.obj = self._getobj()
- else:
- obj = self.parent.obj
- if inspect.ismethod(self.obj):
- setup_name = 'setup_method'
- teardown_name = 'teardown_method'
- else:
- setup_name = 'setup_function'
- teardown_name = 'teardown_function'
- setup_func_or_method = xunitsetup(obj, setup_name)
- if setup_func_or_method is not None:
- setup_func_or_method(self.obj)
- fin = getattr(obj, teardown_name, None)
- if fin is not None:
- self.addfinalizer(lambda: fin(self.obj))
-
- def _prunetraceback(self, excinfo):
- if hasattr(self, '_obj') and not self.config.option.fulltrace:
- code = _pytest._code.Code(get_real_func(self.obj))
- path, firstlineno = code.path, code.firstlineno
- traceback = excinfo.traceback
- ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
- if ntraceback == traceback:
- ntraceback = ntraceback.cut(path=path)
- if ntraceback == traceback:
- #ntraceback = ntraceback.cut(excludepath=cutdir2)
- ntraceback = ntraceback.filter(filter_traceback)
- if not ntraceback:
- ntraceback = traceback
-
- excinfo.traceback = ntraceback.filter()
- # issue364: mark all but first and last frames to
- # only show a single-line message for each frame
- if self.config.option.tbstyle == "auto":
- if len(excinfo.traceback) > 2:
- for entry in excinfo.traceback[1:-1]:
- entry.set_repr_style('short')
-
- def _repr_failure_py(self, excinfo, style="long"):
- if excinfo.errisinstance(pytest.fail.Exception):
- if not excinfo.value.pytrace:
- return py._builtin._totext(excinfo.value)
- return super(FunctionMixin, self)._repr_failure_py(excinfo,
- style=style)
-
- def repr_failure(self, excinfo, outerr=None):
- assert outerr is None, "XXX outerr usage is deprecated"
- style = self.config.option.tbstyle
- if style == "auto":
- style = "long"
- return self._repr_failure_py(excinfo, style=style)
-
-
-class Generator(FunctionMixin, PyCollector):
- def collect(self):
- # test generators are seen as collectors but they also
- # invoke setup/teardown on popular request
- # (induced by the common "test_*" naming shared with normal tests)
- self.session._setupstate.prepare(self)
- # see FunctionMixin.setup and test_setupstate_is_preserved_134
- self._preservedparent = self.parent.obj
- l = []
- seen = {}
- for i, x in enumerate(self.obj()):
- name, call, args = self.getcallargs(x)
- if not callable(call):
- raise TypeError("%r yielded non callable test %r" %(self.obj, call,))
- if name is None:
- name = "[%d]" % i
- else:
- name = "['%s']" % name
- if name in seen:
- raise ValueError("%r generated tests with non-unique name %r" %(self, name))
- seen[name] = True
- l.append(self.Function(name, self, args=args, callobj=call))
- return l
-
- def getcallargs(self, obj):
- if not isinstance(obj, (tuple, list)):
- obj = (obj,)
- # explict naming
- if isinstance(obj[0], py.builtin._basestring):
- name = obj[0]
- obj = obj[1:]
- else:
- name = None
- call, args = obj[0], obj[1:]
- return name, call, args
-
-
-def hasinit(obj):
- init = getattr(obj, '__init__', None)
- if init:
- if init != object.__init__:
- return True
-
-
-
-def fillfixtures(function):
- """ fill missing funcargs for a test function. """
- try:
- request = function._request
- except AttributeError:
- # XXX this special code path is only expected to execute
- # with the oejskit plugin. It uses classes with funcargs
- # and we thus have to work a bit to allow this.
- fm = function.session._fixturemanager
- fi = fm.getfixtureinfo(function.parent, function.obj, None)
- function._fixtureinfo = fi
- request = function._request = FixtureRequest(function)
- request._fillfixtures()
- # prune out funcargs for jstests
- newfuncargs = {}
- for name in fi.argnames:
- newfuncargs[name] = function.funcargs[name]
- function.funcargs = newfuncargs
- else:
- request._fillfixtures()
-
-
-_notexists = object()
-
-class CallSpec2(object):
- def __init__(self, metafunc):
- self.metafunc = metafunc
- self.funcargs = {}
- self._idlist = []
- self.params = {}
- self._globalid = _notexists
- self._globalid_args = set()
- self._globalparam = _notexists
- self._arg2scopenum = {} # used for sorting parametrized resources
- self.keywords = {}
- self.indices = {}
-
- def copy(self, metafunc):
- cs = CallSpec2(self.metafunc)
- cs.funcargs.update(self.funcargs)
- cs.params.update(self.params)
- cs.keywords.update(self.keywords)
- cs.indices.update(self.indices)
- cs._arg2scopenum.update(self._arg2scopenum)
- cs._idlist = list(self._idlist)
- cs._globalid = self._globalid
- cs._globalid_args = self._globalid_args
- cs._globalparam = self._globalparam
- return cs
-
- def _checkargnotcontained(self, arg):
- if arg in self.params or arg in self.funcargs:
- raise ValueError("duplicate %r" %(arg,))
-
- def getparam(self, name):
- try:
- return self.params[name]
- except KeyError:
- if self._globalparam is _notexists:
- raise ValueError(name)
- return self._globalparam
-
- @property
- def id(self):
- return "-".join(map(str, filter(None, self._idlist)))
-
- def setmulti(self, valtypes, argnames, valset, id, keywords, scopenum,
- param_index):
- for arg,val in zip(argnames, valset):
- self._checkargnotcontained(arg)
- valtype_for_arg = valtypes[arg]
- getattr(self, valtype_for_arg)[arg] = val
- self.indices[arg] = param_index
- self._arg2scopenum[arg] = scopenum
- if val is _notexists:
- self._emptyparamspecified = True
- self._idlist.append(id)
- self.keywords.update(keywords)
-
- def setall(self, funcargs, id, param):
- for x in funcargs:
- self._checkargnotcontained(x)
- self.funcargs.update(funcargs)
- if id is not _notexists:
- self._idlist.append(id)
- if param is not _notexists:
- assert self._globalparam is _notexists
- self._globalparam = param
- for arg in funcargs:
- self._arg2scopenum[arg] = scopenum_function
-
-
-class FuncargnamesCompatAttr:
- """ helper class so that Metafunc, Function and FixtureRequest
- don't need to each define the "funcargnames" compatibility attribute.
- """
- @property
- def funcargnames(self):
- """ alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
- return self.fixturenames
-
-class Metafunc(FuncargnamesCompatAttr):
- """
- Metafunc objects are passed to the ``pytest_generate_tests`` hook.
- They help to inspect a test function and to generate tests according to
- test configuration or values specified in the class or module where a
- test function is defined.
-
- :ivar fixturenames: set of fixture names required by the test function
-
- :ivar function: underlying python test function
-
- :ivar cls: class object where the test function is defined in or ``None``.
-
- :ivar module: the module object where the test function is defined in.
-
- :ivar config: access to the :class:`_pytest.config.Config` object for the
- test session.
-
- :ivar funcargnames:
- .. deprecated:: 2.3
- Use ``fixturenames`` instead.
- """
- def __init__(self, function, fixtureinfo, config, cls=None, module=None):
- self.config = config
- self.module = module
- self.function = function
- self.fixturenames = fixtureinfo.names_closure
- self._arg2fixturedefs = fixtureinfo.name2fixturedefs
- self.cls = cls
- self._calls = []
- self._ids = py.builtin.set()
-
- def parametrize(self, argnames, argvalues, indirect=False, ids=None,
- scope=None):
- """ Add new invocations to the underlying test function using the list
- of argvalues for the given argnames. Parametrization is performed
- during the collection phase. If you need to setup expensive resources
- see about setting indirect to do it rather at test setup time.
-
- :arg argnames: a comma-separated string denoting one or more argument
- names, or a list/tuple of argument strings.
-
- :arg argvalues: The list of argvalues determines how often a
- test is invoked with different argument values. If only one
- argname was specified argvalues is a list of values. If N
- argnames were specified, argvalues must be a list of N-tuples,
- where each tuple-element specifies a value for its respective
- argname.
-
- :arg indirect: The list of argnames or boolean. A list of arguments'
- names (subset of argnames). If True the list contains all names from
- the argnames. Each argvalue corresponding to an argname in this list will
- be passed as request.param to its respective argname fixture
- function so that it can perform more expensive setups during the
- setup phase of a test rather than at collection time.
-
- :arg ids: list of string ids, or a callable.
- If strings, each is corresponding to the argvalues so that they are
- part of the test id.
- If callable, it should take one argument (a single argvalue) and return
- a string or return None. If None, the automatically generated id for that
- argument will be used.
- If no ids are provided they will be generated automatically from
- the argvalues.
-
- :arg scope: if specified it denotes the scope of the parameters.
- The scope is used for grouping tests by parameter instances.
- It will also override any fixture-function defined scope, allowing
- to set a dynamic scope using test context or configuration.
- """
-
- # individual parametrized argument sets can be wrapped in a series
- # of markers in which case we unwrap the values and apply the mark
- # at Function init
- newkeywords = {}
- unwrapped_argvalues = []
- for i, argval in enumerate(argvalues):
- while isinstance(argval, MarkDecorator):
- newmark = MarkDecorator(argval.markname,
- argval.args[:-1], argval.kwargs)
- newmarks = newkeywords.setdefault(i, {})
- newmarks[newmark.markname] = newmark
- argval = argval.args[-1]
- unwrapped_argvalues.append(argval)
- argvalues = unwrapped_argvalues
-
- if not isinstance(argnames, (tuple, list)):
- argnames = [x.strip() for x in argnames.split(",") if x.strip()]
- if len(argnames) == 1:
- argvalues = [(val,) for val in argvalues]
- if not argvalues:
- argvalues = [(_notexists,) * len(argnames)]
-
- if scope is None:
- scope = "function"
- scopenum = scopes.index(scope)
- valtypes = {}
- for arg in argnames:
- if arg not in self.fixturenames:
- raise ValueError("%r uses no fixture %r" %(self.function, arg))
-
- if indirect is True:
- valtypes = dict.fromkeys(argnames, "params")
- elif indirect is False:
- valtypes = dict.fromkeys(argnames, "funcargs")
- elif isinstance(indirect, (tuple, list)):
- valtypes = dict.fromkeys(argnames, "funcargs")
- for arg in indirect:
- if arg not in argnames:
- raise ValueError("indirect given to %r: fixture %r doesn't exist" %(
- self.function, arg))
- valtypes[arg] = "params"
- idfn = None
- if callable(ids):
- idfn = ids
- ids = None
- if ids and len(ids) != len(argvalues):
- raise ValueError('%d tests specified with %d ids' %(
- len(argvalues), len(ids)))
- if not ids:
- ids = idmaker(argnames, argvalues, idfn)
- newcalls = []
- for callspec in self._calls or [CallSpec2(self)]:
- for param_index, valset in enumerate(argvalues):
- assert len(valset) == len(argnames)
- newcallspec = callspec.copy(self)
- newcallspec.setmulti(valtypes, argnames, valset, ids[param_index],
- newkeywords.get(param_index, {}), scopenum,
- param_index)
- newcalls.append(newcallspec)
- self._calls = newcalls
-
- def addcall(self, funcargs=None, id=_notexists, param=_notexists):
- """ (deprecated, use parametrize) Add a new call to the underlying
- test function during the collection phase of a test run. Note that
- request.addcall() is called during the test collection phase prior and
- independently to actual test execution. You should only use addcall()
- if you need to specify multiple arguments of a test function.
-
- :arg funcargs: argument keyword dictionary used when invoking
- the test function.
-
- :arg id: used for reporting and identification purposes. If you
- don't supply an `id` an automatic unique id will be generated.
-
- :arg param: a parameter which will be exposed to a later fixture function
- invocation through the ``request.param`` attribute.
- """
- assert funcargs is None or isinstance(funcargs, dict)
- if funcargs is not None:
- for name in funcargs:
- if name not in self.fixturenames:
- pytest.fail("funcarg %r not used in this function." % name)
- else:
- funcargs = {}
- if id is None:
- raise ValueError("id=None not allowed")
- if id is _notexists:
- id = len(self._calls)
- id = str(id)
- if id in self._ids:
- raise ValueError("duplicate id %r" % id)
- self._ids.add(id)
-
- cs = CallSpec2(self)
- cs.setall(funcargs, id, param)
- self._calls.append(cs)
-
-
-if _PY3:
- import codecs
-
- def _escape_bytes(val):
- """
- If val is pure ascii, returns it as a str(), otherwise escapes
- into a sequence of escaped bytes:
- b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
-
- note:
- the obvious "v.decode('unicode-escape')" will return
- valid utf-8 unicode if it finds them in the string, but we
- want to return escaped bytes for any byte, even if they match
- a utf-8 string.
- """
- if val:
- # source: http://goo.gl/bGsnwC
- encoded_bytes, _ = codecs.escape_encode(val)
- return encoded_bytes.decode('ascii')
- else:
- # empty bytes crashes codecs.escape_encode (#1087)
- return ''
-else:
- def _escape_bytes(val):
- """
- In py2 bytes and str are the same type, so return it unchanged if it
- is a full ascii string, otherwise escape it into its binary form.
- """
- try:
- return val.decode('ascii')
- except UnicodeDecodeError:
- return val.encode('string-escape')
-
-
-def _idval(val, argname, idx, idfn):
- if idfn:
- try:
- s = idfn(val)
- if s:
- return s
- except Exception:
- pass
-
- if isinstance(val, bytes):
- return _escape_bytes(val)
- elif isinstance(val, (float, int, str, bool, NoneType)):
- return str(val)
- elif isinstance(val, REGEX_TYPE):
- return _escape_bytes(val.pattern) if isinstance(val.pattern, bytes) else val.pattern
- elif enum is not None and isinstance(val, enum.Enum):
- return str(val)
- elif isclass(val) and hasattr(val, '__name__'):
- return val.__name__
- elif _PY2 and isinstance(val, unicode):
- # special case for python 2: if a unicode string is
- # convertible to ascii, return it as an str() object instead
- try:
- return str(val)
- except UnicodeError:
- # fallthrough
- pass
- return str(argname)+str(idx)
-
-def _idvalset(idx, valset, argnames, idfn):
- this_id = [_idval(val, argname, idx, idfn)
- for val, argname in zip(valset, argnames)]
- return "-".join(this_id)
-
-def idmaker(argnames, argvalues, idfn=None):
- ids = [_idvalset(valindex, valset, argnames, idfn)
- for valindex, valset in enumerate(argvalues)]
- if len(set(ids)) < len(ids):
- # user may have provided a bad idfn which means the ids are not unique
- ids = [str(i) + testid for i, testid in enumerate(ids)]
- return ids
-
-def showfixtures(config):
- from _pytest.main import wrap_session
- return wrap_session(config, _showfixtures_main)
-
-def _showfixtures_main(config, session):
- import _pytest.config
- session.perform_collect()
- curdir = py.path.local()
- tw = _pytest.config.create_terminal_writer(config)
- verbose = config.getvalue("verbose")
-
- fm = session._fixturemanager
-
- available = []
- for argname, fixturedefs in fm._arg2fixturedefs.items():
- assert fixturedefs is not None
- if not fixturedefs:
- continue
- fixturedef = fixturedefs[-1]
- loc = getlocation(fixturedef.func, curdir)
- available.append((len(fixturedef.baseid),
- fixturedef.func.__module__,
- curdir.bestrelpath(loc),
- fixturedef.argname, fixturedef))
-
- available.sort()
- currentmodule = None
- for baseid, module, bestrel, argname, fixturedef in available:
- if currentmodule != module:
- if not module.startswith("_pytest."):
- tw.line()
- tw.sep("-", "fixtures defined from %s" %(module,))
- currentmodule = module
- if verbose <= 0 and argname[0] == "_":
- continue
- if verbose > 0:
- funcargspec = "%s -- %s" %(argname, bestrel,)
- else:
- funcargspec = argname
- tw.line(funcargspec, green=True)
- loc = getlocation(fixturedef.func, curdir)
- doc = fixturedef.func.__doc__ or ""
- if doc:
- for line in doc.strip().split("\n"):
- tw.line(" " + line.strip())
- else:
- tw.line(" %s: no docstring available" %(loc,),
- red=True)
-
-def getlocation(function, curdir):
- import inspect
- fn = py.path.local(inspect.getfile(function))
- lineno = py.builtin._getcode(function).co_firstlineno
- if fn.relto(curdir):
- fn = fn.relto(curdir)
- return "%s:%d" %(fn, lineno+1)
-
-# builtin pytest.raises helper
-
-def raises(expected_exception, *args, **kwargs):
- """ assert that a code block/function call raises ``expected_exception``
- and raise a failure exception otherwise.
-
- This helper produces a ``ExceptionInfo()`` object (see below).
-
- If using Python 2.5 or above, you may use this function as a
- context manager::
-
- >>> with raises(ZeroDivisionError):
- ... 1/0
-
- .. note::
-
- When using ``pytest.raises`` as a context manager, it's worthwhile to
- note that normal context manager rules apply and that the exception
- raised *must* be the final line in the scope of the context manager.
- Lines of code after that, within the scope of the context manager will
- not be executed. For example::
-
- >>> with raises(OSError) as exc_info:
- assert 1 == 1 # this will execute as expected
- raise OSError(errno.EEXISTS, 'directory exists')
- assert exc_info.value.errno == errno.EEXISTS # this will not execute
-
- Instead, the following approach must be taken (note the difference in
- scope)::
-
- >>> with raises(OSError) as exc_info:
- assert 1 == 1 # this will execute as expected
- raise OSError(errno.EEXISTS, 'directory exists')
-
- assert exc_info.value.errno == errno.EEXISTS # this will now execute
-
- Or you can specify a callable by passing a to-be-called lambda::
-
- >>> raises(ZeroDivisionError, lambda: 1/0)
- <ExceptionInfo ...>
-
- or you can specify an arbitrary callable with arguments::
-
- >>> def f(x): return 1/x
- ...
- >>> raises(ZeroDivisionError, f, 0)
- <ExceptionInfo ...>
- >>> raises(ZeroDivisionError, f, x=0)
- <ExceptionInfo ...>
-
- A third possibility is to use a string to be executed::
-
- >>> raises(ZeroDivisionError, "f(0)")
- <ExceptionInfo ...>
-
- .. autoclass:: _pytest._code.ExceptionInfo
- :members:
-
- .. note::
- Similar to caught exception objects in Python, explicitly clearing
- local references to returned ``ExceptionInfo`` objects can
- help the Python interpreter speed up its garbage collection.
-
- Clearing those references breaks a reference cycle
- (``ExceptionInfo`` --> caught exception --> frame stack raising
- the exception --> current frame stack --> local variables -->
- ``ExceptionInfo``) which makes Python keep all objects referenced
- from that cycle (including all local variables in the current
- frame) alive until the next cyclic garbage collection run. See the
- official Python ``try`` statement documentation for more detailed
- information.
-
- """
- __tracebackhide__ = True
- if expected_exception is AssertionError:
- # we want to catch a AssertionError
- # replace our subclass with the builtin one
- # see https://github.com/pytest-dev/pytest/issues/176
- from _pytest.assertion.util import BuiltinAssertionError \
- as expected_exception
- msg = ("exceptions must be old-style classes or"
- " derived from BaseException, not %s")
- if isinstance(expected_exception, tuple):
- for exc in expected_exception:
- if not isclass(exc):
- raise TypeError(msg % type(exc))
- elif not isclass(expected_exception):
- raise TypeError(msg % type(expected_exception))
-
- if not args:
- return RaisesContext(expected_exception)
- elif isinstance(args[0], str):
- code, = args
- assert isinstance(code, str)
- frame = sys._getframe(1)
- loc = frame.f_locals.copy()
- loc.update(kwargs)
- #print "raises frame scope: %r" % frame.f_locals
- try:
- code = _pytest._code.Source(code).compile()
- py.builtin.exec_(code, frame.f_globals, loc)
- # XXX didn'T mean f_globals == f_locals something special?
- # this is destroyed here ...
- except expected_exception:
- return _pytest._code.ExceptionInfo()
- else:
- func = args[0]
- try:
- func(*args[1:], **kwargs)
- except expected_exception:
- return _pytest._code.ExceptionInfo()
- pytest.fail("DID NOT RAISE {0}".format(expected_exception))
-
-class RaisesContext(object):
- def __init__(self, expected_exception):
- self.expected_exception = expected_exception
- self.excinfo = None
-
- def __enter__(self):
- self.excinfo = object.__new__(_pytest._code.ExceptionInfo)
- return self.excinfo
-
- def __exit__(self, *tp):
- __tracebackhide__ = True
- if tp[0] is None:
- pytest.fail("DID NOT RAISE")
- if sys.version_info < (2, 7):
- # py26: on __exit__() exc_value often does not contain the
- # exception value.
- # http://bugs.python.org/issue7853
- if not isinstance(tp[1], BaseException):
- exc_type, value, traceback = tp
- tp = exc_type, exc_type(value), traceback
- self.excinfo.__init__(tp)
- return issubclass(self.excinfo.type, self.expected_exception)
-
-#
-# the basic pytest Function item
-#
-
-class Function(FunctionMixin, pytest.Item, FuncargnamesCompatAttr):
- """ a Function Item is responsible for setting up and executing a
- Python test function.
- """
- _genid = None
- def __init__(self, name, parent, args=None, config=None,
- callspec=None, callobj=NOTSET, keywords=None, session=None,
- fixtureinfo=None):
- super(Function, self).__init__(name, parent, config=config,
- session=session)
- self._args = args
- if callobj is not NOTSET:
- self.obj = callobj
-
- self.keywords.update(self.obj.__dict__)
- if callspec:
- self.callspec = callspec
- self.keywords.update(callspec.keywords)
- if keywords:
- self.keywords.update(keywords)
-
- if fixtureinfo is None:
- fixtureinfo = self.session._fixturemanager.getfixtureinfo(
- self.parent, self.obj, self.cls,
- funcargs=not self._isyieldedfunction())
- self._fixtureinfo = fixtureinfo
- self.fixturenames = fixtureinfo.names_closure
- self._initrequest()
-
- def _initrequest(self):
- self.funcargs = {}
- if self._isyieldedfunction():
- assert not hasattr(self, "callspec"), (
- "yielded functions (deprecated) cannot have funcargs")
- else:
- if hasattr(self, "callspec"):
- callspec = self.callspec
- assert not callspec.funcargs
- self._genid = callspec.id
- if hasattr(callspec, "param"):
- self.param = callspec.param
- self._request = FixtureRequest(self)
-
- @property
- def function(self):
- "underlying python 'function' object"
- return getattr(self.obj, 'im_func', self.obj)
-
- def _getobj(self):
- name = self.name
- i = name.find("[") # parametrization
- if i != -1:
- name = name[:i]
- return getattr(self.parent.obj, name)
-
- @property
- def _pyfuncitem(self):
- "(compatonly) for code expecting pytest-2.2 style request objects"
- return self
-
- def _isyieldedfunction(self):
- return getattr(self, "_args", None) is not None
-
- def runtest(self):
- """ execute the underlying test function. """
- self.ihook.pytest_pyfunc_call(pyfuncitem=self)
-
- def setup(self):
- # check if parametrization happend with an empty list
- try:
- self.callspec._emptyparamspecified
- except AttributeError:
- pass
- else:
- fs, lineno = self._getfslineno()
- pytest.skip("got empty parameter set, function %s at %s:%d" %(
- self.function.__name__, fs, lineno))
- super(Function, self).setup()
- fillfixtures(self)
-
-
-scope2props = dict(session=())
-scope2props["module"] = ("fspath", "module")
-scope2props["class"] = scope2props["module"] + ("cls",)
-scope2props["instance"] = scope2props["class"] + ("instance", )
-scope2props["function"] = scope2props["instance"] + ("function", "keywords")
-
-def scopeproperty(name=None, doc=None):
- def decoratescope(func):
- scopename = name or func.__name__
- def provide(self):
- if func.__name__ in scope2props[self.scope]:
- return func(self)
- raise AttributeError("%s not available in %s-scoped context" % (
- scopename, self.scope))
- return property(provide, None, None, func.__doc__)
- return decoratescope
-
-
-class FixtureRequest(FuncargnamesCompatAttr):
- """ A request for a fixture from a test or fixture function.
-
- A request object gives access to the requesting test context
- and has an optional ``param`` attribute in case
- the fixture is parametrized indirectly.
- """
-
- def __init__(self, pyfuncitem):
- self._pyfuncitem = pyfuncitem
- #: fixture for which this request is being performed
- self.fixturename = None
- #: Scope string, one of "function", "cls", "module", "session"
- self.scope = "function"
- self._funcargs = {}
- self._fixturedefs = {}
- fixtureinfo = pyfuncitem._fixtureinfo
- self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
- self._arg2index = {}
- self.fixturenames = fixtureinfo.names_closure
- self._fixturemanager = pyfuncitem.session._fixturemanager
-
- @property
- def node(self):
- """ underlying collection node (depends on current request scope)"""
- return self._getscopeitem(self.scope)
-
-
- def _getnextfixturedef(self, argname):
- fixturedefs = self._arg2fixturedefs.get(argname, None)
- if fixturedefs is None:
- # we arrive here because of a a dynamic call to
- # getfuncargvalue(argname) usage which was naturally
- # not known at parsing/collection time
- fixturedefs = self._fixturemanager.getfixturedefs(
- argname, self._pyfuncitem.parent.nodeid)
- self._arg2fixturedefs[argname] = fixturedefs
- # fixturedefs list is immutable so we maintain a decreasing index
- index = self._arg2index.get(argname, 0) - 1
- if fixturedefs is None or (-index > len(fixturedefs)):
- raise FixtureLookupError(argname, self)
- self._arg2index[argname] = index
- return fixturedefs[index]
-
- @property
- def config(self):
- """ the pytest config object associated with this request. """
- return self._pyfuncitem.config
-
-
- @scopeproperty()
- def function(self):
- """ test function object if the request has a per-function scope. """
- return self._pyfuncitem.obj
-
- @scopeproperty("class")
- def cls(self):
- """ class (can be None) where the test function was collected. """
- clscol = self._pyfuncitem.getparent(pytest.Class)
- if clscol:
- return clscol.obj
-
- @property
- def instance(self):
- """ instance (can be None) on which test function was collected. """
- # unittest support hack, see _pytest.unittest.TestCaseFunction
- try:
- return self._pyfuncitem._testcase
- except AttributeError:
- function = getattr(self, "function", None)
- if function is not None:
- return py.builtin._getimself(function)
-
- @scopeproperty()
- def module(self):
- """ python module object where the test function was collected. """
- return self._pyfuncitem.getparent(pytest.Module).obj
-
- @scopeproperty()
- def fspath(self):
- """ the file system path of the test module which collected this test. """
- return self._pyfuncitem.fspath
-
- @property
- def keywords(self):
- """ keywords/markers dictionary for the underlying node. """
- return self.node.keywords
-
- @property
- def session(self):
- """ pytest session object. """
- return self._pyfuncitem.session
-
- def addfinalizer(self, finalizer):
- """ add finalizer/teardown function to be called after the
- last test within the requesting test context finished
- execution. """
- # XXX usually this method is shadowed by fixturedef specific ones
- self._addfinalizer(finalizer, scope=self.scope)
-
- def _addfinalizer(self, finalizer, scope):
- colitem = self._getscopeitem(scope)
- self._pyfuncitem.session._setupstate.addfinalizer(
- finalizer=finalizer, colitem=colitem)
-
- def applymarker(self, marker):
- """ Apply a marker to a single test function invocation.
- This method is useful if you don't want to have a keyword/marker
- on all function invocations.
-
- :arg marker: a :py:class:`_pytest.mark.MarkDecorator` object
- created by a call to ``pytest.mark.NAME(...)``.
- """
- try:
- self.node.keywords[marker.markname] = marker
- except AttributeError:
- raise ValueError(marker)
-
- def raiseerror(self, msg):
- """ raise a FixtureLookupError with the given message. """
- raise self._fixturemanager.FixtureLookupError(None, self, msg)
-
- def _fillfixtures(self):
- item = self._pyfuncitem
- fixturenames = getattr(item, "fixturenames", self.fixturenames)
- for argname in fixturenames:
- if argname not in item.funcargs:
- item.funcargs[argname] = self.getfuncargvalue(argname)
-
- def cached_setup(self, setup, teardown=None, scope="module", extrakey=None):
- """ (deprecated) Return a testing resource managed by ``setup`` &
- ``teardown`` calls. ``scope`` and ``extrakey`` determine when the
- ``teardown`` function will be called so that subsequent calls to
- ``setup`` would recreate the resource. With pytest-2.3 you often
- do not need ``cached_setup()`` as you can directly declare a scope
- on a fixture function and register a finalizer through
- ``request.addfinalizer()``.
-
- :arg teardown: function receiving a previously setup resource.
- :arg setup: a no-argument function creating a resource.
- :arg scope: a string value out of ``function``, ``class``, ``module``
- or ``session`` indicating the caching lifecycle of the resource.
- :arg extrakey: added to internal caching key of (funcargname, scope).
- """
- if not hasattr(self.config, '_setupcache'):
- self.config._setupcache = {} # XXX weakref?
- cachekey = (self.fixturename, self._getscopeitem(scope), extrakey)
- cache = self.config._setupcache
- try:
- val = cache[cachekey]
- except KeyError:
- self._check_scope(self.fixturename, self.scope, scope)
- val = setup()
- cache[cachekey] = val
- if teardown is not None:
- def finalizer():
- del cache[cachekey]
- teardown(val)
- self._addfinalizer(finalizer, scope=scope)
- return val
-
- def getfuncargvalue(self, argname):
- """ Dynamically retrieve a named fixture function argument.
-
- As of pytest-2.3, it is easier and usually better to access other
- fixture values by stating it as an input argument in the fixture
- function. If you only can decide about using another fixture at test
- setup time, you may use this function to retrieve it inside a fixture
- function body.
- """
- return self._get_active_fixturedef(argname).cached_result[0]
-
- def _get_active_fixturedef(self, argname):
- try:
- return self._fixturedefs[argname]
- except KeyError:
- try:
- fixturedef = self._getnextfixturedef(argname)
- except FixtureLookupError:
- if argname == "request":
- class PseudoFixtureDef:
- cached_result = (self, [0], None)
- scope = "function"
- return PseudoFixtureDef
- raise
- # remove indent to prevent the python3 exception
- # from leaking into the call
- result = self._getfuncargvalue(fixturedef)
- self._funcargs[argname] = result
- self._fixturedefs[argname] = fixturedef
- return fixturedef
-
- def _get_fixturestack(self):
- current = self
- l = []
- while 1:
- fixturedef = getattr(current, "_fixturedef", None)
- if fixturedef is None:
- l.reverse()
- return l
- l.append(fixturedef)
- current = current._parent_request
-
- def _getfuncargvalue(self, fixturedef):
- # prepare a subrequest object before calling fixture function
- # (latter managed by fixturedef)
- argname = fixturedef.argname
- funcitem = self._pyfuncitem
- scope = fixturedef.scope
- try:
- param = funcitem.callspec.getparam(argname)
- except (AttributeError, ValueError):
- param = NOTSET
- param_index = 0
- else:
- # indices might not be set if old-style metafunc.addcall() was used
- param_index = funcitem.callspec.indices.get(argname, 0)
- # if a parametrize invocation set a scope it will override
- # the static scope defined with the fixture function
- paramscopenum = funcitem.callspec._arg2scopenum.get(argname)
- if paramscopenum is not None:
- scope = scopes[paramscopenum]
-
- subrequest = SubRequest(self, scope, param, param_index, fixturedef)
-
- # check if a higher-level scoped fixture accesses a lower level one
- subrequest._check_scope(argname, self.scope, scope)
-
- # clear sys.exc_info before invoking the fixture (python bug?)
- # if its not explicitly cleared it will leak into the call
- exc_clear()
- try:
- # call the fixture function
- val = fixturedef.execute(request=subrequest)
- finally:
- # if fixture function failed it might have registered finalizers
- self.session._setupstate.addfinalizer(fixturedef.finish,
- subrequest.node)
- return val
-
- def _check_scope(self, argname, invoking_scope, requested_scope):
- if argname == "request":
- return
- if scopemismatch(invoking_scope, requested_scope):
- # try to report something helpful
- lines = self._factorytraceback()
- pytest.fail("ScopeMismatch: You tried to access the %r scoped "
- "fixture %r with a %r scoped request object, "
- "involved factories\n%s" %(
- (requested_scope, argname, invoking_scope, "\n".join(lines))),
- pytrace=False)
-
- def _factorytraceback(self):
- lines = []
- for fixturedef in self._get_fixturestack():
- factory = fixturedef.func
- fs, lineno = getfslineno(factory)
- p = self._pyfuncitem.session.fspath.bestrelpath(fs)
- args = _format_args(factory)
- lines.append("%s:%d: def %s%s" %(
- p, lineno, factory.__name__, args))
- return lines
-
- def _getscopeitem(self, scope):
- if scope == "function":
- # this might also be a non-function Item despite its attribute name
- return self._pyfuncitem
- node = get_scope_node(self._pyfuncitem, scope)
- if node is None and scope == "class":
- # fallback to function item itself
- node = self._pyfuncitem
- assert node
- return node
-
- def __repr__(self):
- return "<FixtureRequest for %r>" %(self.node)
-
-
-class SubRequest(FixtureRequest):
- """ a sub request for handling getting a fixture from a
- test function/fixture. """
- def __init__(self, request, scope, param, param_index, fixturedef):
- self._parent_request = request
- self.fixturename = fixturedef.argname
- if param is not NOTSET:
- self.param = param
- self.param_index = param_index
- self.scope = scope
- self._fixturedef = fixturedef
- self.addfinalizer = fixturedef.addfinalizer
- self._pyfuncitem = request._pyfuncitem
- self._funcargs = request._funcargs
- self._fixturedefs = request._fixturedefs
- self._arg2fixturedefs = request._arg2fixturedefs
- self._arg2index = request._arg2index
- self.fixturenames = request.fixturenames
- self._fixturemanager = request._fixturemanager
-
- def __repr__(self):
- return "<SubRequest %r for %r>" % (self.fixturename, self._pyfuncitem)
-
-
-class ScopeMismatchError(Exception):
- """ A fixture function tries to use a different fixture function which
- which has a lower scope (e.g. a Session one calls a function one)
- """
-
-scopes = "session module class function".split()
-scopenum_function = scopes.index("function")
-def scopemismatch(currentscope, newscope):
- return scopes.index(newscope) > scopes.index(currentscope)
-
-
-class FixtureLookupError(LookupError):
- """ could not return a requested Fixture (missing or invalid). """
- def __init__(self, argname, request, msg=None):
- self.argname = argname
- self.request = request
- self.fixturestack = request._get_fixturestack()
- self.msg = msg
-
- def formatrepr(self):
- tblines = []
- addline = tblines.append
- stack = [self.request._pyfuncitem.obj]
- stack.extend(map(lambda x: x.func, self.fixturestack))
- msg = self.msg
- if msg is not None:
- # the last fixture raise an error, let's present
- # it at the requesting side
- stack = stack[:-1]
- for function in stack:
- fspath, lineno = getfslineno(function)
- try:
- lines, _ = inspect.getsourcelines(get_real_func(function))
- except (IOError, IndexError):
- error_msg = "file %s, line %s: source code not available"
- addline(error_msg % (fspath, lineno+1))
- else:
- addline("file %s, line %s" % (fspath, lineno+1))
- for i, line in enumerate(lines):
- line = line.rstrip()
- addline(" " + line)
- if line.lstrip().startswith('def'):
- break
-
- if msg is None:
- fm = self.request._fixturemanager
- available = []
- for name, fixturedef in fm._arg2fixturedefs.items():
- parentid = self.request._pyfuncitem.parent.nodeid
- faclist = list(fm._matchfactories(fixturedef, parentid))
- if faclist:
- available.append(name)
- msg = "fixture %r not found" % (self.argname,)
- msg += "\n available fixtures: %s" %(", ".join(available),)
- msg += "\n use 'py.test --fixtures [testpath]' for help on them."
-
- return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname)
-
-class FixtureLookupErrorRepr(TerminalRepr):
- def __init__(self, filename, firstlineno, tblines, errorstring, argname):
- self.tblines = tblines
- self.errorstring = errorstring
- self.filename = filename
- self.firstlineno = firstlineno
- self.argname = argname
-
- def toterminal(self, tw):
- #tw.line("FixtureLookupError: %s" %(self.argname), red=True)
- for tbline in self.tblines:
- tw.line(tbline.rstrip())
- for line in self.errorstring.split("\n"):
- tw.line(" " + line.strip(), red=True)
- tw.line()
- tw.line("%s:%d" % (self.filename, self.firstlineno+1))
-
-class FixtureManager:
- """
- pytest fixtures definitions and information is stored and managed
- from this class.
-
- During collection fm.parsefactories() is called multiple times to parse
- fixture function definitions into FixtureDef objects and internal
- data structures.
-
- During collection of test functions, metafunc-mechanics instantiate
- a FuncFixtureInfo object which is cached per node/func-name.
- This FuncFixtureInfo object is later retrieved by Function nodes
- which themselves offer a fixturenames attribute.
-
- The FuncFixtureInfo object holds information about fixtures and FixtureDefs
- relevant for a particular function. An initial list of fixtures is
- assembled like this:
-
- - ini-defined usefixtures
- - autouse-marked fixtures along the collection chain up from the function
- - usefixtures markers at module/class/function level
- - test function funcargs
-
- Subsequently the funcfixtureinfo.fixturenames attribute is computed
- as the closure of the fixtures needed to setup the initial fixtures,
- i. e. fixtures needed by fixture functions themselves are appended
- to the fixturenames list.
-
- Upon the test-setup phases all fixturenames are instantiated, retrieved
- by a lookup of their FuncFixtureInfo.
- """
-
- _argprefix = "pytest_funcarg__"
- FixtureLookupError = FixtureLookupError
- FixtureLookupErrorRepr = FixtureLookupErrorRepr
-
- def __init__(self, session):
- self.session = session
- self.config = session.config
- self._arg2fixturedefs = {}
- self._holderobjseen = set()
- self._arg2finish = {}
- self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))]
- session.config.pluginmanager.register(self, "funcmanage")
-
-
- def getfixtureinfo(self, node, func, cls, funcargs=True):
- if funcargs and not hasattr(node, "nofuncargs"):
- if cls is not None:
- startindex = 1
- else:
- startindex = None
- argnames = getfuncargnames(func, startindex)
- else:
- argnames = ()
- usefixtures = getattr(func, "usefixtures", None)
- initialnames = argnames
- if usefixtures is not None:
- initialnames = usefixtures.args + initialnames
- fm = node.session._fixturemanager
- names_closure, arg2fixturedefs = fm.getfixtureclosure(initialnames,
- node)
- return FuncFixtureInfo(argnames, names_closure, arg2fixturedefs)
-
- def pytest_plugin_registered(self, plugin):
- nodeid = None
- try:
- p = py.path.local(plugin.__file__)
- except AttributeError:
- pass
- else:
- # construct the base nodeid which is later used to check
- # what fixtures are visible for particular tests (as denoted
- # by their test id)
- if p.basename.startswith("conftest.py"):
- nodeid = p.dirpath().relto(self.config.rootdir)
- if p.sep != "/":
- nodeid = nodeid.replace(p.sep, "/")
- self.parsefactories(plugin, nodeid)
-
- def _getautousenames(self, nodeid):
- """ return a tuple of fixture names to be used. """
- autousenames = []
- for baseid, basenames in self._nodeid_and_autousenames:
- if nodeid.startswith(baseid):
- if baseid:
- i = len(baseid)
- nextchar = nodeid[i:i+1]
- if nextchar and nextchar not in ":/":
- continue
- autousenames.extend(basenames)
- # make sure autousenames are sorted by scope, scopenum 0 is session
- autousenames.sort(
- key=lambda x: self._arg2fixturedefs[x][-1].scopenum)
- return autousenames
-
- def getfixtureclosure(self, fixturenames, parentnode):
- # collect the closure of all fixtures , starting with the given
- # fixturenames as the initial set. As we have to visit all
- # factory definitions anyway, we also return a arg2fixturedefs
- # mapping so that the caller can reuse it and does not have
- # to re-discover fixturedefs again for each fixturename
- # (discovering matching fixtures for a given name/node is expensive)
-
- parentid = parentnode.nodeid
- fixturenames_closure = self._getautousenames(parentid)
- def merge(otherlist):
- for arg in otherlist:
- if arg not in fixturenames_closure:
- fixturenames_closure.append(arg)
- merge(fixturenames)
- arg2fixturedefs = {}
- lastlen = -1
- while lastlen != len(fixturenames_closure):
- lastlen = len(fixturenames_closure)
- for argname in fixturenames_closure:
- if argname in arg2fixturedefs:
- continue
- fixturedefs = self.getfixturedefs(argname, parentid)
- if fixturedefs:
- arg2fixturedefs[argname] = fixturedefs
- merge(fixturedefs[-1].argnames)
- return fixturenames_closure, arg2fixturedefs
-
- def pytest_generate_tests(self, metafunc):
- for argname in metafunc.fixturenames:
- faclist = metafunc._arg2fixturedefs.get(argname)
- if faclist:
- fixturedef = faclist[-1]
- if fixturedef.params is not None:
- func_params = getattr(getattr(metafunc.function, 'parametrize', None), 'args', [[None]])
- # skip directly parametrized arguments
- argnames = func_params[0]
- if not isinstance(argnames, (tuple, list)):
- argnames = [x.strip() for x in argnames.split(",") if x.strip()]
- if argname not in func_params and argname not in argnames:
- metafunc.parametrize(argname, fixturedef.params,
- indirect=True, scope=fixturedef.scope,
- ids=fixturedef.ids)
- else:
- continue # will raise FixtureLookupError at setup time
-
- def pytest_collection_modifyitems(self, items):
- # separate parametrized setups
- items[:] = reorder_items(items)
-
- def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
- if nodeid is not NOTSET:
- holderobj = node_or_obj
- else:
- holderobj = node_or_obj.obj
- nodeid = node_or_obj.nodeid
- if holderobj in self._holderobjseen:
- return
- self._holderobjseen.add(holderobj)
- autousenames = []
- for name in dir(holderobj):
- obj = getattr(holderobj, name, None)
- # fixture functions have a pytest_funcarg__ prefix (pre-2.3 style)
- # or are "@pytest.fixture" marked
- marker = getfixturemarker(obj)
- if marker is None:
- if not name.startswith(self._argprefix):
- continue
- if not callable(obj):
- continue
- marker = defaultfuncargprefixmarker
- name = name[len(self._argprefix):]
- elif not isinstance(marker, FixtureFunctionMarker):
- # magic globals with __getattr__ might have got us a wrong
- # fixture attribute
- continue
- else:
- assert not name.startswith(self._argprefix)
- fixturedef = FixtureDef(self, nodeid, name, obj,
- marker.scope, marker.params,
- yieldctx=marker.yieldctx,
- unittest=unittest, ids=marker.ids)
- faclist = self._arg2fixturedefs.setdefault(name, [])
- if fixturedef.has_location:
- faclist.append(fixturedef)
- else:
- # fixturedefs with no location are at the front
- # so this inserts the current fixturedef after the
- # existing fixturedefs from external plugins but
- # before the fixturedefs provided in conftests.
- i = len([f for f in faclist if not f.has_location])
- faclist.insert(i, fixturedef)
- if marker.autouse:
- autousenames.append(name)
- if autousenames:
- self._nodeid_and_autousenames.append((nodeid or '', autousenames))
-
- def getfixturedefs(self, argname, nodeid):
- try:
- fixturedefs = self._arg2fixturedefs[argname]
- except KeyError:
- return None
- else:
- return tuple(self._matchfactories(fixturedefs, nodeid))
-
- def _matchfactories(self, fixturedefs, nodeid):
- for fixturedef in fixturedefs:
- if nodeid.startswith(fixturedef.baseid):
- yield fixturedef
-
-
-def fail_fixturefunc(fixturefunc, msg):
- fs, lineno = getfslineno(fixturefunc)
- location = "%s:%s" % (fs, lineno+1)
- source = _pytest._code.Source(fixturefunc)
- pytest.fail(msg + ":\n\n" + str(source.indent()) + "\n" + location,
- pytrace=False)
-
-def call_fixture_func(fixturefunc, request, kwargs, yieldctx):
- if yieldctx:
- if not is_generator(fixturefunc):
- fail_fixturefunc(fixturefunc,
- msg="yield_fixture requires yield statement in function")
- iter = fixturefunc(**kwargs)
- next = getattr(iter, "__next__", None)
- if next is None:
- next = getattr(iter, "next")
- res = next()
- def teardown():
- try:
- next()
- except StopIteration:
- pass
- else:
- fail_fixturefunc(fixturefunc,
- "yield_fixture function has more than one 'yield'")
- request.addfinalizer(teardown)
- else:
- if is_generator(fixturefunc):
- fail_fixturefunc(fixturefunc,
- msg="pytest.fixture functions cannot use ``yield``. "
- "Instead write and return an inner function/generator "
- "and let the consumer call and iterate over it.")
- res = fixturefunc(**kwargs)
- return res
-
-class FixtureDef:
- """ A container for a factory definition. """
- def __init__(self, fixturemanager, baseid, argname, func, scope, params,
- yieldctx, unittest=False, ids=None):
- self._fixturemanager = fixturemanager
- self.baseid = baseid or ''
- self.has_location = baseid is not None
- self.func = func
- self.argname = argname
- self.scope = scope
- self.scopenum = scopes.index(scope or "function")
- self.params = params
- startindex = unittest and 1 or None
- self.argnames = getfuncargnames(func, startindex=startindex)
- self.yieldctx = yieldctx
- self.unittest = unittest
- self.ids = ids
- self._finalizer = []
-
- def addfinalizer(self, finalizer):
- self._finalizer.append(finalizer)
-
- def finish(self):
- try:
- while self._finalizer:
- func = self._finalizer.pop()
- func()
- finally:
- # even if finalization fails, we invalidate
- # the cached fixture value
- if hasattr(self, "cached_result"):
- del self.cached_result
-
- def execute(self, request):
- # get required arguments and register our own finish()
- # with their finalization
- kwargs = {}
- for argname in self.argnames:
- fixturedef = request._get_active_fixturedef(argname)
- result, arg_cache_key, exc = fixturedef.cached_result
- request._check_scope(argname, request.scope, fixturedef.scope)
- kwargs[argname] = result
- if argname != "request":
- fixturedef.addfinalizer(self.finish)
-
- my_cache_key = request.param_index
- cached_result = getattr(self, "cached_result", None)
- if cached_result is not None:
- result, cache_key, err = cached_result
- if my_cache_key == cache_key:
- if err is not None:
- py.builtin._reraise(*err)
- else:
- return result
- # we have a previous but differently parametrized fixture instance
- # so we need to tear it down before creating a new one
- self.finish()
- assert not hasattr(self, "cached_result")
-
- fixturefunc = self.func
-
- if self.unittest:
- if request.instance is not None:
- # bind the unbound method to the TestCase instance
- fixturefunc = self.func.__get__(request.instance)
- else:
- # the fixture function needs to be bound to the actual
- # request.instance so that code working with "self" behaves
- # as expected.
- if request.instance is not None:
- fixturefunc = getimfunc(self.func)
- if fixturefunc != self.func:
- fixturefunc = fixturefunc.__get__(request.instance)
-
- try:
- result = call_fixture_func(fixturefunc, request, kwargs,
- self.yieldctx)
- except Exception:
- self.cached_result = (None, my_cache_key, sys.exc_info())
- raise
- self.cached_result = (result, my_cache_key, None)
- return result
-
- def __repr__(self):
- return ("<FixtureDef name=%r scope=%r baseid=%r >" %
- (self.argname, self.scope, self.baseid))
-
-def num_mock_patch_args(function):
- """ return number of arguments used up by mock arguments (if any) """
- patchings = getattr(function, "patchings", None)
- if not patchings:
- return 0
- mock = sys.modules.get("mock", sys.modules.get("unittest.mock", None))
- if mock is not None:
- return len([p for p in patchings
- if not p.attribute_name and p.new is mock.DEFAULT])
- return len(patchings)
-
-
-def getfuncargnames(function, startindex=None):
- # XXX merge with main.py's varnames
- #assert not isclass(function)
- realfunction = function
- while hasattr(realfunction, "__wrapped__"):
- realfunction = realfunction.__wrapped__
- if startindex is None:
- startindex = inspect.ismethod(function) and 1 or 0
- if realfunction != function:
- startindex += num_mock_patch_args(function)
- function = realfunction
- if isinstance(function, functools.partial):
- argnames = inspect.getargs(_pytest._code.getrawcode(function.func))[0]
- partial = function
- argnames = argnames[len(partial.args):]
- if partial.keywords:
- for kw in partial.keywords:
- argnames.remove(kw)
- else:
- argnames = inspect.getargs(_pytest._code.getrawcode(function))[0]
- defaults = getattr(function, 'func_defaults',
- getattr(function, '__defaults__', None)) or ()
- numdefaults = len(defaults)
- if numdefaults:
- return tuple(argnames[startindex:-numdefaults])
- return tuple(argnames[startindex:])
-
-# algorithm for sorting on a per-parametrized resource setup basis
-# it is called for scopenum==0 (session) first and performs sorting
-# down to the lower scopes such as to minimize number of "high scope"
-# setups and teardowns
-
-def reorder_items(items):
- argkeys_cache = {}
- for scopenum in range(0, scopenum_function):
- argkeys_cache[scopenum] = d = {}
- for item in items:
- keys = set(get_parametrized_fixture_keys(item, scopenum))
- if keys:
- d[item] = keys
- return reorder_items_atscope(items, set(), argkeys_cache, 0)
-
-def reorder_items_atscope(items, ignore, argkeys_cache, scopenum):
- if scopenum >= scopenum_function or len(items) < 3:
- return items
- items_done = []
- while 1:
- items_before, items_same, items_other, newignore = \
- slice_items(items, ignore, argkeys_cache[scopenum])
- items_before = reorder_items_atscope(
- items_before, ignore, argkeys_cache,scopenum+1)
- if items_same is None:
- # nothing to reorder in this scope
- assert items_other is None
- return items_done + items_before
- items_done.extend(items_before)
- items = items_same + items_other
- ignore = newignore
-
-
-def slice_items(items, ignore, scoped_argkeys_cache):
- # we pick the first item which uses a fixture instance in the
- # requested scope and which we haven't seen yet. We slice the input
- # items list into a list of items_nomatch, items_same and
- # items_other
- if scoped_argkeys_cache: # do we need to do work at all?
- it = iter(items)
- # first find a slicing key
- for i, item in enumerate(it):
- argkeys = scoped_argkeys_cache.get(item)
- if argkeys is not None:
- argkeys = argkeys.difference(ignore)
- if argkeys: # found a slicing key
- slicing_argkey = argkeys.pop()
- items_before = items[:i]
- items_same = [item]
- items_other = []
- # now slice the remainder of the list
- for item in it:
- argkeys = scoped_argkeys_cache.get(item)
- if argkeys and slicing_argkey in argkeys and \
- slicing_argkey not in ignore:
- items_same.append(item)
- else:
- items_other.append(item)
- newignore = ignore.copy()
- newignore.add(slicing_argkey)
- return (items_before, items_same, items_other, newignore)
- return items, None, None, None
-
-def get_parametrized_fixture_keys(item, scopenum):
- """ return list of keys for all parametrized arguments which match
- the specified scope. """
- assert scopenum < scopenum_function # function
- try:
- cs = item.callspec
- except AttributeError:
- pass
- else:
- # cs.indictes.items() is random order of argnames but
- # then again different functions (items) can change order of
- # arguments so it doesn't matter much probably
- for argname, param_index in cs.indices.items():
- if cs._arg2scopenum[argname] != scopenum:
- continue
- if scopenum == 0: # session
- key = (argname, param_index)
- elif scopenum == 1: # module
- key = (argname, param_index, item.fspath)
- elif scopenum == 2: # class
- key = (argname, param_index, item.fspath, item.cls)
- yield key
-
-
-def xunitsetup(obj, name):
- meth = getattr(obj, name, None)
- if getfixturemarker(meth) is None:
- return meth
-
-def getfixturemarker(obj):
- """ return fixturemarker or None if it doesn't exist or raised
- exceptions."""
- try:
- return getattr(obj, "_pytestfixturefunction", None)
- except KeyboardInterrupt:
- raise
- except Exception:
- # some objects raise errors like request (from flask import request)
- # we don't expect them to be fixture functions
- return None
-
-scopename2class = {
- 'class': Class,
- 'module': Module,
- 'function': pytest.Item,
-}
-def get_scope_node(node, scope):
- cls = scopename2class.get(scope)
- if cls is None:
- if scope == "session":
- return node.session
- raise ValueError("unknown scope")
- return node.getparent(cls)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/recwarn.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/recwarn.py
deleted file mode 100644
index a89474c036a..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/recwarn.py
+++ /dev/null
@@ -1,221 +0,0 @@
-""" recording warnings during test function execution. """
-
-import inspect
-
-import _pytest._code
-import py
-import sys
-import warnings
-import pytest
-
-
-@pytest.yield_fixture
-def recwarn(request):
- """Return a WarningsRecorder instance that provides these methods:
-
- * ``pop(category=None)``: return last warning matching the category.
- * ``clear()``: clear list of warnings
-
- See http://docs.python.org/library/warnings.html for information
- on warning categories.
- """
- wrec = WarningsRecorder()
- with wrec:
- warnings.simplefilter('default')
- yield wrec
-
-
-def pytest_namespace():
- return {'deprecated_call': deprecated_call,
- 'warns': warns}
-
-
-def deprecated_call(func=None, *args, **kwargs):
- """ assert that calling ``func(*args, **kwargs)`` triggers a
- ``DeprecationWarning`` or ``PendingDeprecationWarning``.
-
- This function can be used as a context manager::
-
- >>> with deprecated_call():
- ... myobject.deprecated_method()
-
- Note: we cannot use WarningsRecorder here because it is still subject
- to the mechanism that prevents warnings of the same type from being
- triggered twice for the same module. See #1190.
- """
- if not func:
- return WarningsChecker(expected_warning=DeprecationWarning)
-
- categories = []
-
- def warn_explicit(message, category, *args, **kwargs):
- categories.append(category)
- old_warn_explicit(message, category, *args, **kwargs)
-
- def warn(message, category=None, *args, **kwargs):
- if isinstance(message, Warning):
- categories.append(message.__class__)
- else:
- categories.append(category)
- old_warn(message, category, *args, **kwargs)
-
- old_warn = warnings.warn
- old_warn_explicit = warnings.warn_explicit
- warnings.warn_explicit = warn_explicit
- warnings.warn = warn
- try:
- ret = func(*args, **kwargs)
- finally:
- warnings.warn_explicit = old_warn_explicit
- warnings.warn = old_warn
- deprecation_categories = (DeprecationWarning, PendingDeprecationWarning)
- if not any(issubclass(c, deprecation_categories) for c in categories):
- __tracebackhide__ = True
- raise AssertionError("%r did not produce DeprecationWarning" % (func,))
- return ret
-
-
-def warns(expected_warning, *args, **kwargs):
- """Assert that code raises a particular class of warning.
-
- Specifically, the input @expected_warning can be a warning class or
- tuple of warning classes, and the code must return that warning
- (if a single class) or one of those warnings (if a tuple).
-
- This helper produces a list of ``warnings.WarningMessage`` objects,
- one for each warning raised.
-
- This function can be used as a context manager, or any of the other ways
- ``pytest.raises`` can be used::
-
- >>> with warns(RuntimeWarning):
- ... warnings.warn("my warning", RuntimeWarning)
- """
- wcheck = WarningsChecker(expected_warning)
- if not args:
- return wcheck
- elif isinstance(args[0], str):
- code, = args
- assert isinstance(code, str)
- frame = sys._getframe(1)
- loc = frame.f_locals.copy()
- loc.update(kwargs)
-
- with wcheck:
- code = _pytest._code.Source(code).compile()
- py.builtin.exec_(code, frame.f_globals, loc)
- else:
- func = args[0]
- with wcheck:
- return func(*args[1:], **kwargs)
-
-
-class RecordedWarning(object):
- def __init__(self, message, category, filename, lineno, file, line):
- self.message = message
- self.category = category
- self.filename = filename
- self.lineno = lineno
- self.file = file
- self.line = line
-
-
-class WarningsRecorder(object):
- """A context manager to record raised warnings.
-
- Adapted from `warnings.catch_warnings`.
- """
-
- def __init__(self, module=None):
- self._module = sys.modules['warnings'] if module is None else module
- self._entered = False
- self._list = []
-
- @property
- def list(self):
- """The list of recorded warnings."""
- return self._list
-
- def __getitem__(self, i):
- """Get a recorded warning by index."""
- return self._list[i]
-
- def __iter__(self):
- """Iterate through the recorded warnings."""
- return iter(self._list)
-
- def __len__(self):
- """The number of recorded warnings."""
- return len(self._list)
-
- def pop(self, cls=Warning):
- """Pop the first recorded warning, raise exception if not exists."""
- for i, w in enumerate(self._list):
- if issubclass(w.category, cls):
- return self._list.pop(i)
- __tracebackhide__ = True
- raise AssertionError("%r not found in warning list" % cls)
-
- def clear(self):
- """Clear the list of recorded warnings."""
- self._list[:] = []
-
- def __enter__(self):
- if self._entered:
- __tracebackhide__ = True
- raise RuntimeError("Cannot enter %r twice" % self)
- self._entered = True
- self._filters = self._module.filters
- self._module.filters = self._filters[:]
- self._showwarning = self._module.showwarning
-
- def showwarning(message, category, filename, lineno,
- file=None, line=None):
- self._list.append(RecordedWarning(
- message, category, filename, lineno, file, line))
-
- # still perform old showwarning functionality
- self._showwarning(
- message, category, filename, lineno, file=file, line=line)
-
- self._module.showwarning = showwarning
-
- # allow the same warning to be raised more than once
-
- self._module.simplefilter('always')
- return self
-
- def __exit__(self, *exc_info):
- if not self._entered:
- __tracebackhide__ = True
- raise RuntimeError("Cannot exit %r without entering first" % self)
- self._module.filters = self._filters
- self._module.showwarning = self._showwarning
-
-
-class WarningsChecker(WarningsRecorder):
- def __init__(self, expected_warning=None, module=None):
- super(WarningsChecker, self).__init__(module=module)
-
- msg = ("exceptions must be old-style classes or "
- "derived from Warning, not %s")
- if isinstance(expected_warning, tuple):
- for exc in expected_warning:
- if not inspect.isclass(exc):
- raise TypeError(msg % type(exc))
- elif inspect.isclass(expected_warning):
- expected_warning = (expected_warning,)
- elif expected_warning is not None:
- raise TypeError(msg % type(expected_warning))
-
- self.expected_warning = expected_warning
-
- def __exit__(self, *exc_info):
- super(WarningsChecker, self).__exit__(*exc_info)
-
- # only check if we're not currently handling an exception
- if all(a is None for a in exc_info):
- if self.expected_warning is not None:
- if not any(r.category in self.expected_warning for r in self):
- __tracebackhide__ = True
- pytest.fail("DID NOT WARN")
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/resultlog.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/resultlog.py
deleted file mode 100644
index 3670f0214c9..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/resultlog.py
+++ /dev/null
@@ -1,104 +0,0 @@
-""" log machine-parseable test session result information in a plain
-text file.
-"""
-
-import py
-import os
-
-def pytest_addoption(parser):
- group = parser.getgroup("terminal reporting", "resultlog plugin options")
- group.addoption('--resultlog', '--result-log', action="store",
- metavar="path", default=None,
- help="path for machine-readable result log.")
-
-def pytest_configure(config):
- resultlog = config.option.resultlog
- # prevent opening resultlog on slave nodes (xdist)
- if resultlog and not hasattr(config, 'slaveinput'):
- dirname = os.path.dirname(os.path.abspath(resultlog))
- if not os.path.isdir(dirname):
- os.makedirs(dirname)
- logfile = open(resultlog, 'w', 1) # line buffered
- config._resultlog = ResultLog(config, logfile)
- config.pluginmanager.register(config._resultlog)
-
-def pytest_unconfigure(config):
- resultlog = getattr(config, '_resultlog', None)
- if resultlog:
- resultlog.logfile.close()
- del config._resultlog
- config.pluginmanager.unregister(resultlog)
-
-def generic_path(item):
- chain = item.listchain()
- gpath = [chain[0].name]
- fspath = chain[0].fspath
- fspart = False
- for node in chain[1:]:
- newfspath = node.fspath
- if newfspath == fspath:
- if fspart:
- gpath.append(':')
- fspart = False
- else:
- gpath.append('.')
- else:
- gpath.append('/')
- fspart = True
- name = node.name
- if name[0] in '([':
- gpath.pop()
- gpath.append(name)
- fspath = newfspath
- return ''.join(gpath)
-
-class ResultLog(object):
- def __init__(self, config, logfile):
- self.config = config
- self.logfile = logfile # preferably line buffered
-
- def write_log_entry(self, testpath, lettercode, longrepr):
- py.builtin.print_("%s %s" % (lettercode, testpath), file=self.logfile)
- for line in longrepr.splitlines():
- py.builtin.print_(" %s" % line, file=self.logfile)
-
- def log_outcome(self, report, lettercode, longrepr):
- testpath = getattr(report, 'nodeid', None)
- if testpath is None:
- testpath = report.fspath
- self.write_log_entry(testpath, lettercode, longrepr)
-
- def pytest_runtest_logreport(self, report):
- if report.when != "call" and report.passed:
- return
- res = self.config.hook.pytest_report_teststatus(report=report)
- code = res[1]
- if code == 'x':
- longrepr = str(report.longrepr)
- elif code == 'X':
- longrepr = ''
- elif report.passed:
- longrepr = ""
- elif report.failed:
- longrepr = str(report.longrepr)
- elif report.skipped:
- longrepr = str(report.longrepr[2])
- self.log_outcome(report, code, longrepr)
-
- def pytest_collectreport(self, report):
- if not report.passed:
- if report.failed:
- code = "F"
- longrepr = str(report.longrepr)
- else:
- assert report.skipped
- code = "S"
- longrepr = "%s:%d: %s" % report.longrepr
- self.log_outcome(report, code, longrepr)
-
- def pytest_internalerror(self, excrepr):
- reprcrash = getattr(excrepr, 'reprcrash', None)
- path = getattr(reprcrash, "path", None)
- if path is None:
- path = "cwd:%s" % py.path.local()
- self.write_log_entry(path, '!', str(excrepr))
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/runner.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/runner.py
deleted file mode 100644
index cde94c8c89e..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/runner.py
+++ /dev/null
@@ -1,515 +0,0 @@
-""" basic collect and runtest protocol implementations """
-import bdb
-import sys
-from time import time
-
-import py
-import pytest
-from _pytest._code.code import TerminalRepr, ExceptionInfo
-
-
-def pytest_namespace():
- return {
- 'fail' : fail,
- 'skip' : skip,
- 'importorskip' : importorskip,
- 'exit' : exit,
- }
-
-#
-# pytest plugin hooks
-
-def pytest_addoption(parser):
- group = parser.getgroup("terminal reporting", "reporting", after="general")
- group.addoption('--durations',
- action="store", type=int, default=None, metavar="N",
- help="show N slowest setup/test durations (N=0 for all)."),
-
-def pytest_terminal_summary(terminalreporter):
- durations = terminalreporter.config.option.durations
- if durations is None:
- return
- tr = terminalreporter
- dlist = []
- for replist in tr.stats.values():
- for rep in replist:
- if hasattr(rep, 'duration'):
- dlist.append(rep)
- if not dlist:
- return
- dlist.sort(key=lambda x: x.duration)
- dlist.reverse()
- if not durations:
- tr.write_sep("=", "slowest test durations")
- else:
- tr.write_sep("=", "slowest %s test durations" % durations)
- dlist = dlist[:durations]
-
- for rep in dlist:
- nodeid = rep.nodeid.replace("::()::", "::")
- tr.write_line("%02.2fs %-8s %s" %
- (rep.duration, rep.when, nodeid))
-
-def pytest_sessionstart(session):
- session._setupstate = SetupState()
-def pytest_sessionfinish(session):
- session._setupstate.teardown_all()
-
-class NodeInfo:
- def __init__(self, location):
- self.location = location
-
-def pytest_runtest_protocol(item, nextitem):
- item.ihook.pytest_runtest_logstart(
- nodeid=item.nodeid, location=item.location,
- )
- runtestprotocol(item, nextitem=nextitem)
- return True
-
-def runtestprotocol(item, log=True, nextitem=None):
- hasrequest = hasattr(item, "_request")
- if hasrequest and not item._request:
- item._initrequest()
- rep = call_and_report(item, "setup", log)
- reports = [rep]
- if rep.passed:
- reports.append(call_and_report(item, "call", log))
- reports.append(call_and_report(item, "teardown", log,
- nextitem=nextitem))
- # after all teardown hooks have been called
- # want funcargs and request info to go away
- if hasrequest:
- item._request = False
- item.funcargs = None
- return reports
-
-def pytest_runtest_setup(item):
- item.session._setupstate.prepare(item)
-
-def pytest_runtest_call(item):
- try:
- item.runtest()
- except Exception:
- # Store trace info to allow postmortem debugging
- type, value, tb = sys.exc_info()
- tb = tb.tb_next # Skip *this* frame
- sys.last_type = type
- sys.last_value = value
- sys.last_traceback = tb
- del tb # Get rid of it in this namespace
- raise
-
-def pytest_runtest_teardown(item, nextitem):
- item.session._setupstate.teardown_exact(item, nextitem)
-
-def pytest_report_teststatus(report):
- if report.when in ("setup", "teardown"):
- if report.failed:
- # category, shortletter, verbose-word
- return "error", "E", "ERROR"
- elif report.skipped:
- return "skipped", "s", "SKIPPED"
- else:
- return "", "", ""
-
-
-#
-# Implementation
-
-def call_and_report(item, when, log=True, **kwds):
- call = call_runtest_hook(item, when, **kwds)
- hook = item.ihook
- report = hook.pytest_runtest_makereport(item=item, call=call)
- if log:
- hook.pytest_runtest_logreport(report=report)
- if check_interactive_exception(call, report):
- hook.pytest_exception_interact(node=item, call=call, report=report)
- return report
-
-def check_interactive_exception(call, report):
- return call.excinfo and not (
- hasattr(report, "wasxfail") or
- call.excinfo.errisinstance(skip.Exception) or
- call.excinfo.errisinstance(bdb.BdbQuit))
-
-def call_runtest_hook(item, when, **kwds):
- hookname = "pytest_runtest_" + when
- ihook = getattr(item.ihook, hookname)
- return CallInfo(lambda: ihook(item=item, **kwds), when=when)
-
-class CallInfo:
- """ Result/Exception info a function invocation. """
- #: None or ExceptionInfo object.
- excinfo = None
- def __init__(self, func, when):
- #: context of invocation: one of "setup", "call",
- #: "teardown", "memocollect"
- self.when = when
- self.start = time()
- try:
- self.result = func()
- except KeyboardInterrupt:
- self.stop = time()
- raise
- except:
- self.excinfo = ExceptionInfo()
- self.stop = time()
-
- def __repr__(self):
- if self.excinfo:
- status = "exception: %s" % str(self.excinfo.value)
- else:
- status = "result: %r" % (self.result,)
- return "<CallInfo when=%r %s>" % (self.when, status)
-
-def getslaveinfoline(node):
- try:
- return node._slaveinfocache
- except AttributeError:
- d = node.slaveinfo
- ver = "%s.%s.%s" % d['version_info'][:3]
- node._slaveinfocache = s = "[%s] %s -- Python %s %s" % (
- d['id'], d['sysplatform'], ver, d['executable'])
- return s
-
-class BaseReport(object):
-
- def __init__(self, **kw):
- self.__dict__.update(kw)
-
- def toterminal(self, out):
- if hasattr(self, 'node'):
- out.line(getslaveinfoline(self.node))
-
- longrepr = self.longrepr
- if longrepr is None:
- return
-
- if hasattr(longrepr, 'toterminal'):
- longrepr.toterminal(out)
- else:
- try:
- out.line(longrepr)
- except UnicodeEncodeError:
- out.line("<unprintable longrepr>")
-
- def get_sections(self, prefix):
- for name, content in self.sections:
- if name.startswith(prefix):
- yield prefix, content
-
- passed = property(lambda x: x.outcome == "passed")
- failed = property(lambda x: x.outcome == "failed")
- skipped = property(lambda x: x.outcome == "skipped")
-
- @property
- def fspath(self):
- return self.nodeid.split("::")[0]
-
-def pytest_runtest_makereport(item, call):
- when = call.when
- duration = call.stop-call.start
- keywords = dict([(x,1) for x in item.keywords])
- excinfo = call.excinfo
- sections = []
- if not call.excinfo:
- outcome = "passed"
- longrepr = None
- else:
- if not isinstance(excinfo, ExceptionInfo):
- outcome = "failed"
- longrepr = excinfo
- elif excinfo.errisinstance(pytest.skip.Exception):
- outcome = "skipped"
- r = excinfo._getreprcrash()
- longrepr = (str(r.path), r.lineno, r.message)
- else:
- outcome = "failed"
- if call.when == "call":
- longrepr = item.repr_failure(excinfo)
- else: # exception in setup or teardown
- longrepr = item._repr_failure_py(excinfo,
- style=item.config.option.tbstyle)
- for rwhen, key, content in item._report_sections:
- sections.append(("Captured %s %s" %(key, rwhen), content))
- return TestReport(item.nodeid, item.location,
- keywords, outcome, longrepr, when,
- sections, duration)
-
-class TestReport(BaseReport):
- """ Basic test report object (also used for setup and teardown calls if
- they fail).
- """
- def __init__(self, nodeid, location, keywords, outcome,
- longrepr, when, sections=(), duration=0, **extra):
- #: normalized collection node id
- self.nodeid = nodeid
-
- #: a (filesystempath, lineno, domaininfo) tuple indicating the
- #: actual location of a test item - it might be different from the
- #: collected one e.g. if a method is inherited from a different module.
- self.location = location
-
- #: a name -> value dictionary containing all keywords and
- #: markers associated with a test invocation.
- self.keywords = keywords
-
- #: test outcome, always one of "passed", "failed", "skipped".
- self.outcome = outcome
-
- #: None or a failure representation.
- self.longrepr = longrepr
-
- #: one of 'setup', 'call', 'teardown' to indicate runtest phase.
- self.when = when
-
- #: list of (secname, data) extra information which needs to
- #: marshallable
- self.sections = list(sections)
-
- #: time it took to run just the test
- self.duration = duration
-
- self.__dict__.update(extra)
-
- def __repr__(self):
- return "<TestReport %r when=%r outcome=%r>" % (
- self.nodeid, self.when, self.outcome)
-
-class TeardownErrorReport(BaseReport):
- outcome = "failed"
- when = "teardown"
- def __init__(self, longrepr, **extra):
- self.longrepr = longrepr
- self.sections = []
- self.__dict__.update(extra)
-
-def pytest_make_collect_report(collector):
- call = CallInfo(collector._memocollect, "memocollect")
- longrepr = None
- if not call.excinfo:
- outcome = "passed"
- else:
- from _pytest import nose
- skip_exceptions = (Skipped,) + nose.get_skip_exceptions()
- if call.excinfo.errisinstance(skip_exceptions):
- outcome = "skipped"
- r = collector._repr_failure_py(call.excinfo, "line").reprcrash
- longrepr = (str(r.path), r.lineno, r.message)
- else:
- outcome = "failed"
- errorinfo = collector.repr_failure(call.excinfo)
- if not hasattr(errorinfo, "toterminal"):
- errorinfo = CollectErrorRepr(errorinfo)
- longrepr = errorinfo
- rep = CollectReport(collector.nodeid, outcome, longrepr,
- getattr(call, 'result', None))
- rep.call = call # see collect_one_node
- return rep
-
-
-class CollectReport(BaseReport):
- def __init__(self, nodeid, outcome, longrepr, result,
- sections=(), **extra):
- self.nodeid = nodeid
- self.outcome = outcome
- self.longrepr = longrepr
- self.result = result or []
- self.sections = list(sections)
- self.__dict__.update(extra)
-
- @property
- def location(self):
- return (self.fspath, None, self.fspath)
-
- def __repr__(self):
- return "<CollectReport %r lenresult=%s outcome=%r>" % (
- self.nodeid, len(self.result), self.outcome)
-
-class CollectErrorRepr(TerminalRepr):
- def __init__(self, msg):
- self.longrepr = msg
- def toterminal(self, out):
- out.line(self.longrepr, red=True)
-
-class SetupState(object):
- """ shared state for setting up/tearing down test items or collectors. """
- def __init__(self):
- self.stack = []
- self._finalizers = {}
-
- def addfinalizer(self, finalizer, colitem):
- """ attach a finalizer to the given colitem.
- if colitem is None, this will add a finalizer that
- is called at the end of teardown_all().
- """
- assert colitem and not isinstance(colitem, tuple)
- assert py.builtin.callable(finalizer)
- #assert colitem in self.stack # some unit tests don't setup stack :/
- self._finalizers.setdefault(colitem, []).append(finalizer)
-
- def _pop_and_teardown(self):
- colitem = self.stack.pop()
- self._teardown_with_finalization(colitem)
-
- def _callfinalizers(self, colitem):
- finalizers = self._finalizers.pop(colitem, None)
- exc = None
- while finalizers:
- fin = finalizers.pop()
- try:
- fin()
- except Exception:
- # XXX Only first exception will be seen by user,
- # ideally all should be reported.
- if exc is None:
- exc = sys.exc_info()
- if exc:
- py.builtin._reraise(*exc)
-
- def _teardown_with_finalization(self, colitem):
- self._callfinalizers(colitem)
- if hasattr(colitem, "teardown"):
- colitem.teardown()
- for colitem in self._finalizers:
- assert colitem is None or colitem in self.stack \
- or isinstance(colitem, tuple)
-
- def teardown_all(self):
- while self.stack:
- self._pop_and_teardown()
- for key in list(self._finalizers):
- self._teardown_with_finalization(key)
- assert not self._finalizers
-
- def teardown_exact(self, item, nextitem):
- needed_collectors = nextitem and nextitem.listchain() or []
- self._teardown_towards(needed_collectors)
-
- def _teardown_towards(self, needed_collectors):
- while self.stack:
- if self.stack == needed_collectors[:len(self.stack)]:
- break
- self._pop_and_teardown()
-
- def prepare(self, colitem):
- """ setup objects along the collector chain to the test-method
- and teardown previously setup objects."""
- needed_collectors = colitem.listchain()
- self._teardown_towards(needed_collectors)
-
- # check if the last collection node has raised an error
- for col in self.stack:
- if hasattr(col, '_prepare_exc'):
- py.builtin._reraise(*col._prepare_exc)
- for col in needed_collectors[len(self.stack):]:
- self.stack.append(col)
- try:
- col.setup()
- except Exception:
- col._prepare_exc = sys.exc_info()
- raise
-
-def collect_one_node(collector):
- ihook = collector.ihook
- ihook.pytest_collectstart(collector=collector)
- rep = ihook.pytest_make_collect_report(collector=collector)
- call = rep.__dict__.pop("call", None)
- if call and check_interactive_exception(call, rep):
- ihook.pytest_exception_interact(node=collector, call=call, report=rep)
- return rep
-
-
-# =============================================================
-# Test OutcomeExceptions and helpers for creating them.
-
-
-class OutcomeException(Exception):
- """ OutcomeException and its subclass instances indicate and
- contain info about test and collection outcomes.
- """
- def __init__(self, msg=None, pytrace=True):
- Exception.__init__(self, msg)
- self.msg = msg
- self.pytrace = pytrace
-
- def __repr__(self):
- if self.msg:
- val = self.msg
- if isinstance(val, bytes):
- val = py._builtin._totext(val, errors='replace')
- return val
- return "<%s instance>" %(self.__class__.__name__,)
- __str__ = __repr__
-
-class Skipped(OutcomeException):
- # XXX hackish: on 3k we fake to live in the builtins
- # in order to have Skipped exception printing shorter/nicer
- __module__ = 'builtins'
-
-class Failed(OutcomeException):
- """ raised from an explicit call to pytest.fail() """
- __module__ = 'builtins'
-
-class Exit(KeyboardInterrupt):
- """ raised for immediate program exits (no tracebacks/summaries)"""
- def __init__(self, msg="unknown reason"):
- self.msg = msg
- KeyboardInterrupt.__init__(self, msg)
-
-# exposed helper methods
-
-def exit(msg):
- """ exit testing process as if KeyboardInterrupt was triggered. """
- __tracebackhide__ = True
- raise Exit(msg)
-
-exit.Exception = Exit
-
-def skip(msg=""):
- """ skip an executing test with the given message. Note: it's usually
- better to use the pytest.mark.skipif marker to declare a test to be
- skipped under certain conditions like mismatching platforms or
- dependencies. See the pytest_skipping plugin for details.
- """
- __tracebackhide__ = True
- raise Skipped(msg=msg)
-skip.Exception = Skipped
-
-def fail(msg="", pytrace=True):
- """ explicitly fail an currently-executing test with the given Message.
-
- :arg pytrace: if false the msg represents the full failure information
- and no python traceback will be reported.
- """
- __tracebackhide__ = True
- raise Failed(msg=msg, pytrace=pytrace)
-fail.Exception = Failed
-
-
-def importorskip(modname, minversion=None):
- """ return imported module if it has at least "minversion" as its
- __version__ attribute. If no minversion is specified the a skip
- is only triggered if the module can not be imported.
- """
- __tracebackhide__ = True
- compile(modname, '', 'eval') # to catch syntaxerrors
- try:
- __import__(modname)
- except ImportError:
- skip("could not import %r" %(modname,))
- mod = sys.modules[modname]
- if minversion is None:
- return mod
- verattr = getattr(mod, '__version__', None)
- if minversion is not None:
- try:
- from pkg_resources import parse_version as pv
- except ImportError:
- skip("we have a required version for %r but can not import "
- "no pkg_resources to parse version strings." %(modname,))
- if verattr is None or pv(verattr) < pv(minversion):
- skip("module %r has __version__ %r, required is: %r" %(
- modname, verattr, minversion))
- return mod
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/skipping.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/skipping.py
deleted file mode 100644
index 69157f485a0..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/skipping.py
+++ /dev/null
@@ -1,354 +0,0 @@
-""" support for skip/xfail functions and markers. """
-import os
-import sys
-import traceback
-
-import py
-import pytest
-from _pytest.mark import MarkInfo, MarkDecorator
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("general")
- group.addoption('--runxfail',
- action="store_true", dest="runxfail", default=False,
- help="run tests even if they are marked xfail")
-
- parser.addini("xfail_strict", "default for the strict parameter of xfail "
- "markers when not given explicitly (default: "
- "False)",
- default=False,
- type="bool")
-
-
-def pytest_configure(config):
- if config.option.runxfail:
- old = pytest.xfail
- config._cleanup.append(lambda: setattr(pytest, "xfail", old))
- def nop(*args, **kwargs):
- pass
- nop.Exception = XFailed
- setattr(pytest, "xfail", nop)
-
- config.addinivalue_line("markers",
- "skipif(condition): skip the given test function if eval(condition) "
- "results in a True value. Evaluation happens within the "
- "module global context. Example: skipif('sys.platform == \"win32\"') "
- "skips the test if we are on the win32 platform. see "
- "http://pytest.org/latest/skipping.html"
- )
- config.addinivalue_line("markers",
- "xfail(condition, reason=None, run=True, raises=None): mark the the test function "
- "as an expected failure if eval(condition) has a True value. "
- "Optionally specify a reason for better reporting and run=False if "
- "you don't even want to execute the test function. If only specific "
- "exception(s) are expected, you can list them in raises, and if the test fails "
- "in other ways, it will be reported as a true failure. "
- "See http://pytest.org/latest/skipping.html"
- )
-
-
-def pytest_namespace():
- return dict(xfail=xfail)
-
-
-class XFailed(pytest.fail.Exception):
- """ raised from an explicit call to pytest.xfail() """
-
-
-def xfail(reason=""):
- """ xfail an executing test or setup functions with the given reason."""
- __tracebackhide__ = True
- raise XFailed(reason)
-xfail.Exception = XFailed
-
-
-class MarkEvaluator:
- def __init__(self, item, name):
- self.item = item
- self.name = name
-
- @property
- def holder(self):
- return self.item.keywords.get(self.name)
-
- def __bool__(self):
- return bool(self.holder)
- __nonzero__ = __bool__
-
- def wasvalid(self):
- return not hasattr(self, 'exc')
-
- def invalidraise(self, exc):
- raises = self.get('raises')
- if not raises:
- return
- return not isinstance(exc, raises)
-
- def istrue(self):
- try:
- return self._istrue()
- except Exception:
- self.exc = sys.exc_info()
- if isinstance(self.exc[1], SyntaxError):
- msg = [" " * (self.exc[1].offset + 4) + "^",]
- msg.append("SyntaxError: invalid syntax")
- else:
- msg = traceback.format_exception_only(*self.exc[:2])
- pytest.fail("Error evaluating %r expression\n"
- " %s\n"
- "%s"
- %(self.name, self.expr, "\n".join(msg)),
- pytrace=False)
-
- def _getglobals(self):
- d = {'os': os, 'sys': sys, 'config': self.item.config}
- func = self.item.obj
- try:
- d.update(func.__globals__)
- except AttributeError:
- d.update(func.func_globals)
- return d
-
- def _istrue(self):
- if hasattr(self, 'result'):
- return self.result
- if self.holder:
- d = self._getglobals()
- if self.holder.args:
- self.result = False
- # "holder" might be a MarkInfo or a MarkDecorator; only
- # MarkInfo keeps track of all parameters it received in an
- # _arglist attribute
- if hasattr(self.holder, '_arglist'):
- arglist = self.holder._arglist
- else:
- arglist = [(self.holder.args, self.holder.kwargs)]
- for args, kwargs in arglist:
- for expr in args:
- self.expr = expr
- if isinstance(expr, py.builtin._basestring):
- result = cached_eval(self.item.config, expr, d)
- else:
- if "reason" not in kwargs:
- # XXX better be checked at collection time
- msg = "you need to specify reason=STRING " \
- "when using booleans as conditions."
- pytest.fail(msg)
- result = bool(expr)
- if result:
- self.result = True
- self.reason = kwargs.get('reason', None)
- self.expr = expr
- return self.result
- else:
- self.result = True
- return getattr(self, 'result', False)
-
- def get(self, attr, default=None):
- return self.holder.kwargs.get(attr, default)
-
- def getexplanation(self):
- expl = getattr(self, 'reason', None) or self.get('reason', None)
- if not expl:
- if not hasattr(self, 'expr'):
- return ""
- else:
- return "condition: " + str(self.expr)
- return expl
-
-
-@pytest.hookimpl(tryfirst=True)
-def pytest_runtest_setup(item):
- # Check if skip or skipif are specified as pytest marks
-
- skipif_info = item.keywords.get('skipif')
- if isinstance(skipif_info, (MarkInfo, MarkDecorator)):
- eval_skipif = MarkEvaluator(item, 'skipif')
- if eval_skipif.istrue():
- item._evalskip = eval_skipif
- pytest.skip(eval_skipif.getexplanation())
-
- skip_info = item.keywords.get('skip')
- if isinstance(skip_info, (MarkInfo, MarkDecorator)):
- item._evalskip = True
- if 'reason' in skip_info.kwargs:
- pytest.skip(skip_info.kwargs['reason'])
- elif skip_info.args:
- pytest.skip(skip_info.args[0])
- else:
- pytest.skip("unconditional skip")
-
- item._evalxfail = MarkEvaluator(item, 'xfail')
- check_xfail_no_run(item)
-
-
-@pytest.mark.hookwrapper
-def pytest_pyfunc_call(pyfuncitem):
- check_xfail_no_run(pyfuncitem)
- outcome = yield
- passed = outcome.excinfo is None
- if passed:
- check_strict_xfail(pyfuncitem)
-
-
-def check_xfail_no_run(item):
- """check xfail(run=False)"""
- if not item.config.option.runxfail:
- evalxfail = item._evalxfail
- if evalxfail.istrue():
- if not evalxfail.get('run', True):
- pytest.xfail("[NOTRUN] " + evalxfail.getexplanation())
-
-
-def check_strict_xfail(pyfuncitem):
- """check xfail(strict=True) for the given PASSING test"""
- evalxfail = pyfuncitem._evalxfail
- if evalxfail.istrue():
- strict_default = pyfuncitem.config.getini('xfail_strict')
- is_strict_xfail = evalxfail.get('strict', strict_default)
- if is_strict_xfail:
- del pyfuncitem._evalxfail
- explanation = evalxfail.getexplanation()
- pytest.fail('[XPASS(strict)] ' + explanation, pytrace=False)
-
-
-@pytest.hookimpl(hookwrapper=True)
-def pytest_runtest_makereport(item, call):
- outcome = yield
- rep = outcome.get_result()
- evalxfail = getattr(item, '_evalxfail', None)
- evalskip = getattr(item, '_evalskip', None)
- # unitttest special case, see setting of _unexpectedsuccess
- if hasattr(item, '_unexpectedsuccess') and rep.when == "call":
- # we need to translate into how pytest encodes xpass
- rep.wasxfail = "reason: " + repr(item._unexpectedsuccess)
- rep.outcome = "failed"
- elif item.config.option.runxfail:
- pass # don't interefere
- elif call.excinfo and call.excinfo.errisinstance(pytest.xfail.Exception):
- rep.wasxfail = "reason: " + call.excinfo.value.msg
- rep.outcome = "skipped"
- elif evalxfail and not rep.skipped and evalxfail.wasvalid() and \
- evalxfail.istrue():
- if call.excinfo:
- if evalxfail.invalidraise(call.excinfo.value):
- rep.outcome = "failed"
- else:
- rep.outcome = "skipped"
- rep.wasxfail = evalxfail.getexplanation()
- elif call.when == "call":
- rep.outcome = "failed" # xpass outcome
- rep.wasxfail = evalxfail.getexplanation()
- elif evalskip is not None and rep.skipped and type(rep.longrepr) is tuple:
- # skipped by mark.skipif; change the location of the failure
- # to point to the item definition, otherwise it will display
- # the location of where the skip exception was raised within pytest
- filename, line, reason = rep.longrepr
- filename, line = item.location[:2]
- rep.longrepr = filename, line, reason
-
-# called by terminalreporter progress reporting
-def pytest_report_teststatus(report):
- if hasattr(report, "wasxfail"):
- if report.skipped:
- return "xfailed", "x", "xfail"
- elif report.failed:
- return "xpassed", "X", ("XPASS", {'yellow': True})
-
-# called by the terminalreporter instance/plugin
-def pytest_terminal_summary(terminalreporter):
- tr = terminalreporter
- if not tr.reportchars:
- #for name in "xfailed skipped failed xpassed":
- # if not tr.stats.get(name, 0):
- # tr.write_line("HINT: use '-r' option to see extra "
- # "summary info about tests")
- # break
- return
-
- lines = []
- for char in tr.reportchars:
- if char == "x":
- show_xfailed(terminalreporter, lines)
- elif char == "X":
- show_xpassed(terminalreporter, lines)
- elif char in "fF":
- show_simple(terminalreporter, lines, 'failed', "FAIL %s")
- elif char in "sS":
- show_skipped(terminalreporter, lines)
- elif char == "E":
- show_simple(terminalreporter, lines, 'error', "ERROR %s")
- elif char == 'p':
- show_simple(terminalreporter, lines, 'passed', "PASSED %s")
-
- if lines:
- tr._tw.sep("=", "short test summary info")
- for line in lines:
- tr._tw.line(line)
-
-def show_simple(terminalreporter, lines, stat, format):
- failed = terminalreporter.stats.get(stat)
- if failed:
- for rep in failed:
- pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid)
- lines.append(format %(pos,))
-
-def show_xfailed(terminalreporter, lines):
- xfailed = terminalreporter.stats.get("xfailed")
- if xfailed:
- for rep in xfailed:
- pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid)
- reason = rep.wasxfail
- lines.append("XFAIL %s" % (pos,))
- if reason:
- lines.append(" " + str(reason))
-
-def show_xpassed(terminalreporter, lines):
- xpassed = terminalreporter.stats.get("xpassed")
- if xpassed:
- for rep in xpassed:
- pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid)
- reason = rep.wasxfail
- lines.append("XPASS %s %s" %(pos, reason))
-
-def cached_eval(config, expr, d):
- if not hasattr(config, '_evalcache'):
- config._evalcache = {}
- try:
- return config._evalcache[expr]
- except KeyError:
- import _pytest._code
- exprcode = _pytest._code.compile(expr, mode="eval")
- config._evalcache[expr] = x = eval(exprcode, d)
- return x
-
-
-def folded_skips(skipped):
- d = {}
- for event in skipped:
- key = event.longrepr
- assert len(key) == 3, (event, key)
- d.setdefault(key, []).append(event)
- l = []
- for key, events in d.items():
- l.append((len(events),) + key)
- return l
-
-def show_skipped(terminalreporter, lines):
- tr = terminalreporter
- skipped = tr.stats.get('skipped', [])
- if skipped:
- #if not tr.hasopt('skipped'):
- # tr.write_line(
- # "%d skipped tests, specify -rs for more info" %
- # len(skipped))
- # return
- fskips = folded_skips(skipped)
- if fskips:
- #tr.write_sep("_", "skipped test summary")
- for num, fspath, lineno, reason in fskips:
- if reason.startswith("Skipped: "):
- reason = reason[9:]
- lines.append("SKIP [%d] %s:%d: %s" %
- (num, fspath, lineno, reason))
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/standalonetemplate.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/standalonetemplate.py
deleted file mode 100755
index 484d5d1b25f..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/standalonetemplate.py
+++ /dev/null
@@ -1,89 +0,0 @@
-#! /usr/bin/env python
-
-# Hi There!
-# You may be wondering what this giant blob of binary data here is, you might
-# even be worried that we're up to something nefarious (good for you for being
-# paranoid!). This is a base64 encoding of a zip file, this zip file contains
-# a fully functional basic pytest script.
-#
-# Pytest is a thing that tests packages, pytest itself is a package that some-
-# one might want to install, especially if they're looking to run tests inside
-# some package they want to install. Pytest has a lot of code to collect and
-# execute tests, and other such sort of "tribal knowledge" that has been en-
-# coded in its code base. Because of this we basically include a basic copy
-# of pytest inside this blob. We do this because it let's you as a maintainer
-# or application developer who wants people who don't deal with python much to
-# easily run tests without installing the complete pytest package.
-#
-# If you're wondering how this is created: you can create it yourself if you
-# have a complete pytest installation by using this command on the command-
-# line: ``py.test --genscript=runtests.py``.
-
-sources = """
-@SOURCES@"""
-
-import sys
-import base64
-import zlib
-
-class DictImporter(object):
- def __init__(self, sources):
- self.sources = sources
-
- def find_module(self, fullname, path=None):
- if fullname == "argparse" and sys.version_info >= (2,7):
- # we were generated with <python2.7 (which pulls in argparse)
- # but we are running now on a stdlib which has it, so use that.
- return None
- if fullname in self.sources:
- return self
- if fullname + '.__init__' in self.sources:
- return self
- return None
-
- def load_module(self, fullname):
- # print "load_module:", fullname
- from types import ModuleType
- try:
- s = self.sources[fullname]
- is_pkg = False
- except KeyError:
- s = self.sources[fullname + '.__init__']
- is_pkg = True
-
- co = compile(s, fullname, 'exec')
- module = sys.modules.setdefault(fullname, ModuleType(fullname))
- module.__file__ = "%s/%s" % (__file__, fullname)
- module.__loader__ = self
- if is_pkg:
- module.__path__ = [fullname]
-
- do_exec(co, module.__dict__) # noqa
- return sys.modules[fullname]
-
- def get_source(self, name):
- res = self.sources.get(name)
- if res is None:
- res = self.sources.get(name + '.__init__')
- return res
-
-if __name__ == "__main__":
- try:
- import pkg_resources # noqa
- except ImportError:
- sys.stderr.write("ERROR: setuptools not installed\n")
- sys.exit(2)
- if sys.version_info >= (3, 0):
- exec("def do_exec(co, loc): exec(co, loc)\n")
- import pickle
- sources = sources.encode("ascii") # ensure bytes
- sources = pickle.loads(zlib.decompress(base64.decodebytes(sources)))
- else:
- import cPickle as pickle
- exec("def do_exec(co, loc): exec co in loc\n")
- sources = pickle.loads(zlib.decompress(base64.decodestring(sources)))
-
- importer = DictImporter(sources)
- sys.meta_path.insert(0, importer)
- entry = "@ENTRY@"
- do_exec(entry, locals()) # noqa
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/terminal.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/terminal.py
deleted file mode 100644
index 825f553ef2c..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/terminal.py
+++ /dev/null
@@ -1,593 +0,0 @@
-""" terminal reporting of the full testing process.
-
-This is a good source for looking at the various reporting hooks.
-"""
-from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \
- EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED
-import pytest
-import py
-import sys
-import time
-import platform
-
-import _pytest._pluggy as pluggy
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("terminal reporting", "reporting", after="general")
- group._addoption('-v', '--verbose', action="count",
- dest="verbose", default=0, help="increase verbosity."),
- group._addoption('-q', '--quiet', action="count",
- dest="quiet", default=0, help="decrease verbosity."),
- group._addoption('-r',
- action="store", dest="reportchars", default=None, metavar="chars",
- help="show extra test summary info as specified by chars (f)ailed, "
- "(E)error, (s)skipped, (x)failed, (X)passed (w)pytest-warnings "
- "(p)passed, (P)passed with output, (a)all except pP.")
- group._addoption('-l', '--showlocals',
- action="store_true", dest="showlocals", default=False,
- help="show locals in tracebacks (disabled by default).")
- group._addoption('--report',
- action="store", dest="report", default=None, metavar="opts",
- help="(deprecated, use -r)")
- group._addoption('--tb', metavar="style",
- action="store", dest="tbstyle", default='auto',
- choices=['auto', 'long', 'short', 'no', 'line', 'native'],
- help="traceback print mode (auto/long/short/line/native/no).")
- group._addoption('--fulltrace', '--full-trace',
- action="store_true", default=False,
- help="don't cut any tracebacks (default is to cut).")
- group._addoption('--color', metavar="color",
- action="store", dest="color", default='auto',
- choices=['yes', 'no', 'auto'],
- help="color terminal output (yes/no/auto).")
-
-def pytest_configure(config):
- config.option.verbose -= config.option.quiet
- reporter = TerminalReporter(config, sys.stdout)
- config.pluginmanager.register(reporter, 'terminalreporter')
- if config.option.debug or config.option.traceconfig:
- def mywriter(tags, args):
- msg = " ".join(map(str, args))
- reporter.write_line("[traceconfig] " + msg)
- config.trace.root.setprocessor("pytest:config", mywriter)
-
-def getreportopt(config):
- reportopts = ""
- optvalue = config.option.report
- if optvalue:
- py.builtin.print_("DEPRECATED: use -r instead of --report option.",
- file=sys.stderr)
- if optvalue:
- for setting in optvalue.split(","):
- setting = setting.strip()
- if setting == "skipped":
- reportopts += "s"
- elif setting == "xfailed":
- reportopts += "x"
- reportchars = config.option.reportchars
- if reportchars:
- for char in reportchars:
- if char not in reportopts and char != 'a':
- reportopts += char
- elif char == 'a':
- reportopts = 'fEsxXw'
- return reportopts
-
-def pytest_report_teststatus(report):
- if report.passed:
- letter = "."
- elif report.skipped:
- letter = "s"
- elif report.failed:
- letter = "F"
- if report.when != "call":
- letter = "f"
- return report.outcome, letter, report.outcome.upper()
-
-class WarningReport:
- def __init__(self, code, message, nodeid=None, fslocation=None):
- self.code = code
- self.message = message
- self.nodeid = nodeid
- self.fslocation = fslocation
-
-
-class TerminalReporter:
- def __init__(self, config, file=None):
- import _pytest.config
- self.config = config
- self.verbosity = self.config.option.verbose
- self.showheader = self.verbosity >= 0
- self.showfspath = self.verbosity >= 0
- self.showlongtestinfo = self.verbosity > 0
- self._numcollected = 0
-
- self.stats = {}
- self.startdir = py.path.local()
- if file is None:
- file = sys.stdout
- self._tw = self.writer = _pytest.config.create_terminal_writer(config,
- file)
- self.currentfspath = None
- self.reportchars = getreportopt(config)
- self.hasmarkup = self._tw.hasmarkup
- self.isatty = file.isatty()
-
- def hasopt(self, char):
- char = {'xfailed': 'x', 'skipped': 's'}.get(char, char)
- return char in self.reportchars
-
- def write_fspath_result(self, nodeid, res):
- fspath = self.config.rootdir.join(nodeid.split("::")[0])
- if fspath != self.currentfspath:
- self.currentfspath = fspath
- fspath = self.startdir.bestrelpath(fspath)
- self._tw.line()
- self._tw.write(fspath + " ")
- self._tw.write(res)
-
- def write_ensure_prefix(self, prefix, extra="", **kwargs):
- if self.currentfspath != prefix:
- self._tw.line()
- self.currentfspath = prefix
- self._tw.write(prefix)
- if extra:
- self._tw.write(extra, **kwargs)
- self.currentfspath = -2
-
- def ensure_newline(self):
- if self.currentfspath:
- self._tw.line()
- self.currentfspath = None
-
- def write(self, content, **markup):
- self._tw.write(content, **markup)
-
- def write_line(self, line, **markup):
- if not py.builtin._istext(line):
- line = py.builtin.text(line, errors="replace")
- self.ensure_newline()
- self._tw.line(line, **markup)
-
- def rewrite(self, line, **markup):
- line = str(line)
- self._tw.write("\r" + line, **markup)
-
- def write_sep(self, sep, title=None, **markup):
- self.ensure_newline()
- self._tw.sep(sep, title, **markup)
-
- def section(self, title, sep="=", **kw):
- self._tw.sep(sep, title, **kw)
-
- def line(self, msg, **kw):
- self._tw.line(msg, **kw)
-
- def pytest_internalerror(self, excrepr):
- for line in py.builtin.text(excrepr).split("\n"):
- self.write_line("INTERNALERROR> " + line)
- return 1
-
- def pytest_logwarning(self, code, fslocation, message, nodeid):
- warnings = self.stats.setdefault("warnings", [])
- if isinstance(fslocation, tuple):
- fslocation = "%s:%d" % fslocation
- warning = WarningReport(code=code, fslocation=fslocation,
- message=message, nodeid=nodeid)
- warnings.append(warning)
-
- def pytest_plugin_registered(self, plugin):
- if self.config.option.traceconfig:
- msg = "PLUGIN registered: %s" % (plugin,)
- # XXX this event may happen during setup/teardown time
- # which unfortunately captures our output here
- # which garbles our output if we use self.write_line
- self.write_line(msg)
-
- def pytest_deselected(self, items):
- self.stats.setdefault('deselected', []).extend(items)
-
- def pytest_runtest_logstart(self, nodeid, location):
- # ensure that the path is printed before the
- # 1st test of a module starts running
- if self.showlongtestinfo:
- line = self._locationline(nodeid, *location)
- self.write_ensure_prefix(line, "")
- elif self.showfspath:
- fsid = nodeid.split("::")[0]
- self.write_fspath_result(fsid, "")
-
- def pytest_runtest_logreport(self, report):
- rep = report
- res = self.config.hook.pytest_report_teststatus(report=rep)
- cat, letter, word = res
- self.stats.setdefault(cat, []).append(rep)
- self._tests_ran = True
- if not letter and not word:
- # probably passed setup/teardown
- return
- if self.verbosity <= 0:
- if not hasattr(rep, 'node') and self.showfspath:
- self.write_fspath_result(rep.nodeid, letter)
- else:
- self._tw.write(letter)
- else:
- if isinstance(word, tuple):
- word, markup = word
- else:
- if rep.passed:
- markup = {'green':True}
- elif rep.failed:
- markup = {'red':True}
- elif rep.skipped:
- markup = {'yellow':True}
- line = self._locationline(rep.nodeid, *rep.location)
- if not hasattr(rep, 'node'):
- self.write_ensure_prefix(line, word, **markup)
- #self._tw.write(word, **markup)
- else:
- self.ensure_newline()
- if hasattr(rep, 'node'):
- self._tw.write("[%s] " % rep.node.gateway.id)
- self._tw.write(word, **markup)
- self._tw.write(" " + line)
- self.currentfspath = -2
-
- def pytest_collection(self):
- if not self.isatty and self.config.option.verbose >= 1:
- self.write("collecting ... ", bold=True)
-
- def pytest_collectreport(self, report):
- if report.failed:
- self.stats.setdefault("error", []).append(report)
- elif report.skipped:
- self.stats.setdefault("skipped", []).append(report)
- items = [x for x in report.result if isinstance(x, pytest.Item)]
- self._numcollected += len(items)
- if self.isatty:
- #self.write_fspath_result(report.nodeid, 'E')
- self.report_collect()
-
- def report_collect(self, final=False):
- if self.config.option.verbose < 0:
- return
-
- errors = len(self.stats.get('error', []))
- skipped = len(self.stats.get('skipped', []))
- if final:
- line = "collected "
- else:
- line = "collecting "
- line += str(self._numcollected) + " items"
- if errors:
- line += " / %d errors" % errors
- if skipped:
- line += " / %d skipped" % skipped
- if self.isatty:
- if final:
- line += " \n"
- self.rewrite(line, bold=True)
- else:
- self.write_line(line)
-
- def pytest_collection_modifyitems(self):
- self.report_collect(True)
-
- @pytest.hookimpl(trylast=True)
- def pytest_sessionstart(self, session):
- self._sessionstarttime = time.time()
- if not self.showheader:
- return
- self.write_sep("=", "test session starts", bold=True)
- verinfo = platform.python_version()
- msg = "platform %s -- Python %s" % (sys.platform, verinfo)
- if hasattr(sys, 'pypy_version_info'):
- verinfo = ".".join(map(str, sys.pypy_version_info[:3]))
- msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3])
- msg += ", pytest-%s, py-%s, pluggy-%s" % (
- pytest.__version__, py.__version__, pluggy.__version__)
- if self.verbosity > 0 or self.config.option.debug or \
- getattr(self.config.option, 'pastebin', None):
- msg += " -- " + str(sys.executable)
- self.write_line(msg)
- lines = self.config.hook.pytest_report_header(
- config=self.config, startdir=self.startdir)
- lines.reverse()
- for line in flatten(lines):
- self.write_line(line)
-
- def pytest_report_header(self, config):
- inifile = ""
- if config.inifile:
- inifile = config.rootdir.bestrelpath(config.inifile)
- lines = ["rootdir: %s, inifile: %s" %(config.rootdir, inifile)]
-
- plugininfo = config.pluginmanager.list_plugin_distinfo()
- if plugininfo:
-
- lines.append(
- "plugins: %s" % ", ".join(_plugin_nameversions(plugininfo)))
- return lines
-
- def pytest_collection_finish(self, session):
- if self.config.option.collectonly:
- self._printcollecteditems(session.items)
- if self.stats.get('failed'):
- self._tw.sep("!", "collection failures")
- for rep in self.stats.get('failed'):
- rep.toterminal(self._tw)
- return 1
- return 0
- if not self.showheader:
- return
- #for i, testarg in enumerate(self.config.args):
- # self.write_line("test path %d: %s" %(i+1, testarg))
-
- def _printcollecteditems(self, items):
- # to print out items and their parent collectors
- # we take care to leave out Instances aka ()
- # because later versions are going to get rid of them anyway
- if self.config.option.verbose < 0:
- if self.config.option.verbose < -1:
- counts = {}
- for item in items:
- name = item.nodeid.split('::', 1)[0]
- counts[name] = counts.get(name, 0) + 1
- for name, count in sorted(counts.items()):
- self._tw.line("%s: %d" % (name, count))
- else:
- for item in items:
- nodeid = item.nodeid
- nodeid = nodeid.replace("::()::", "::")
- self._tw.line(nodeid)
- return
- stack = []
- indent = ""
- for item in items:
- needed_collectors = item.listchain()[1:] # strip root node
- while stack:
- if stack == needed_collectors[:len(stack)]:
- break
- stack.pop()
- for col in needed_collectors[len(stack):]:
- stack.append(col)
- #if col.name == "()":
- # continue
- indent = (len(stack) - 1) * " "
- self._tw.line("%s%s" % (indent, col))
-
- @pytest.hookimpl(hookwrapper=True)
- def pytest_sessionfinish(self, exitstatus):
- outcome = yield
- outcome.get_result()
- self._tw.line("")
- summary_exit_codes = (
- EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, EXIT_USAGEERROR,
- EXIT_NOTESTSCOLLECTED)
- if exitstatus in summary_exit_codes:
- self.config.hook.pytest_terminal_summary(terminalreporter=self)
- self.summary_errors()
- self.summary_failures()
- self.summary_warnings()
- self.summary_passes()
- if exitstatus == EXIT_INTERRUPTED:
- self._report_keyboardinterrupt()
- del self._keyboardinterrupt_memo
- self.summary_deselected()
- self.summary_stats()
-
- def pytest_keyboard_interrupt(self, excinfo):
- self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True)
-
- def pytest_unconfigure(self):
- if hasattr(self, '_keyboardinterrupt_memo'):
- self._report_keyboardinterrupt()
-
- def _report_keyboardinterrupt(self):
- excrepr = self._keyboardinterrupt_memo
- msg = excrepr.reprcrash.message
- self.write_sep("!", msg)
- if "KeyboardInterrupt" in msg:
- if self.config.option.fulltrace:
- excrepr.toterminal(self._tw)
- else:
- self._tw.line("to show a full traceback on KeyboardInterrupt use --fulltrace", yellow=True)
- excrepr.reprcrash.toterminal(self._tw)
-
- def _locationline(self, nodeid, fspath, lineno, domain):
- def mkrel(nodeid):
- line = self.config.cwd_relative_nodeid(nodeid)
- if domain and line.endswith(domain):
- line = line[:-len(domain)]
- l = domain.split("[")
- l[0] = l[0].replace('.', '::') # don't replace '.' in params
- line += "[".join(l)
- return line
- # collect_fspath comes from testid which has a "/"-normalized path
-
- if fspath:
- res = mkrel(nodeid).replace("::()", "") # parens-normalization
- if nodeid.split("::")[0] != fspath.replace("\\", "/"):
- res += " <- " + self.startdir.bestrelpath(fspath)
- else:
- res = "[location]"
- return res + " "
-
- def _getfailureheadline(self, rep):
- if hasattr(rep, 'location'):
- fspath, lineno, domain = rep.location
- return domain
- else:
- return "test session" # XXX?
-
- def _getcrashline(self, rep):
- try:
- return str(rep.longrepr.reprcrash)
- except AttributeError:
- try:
- return str(rep.longrepr)[:50]
- except AttributeError:
- return ""
-
- #
- # summaries for sessionfinish
- #
- def getreports(self, name):
- l = []
- for x in self.stats.get(name, []):
- if not hasattr(x, '_pdbshown'):
- l.append(x)
- return l
-
- def summary_warnings(self):
- if self.hasopt("w"):
- warnings = self.stats.get("warnings")
- if not warnings:
- return
- self.write_sep("=", "pytest-warning summary")
- for w in warnings:
- self._tw.line("W%s %s %s" % (w.code,
- w.fslocation, w.message))
-
- def summary_passes(self):
- if self.config.option.tbstyle != "no":
- if self.hasopt("P"):
- reports = self.getreports('passed')
- if not reports:
- return
- self.write_sep("=", "PASSES")
- for rep in reports:
- msg = self._getfailureheadline(rep)
- self.write_sep("_", msg)
- self._outrep_summary(rep)
-
- def summary_failures(self):
- if self.config.option.tbstyle != "no":
- reports = self.getreports('failed')
- if not reports:
- return
- self.write_sep("=", "FAILURES")
- for rep in reports:
- if self.config.option.tbstyle == "line":
- line = self._getcrashline(rep)
- self.write_line(line)
- else:
- msg = self._getfailureheadline(rep)
- markup = {'red': True, 'bold': True}
- self.write_sep("_", msg, **markup)
- self._outrep_summary(rep)
-
- def summary_errors(self):
- if self.config.option.tbstyle != "no":
- reports = self.getreports('error')
- if not reports:
- return
- self.write_sep("=", "ERRORS")
- for rep in self.stats['error']:
- msg = self._getfailureheadline(rep)
- if not hasattr(rep, 'when'):
- # collect
- msg = "ERROR collecting " + msg
- elif rep.when == "setup":
- msg = "ERROR at setup of " + msg
- elif rep.when == "teardown":
- msg = "ERROR at teardown of " + msg
- self.write_sep("_", msg)
- self._outrep_summary(rep)
-
- def _outrep_summary(self, rep):
- rep.toterminal(self._tw)
- for secname, content in rep.sections:
- self._tw.sep("-", secname)
- if content[-1:] == "\n":
- content = content[:-1]
- self._tw.line(content)
-
- def summary_stats(self):
- session_duration = time.time() - self._sessionstarttime
- (line, color) = build_summary_stats_line(self.stats)
- msg = "%s in %.2f seconds" % (line, session_duration)
- markup = {color: True, 'bold': True}
-
- if self.verbosity >= 0:
- self.write_sep("=", msg, **markup)
- if self.verbosity == -1:
- self.write_line(msg, **markup)
-
- def summary_deselected(self):
- if 'deselected' in self.stats:
- l = []
- k = self.config.option.keyword
- if k:
- l.append("-k%s" % k)
- m = self.config.option.markexpr
- if m:
- l.append("-m %r" % m)
- if l:
- self.write_sep("=", "%d tests deselected by %r" % (
- len(self.stats['deselected']), " ".join(l)), bold=True)
-
-def repr_pythonversion(v=None):
- if v is None:
- v = sys.version_info
- try:
- return "%s.%s.%s-%s-%s" % v
- except (TypeError, ValueError):
- return str(v)
-
-def flatten(l):
- for x in l:
- if isinstance(x, (list, tuple)):
- for y in flatten(x):
- yield y
- else:
- yield x
-
-def build_summary_stats_line(stats):
- keys = ("failed passed skipped deselected "
- "xfailed xpassed warnings error").split()
- key_translation = {'warnings': 'pytest-warnings'}
- unknown_key_seen = False
- for key in stats.keys():
- if key not in keys:
- if key: # setup/teardown reports have an empty key, ignore them
- keys.append(key)
- unknown_key_seen = True
- parts = []
- for key in keys:
- val = stats.get(key, None)
- if val:
- key_name = key_translation.get(key, key)
- parts.append("%d %s" % (len(val), key_name))
-
- if parts:
- line = ", ".join(parts)
- else:
- line = "no tests ran"
-
- if 'failed' in stats or 'error' in stats:
- color = 'red'
- elif 'warnings' in stats or unknown_key_seen:
- color = 'yellow'
- elif 'passed' in stats:
- color = 'green'
- else:
- color = 'yellow'
-
- return (line, color)
-
-
-def _plugin_nameversions(plugininfo):
- l = []
- for plugin, dist in plugininfo:
- # gets us name and version!
- name = '{dist.project_name}-{dist.version}'.format(dist=dist)
- # questionable convenience, but it keeps things short
- if name.startswith("pytest-"):
- name = name[7:]
- # we decided to print python package names
- # they can have more than one plugin
- if name not in l:
- l.append(name)
- return l
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/tmpdir.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/tmpdir.py
deleted file mode 100644
index ebc48dbe5b2..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/tmpdir.py
+++ /dev/null
@@ -1,123 +0,0 @@
-""" support for providing temporary directories to test functions. """
-import re
-
-import pytest
-import py
-from _pytest.monkeypatch import monkeypatch
-
-
-class TempdirFactory:
- """Factory for temporary directories under the common base temp directory.
-
- The base directory can be configured using the ``--basetemp`` option.
- """
-
- def __init__(self, config):
- self.config = config
- self.trace = config.trace.get("tmpdir")
-
- def ensuretemp(self, string, dir=1):
- """ (deprecated) return temporary directory path with
- the given string as the trailing part. It is usually
- better to use the 'tmpdir' function argument which
- provides an empty unique-per-test-invocation directory
- and is guaranteed to be empty.
- """
- #py.log._apiwarn(">1.1", "use tmpdir function argument")
- return self.getbasetemp().ensure(string, dir=dir)
-
- def mktemp(self, basename, numbered=True):
- """Create a subdirectory of the base temporary directory and return it.
- If ``numbered``, ensure the directory is unique by adding a number
- prefix greater than any existing one.
- """
- basetemp = self.getbasetemp()
- if not numbered:
- p = basetemp.mkdir(basename)
- else:
- p = py.path.local.make_numbered_dir(prefix=basename,
- keep=0, rootdir=basetemp, lock_timeout=None)
- self.trace("mktemp", p)
- return p
-
- def getbasetemp(self):
- """ return base temporary directory. """
- try:
- return self._basetemp
- except AttributeError:
- basetemp = self.config.option.basetemp
- if basetemp:
- basetemp = py.path.local(basetemp)
- if basetemp.check():
- basetemp.remove()
- basetemp.mkdir()
- else:
- temproot = py.path.local.get_temproot()
- user = get_user()
- if user:
- # use a sub-directory in the temproot to speed-up
- # make_numbered_dir() call
- rootdir = temproot.join('pytest-of-%s' % user)
- else:
- rootdir = temproot
- rootdir.ensure(dir=1)
- basetemp = py.path.local.make_numbered_dir(prefix='pytest-',
- rootdir=rootdir)
- self._basetemp = t = basetemp.realpath()
- self.trace("new basetemp", t)
- return t
-
- def finish(self):
- self.trace("finish")
-
-
-def get_user():
- """Return the current user name, or None if getuser() does not work
- in the current environment (see #1010).
- """
- import getpass
- try:
- return getpass.getuser()
- except (ImportError, KeyError):
- return None
-
-# backward compatibility
-TempdirHandler = TempdirFactory
-
-
-def pytest_configure(config):
- """Create a TempdirFactory and attach it to the config object.
-
- This is to comply with existing plugins which expect the handler to be
- available at pytest_configure time, but ideally should be moved entirely
- to the tmpdir_factory session fixture.
- """
- mp = monkeypatch()
- t = TempdirFactory(config)
- config._cleanup.extend([mp.undo, t.finish])
- mp.setattr(config, '_tmpdirhandler', t, raising=False)
- mp.setattr(pytest, 'ensuretemp', t.ensuretemp, raising=False)
-
-
-@pytest.fixture(scope='session')
-def tmpdir_factory(request):
- """Return a TempdirFactory instance for the test session.
- """
- return request.config._tmpdirhandler
-
-
-@pytest.fixture
-def tmpdir(request, tmpdir_factory):
- """return a temporary directory path object
- which is unique to each test function invocation,
- created as a sub directory of the base temporary
- directory. The returned object is a `py.path.local`_
- path object.
- """
- name = request.node.name
- name = re.sub("[\W]", "_", name)
- MAXVAL = 30
- if len(name) > MAXVAL:
- name = name[:MAXVAL]
- x = tmpdir_factory.mktemp(name, numbered=True)
- return x
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/unittest.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/unittest.py
deleted file mode 100644
index 8120e94fbf4..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/unittest.py
+++ /dev/null
@@ -1,205 +0,0 @@
-""" discovery and running of std-library "unittest" style tests. """
-from __future__ import absolute_import
-
-import sys
-import traceback
-
-import pytest
-# for transfering markers
-import _pytest._code
-from _pytest.python import transfer_markers
-from _pytest.skipping import MarkEvaluator
-
-
-def pytest_pycollect_makeitem(collector, name, obj):
- # has unittest been imported and is obj a subclass of its TestCase?
- try:
- if not issubclass(obj, sys.modules["unittest"].TestCase):
- return
- except Exception:
- return
- # yes, so let's collect it
- return UnitTestCase(name, parent=collector)
-
-
-class UnitTestCase(pytest.Class):
- # marker for fixturemanger.getfixtureinfo()
- # to declare that our children do not support funcargs
- nofuncargs = True
-
- def setup(self):
- cls = self.obj
- if getattr(cls, '__unittest_skip__', False):
- return # skipped
- setup = getattr(cls, 'setUpClass', None)
- if setup is not None:
- setup()
- teardown = getattr(cls, 'tearDownClass', None)
- if teardown is not None:
- self.addfinalizer(teardown)
- super(UnitTestCase, self).setup()
-
- def collect(self):
- from unittest import TestLoader
- cls = self.obj
- if not getattr(cls, "__test__", True):
- return
- self.session._fixturemanager.parsefactories(self, unittest=True)
- loader = TestLoader()
- module = self.getparent(pytest.Module).obj
- foundsomething = False
- for name in loader.getTestCaseNames(self.obj):
- x = getattr(self.obj, name)
- funcobj = getattr(x, 'im_func', x)
- transfer_markers(funcobj, cls, module)
- yield TestCaseFunction(name, parent=self)
- foundsomething = True
-
- if not foundsomething:
- runtest = getattr(self.obj, 'runTest', None)
- if runtest is not None:
- ut = sys.modules.get("twisted.trial.unittest", None)
- if ut is None or runtest != ut.TestCase.runTest:
- yield TestCaseFunction('runTest', parent=self)
-
-
-
-class TestCaseFunction(pytest.Function):
- _excinfo = None
-
- def setup(self):
- self._testcase = self.parent.obj(self.name)
- self._fix_unittest_skip_decorator()
- self._obj = getattr(self._testcase, self.name)
- if hasattr(self._testcase, 'setup_method'):
- self._testcase.setup_method(self._obj)
- if hasattr(self, "_request"):
- self._request._fillfixtures()
-
- def _fix_unittest_skip_decorator(self):
- """
- The @unittest.skip decorator calls functools.wraps(self._testcase)
- The call to functools.wraps() fails unless self._testcase
- has a __name__ attribute. This is usually automatically supplied
- if the test is a function or method, but we need to add manually
- here.
-
- See issue #1169
- """
- if sys.version_info[0] == 2:
- setattr(self._testcase, "__name__", self.name)
-
- def teardown(self):
- if hasattr(self._testcase, 'teardown_method'):
- self._testcase.teardown_method(self._obj)
-
- def startTest(self, testcase):
- pass
-
- def _addexcinfo(self, rawexcinfo):
- # unwrap potential exception info (see twisted trial support below)
- rawexcinfo = getattr(rawexcinfo, '_rawexcinfo', rawexcinfo)
- try:
- excinfo = _pytest._code.ExceptionInfo(rawexcinfo)
- except TypeError:
- try:
- try:
- l = traceback.format_exception(*rawexcinfo)
- l.insert(0, "NOTE: Incompatible Exception Representation, "
- "displaying natively:\n\n")
- pytest.fail("".join(l), pytrace=False)
- except (pytest.fail.Exception, KeyboardInterrupt):
- raise
- except:
- pytest.fail("ERROR: Unknown Incompatible Exception "
- "representation:\n%r" %(rawexcinfo,), pytrace=False)
- except KeyboardInterrupt:
- raise
- except pytest.fail.Exception:
- excinfo = _pytest._code.ExceptionInfo()
- self.__dict__.setdefault('_excinfo', []).append(excinfo)
-
- def addError(self, testcase, rawexcinfo):
- self._addexcinfo(rawexcinfo)
- def addFailure(self, testcase, rawexcinfo):
- self._addexcinfo(rawexcinfo)
-
- def addSkip(self, testcase, reason):
- try:
- pytest.skip(reason)
- except pytest.skip.Exception:
- self._evalskip = MarkEvaluator(self, 'SkipTest')
- self._evalskip.result = True
- self._addexcinfo(sys.exc_info())
-
- def addExpectedFailure(self, testcase, rawexcinfo, reason=""):
- try:
- pytest.xfail(str(reason))
- except pytest.xfail.Exception:
- self._addexcinfo(sys.exc_info())
-
- def addUnexpectedSuccess(self, testcase, reason=""):
- self._unexpectedsuccess = reason
-
- def addSuccess(self, testcase):
- pass
-
- def stopTest(self, testcase):
- pass
-
- def runtest(self):
- self._testcase(result=self)
-
- def _prunetraceback(self, excinfo):
- pytest.Function._prunetraceback(self, excinfo)
- traceback = excinfo.traceback.filter(
- lambda x:not x.frame.f_globals.get('__unittest'))
- if traceback:
- excinfo.traceback = traceback
-
-@pytest.hookimpl(tryfirst=True)
-def pytest_runtest_makereport(item, call):
- if isinstance(item, TestCaseFunction):
- if item._excinfo:
- call.excinfo = item._excinfo.pop(0)
- try:
- del call.result
- except AttributeError:
- pass
-
-# twisted trial support
-
-@pytest.hookimpl(hookwrapper=True)
-def pytest_runtest_protocol(item):
- if isinstance(item, TestCaseFunction) and \
- 'twisted.trial.unittest' in sys.modules:
- ut = sys.modules['twisted.python.failure']
- Failure__init__ = ut.Failure.__init__
- check_testcase_implements_trial_reporter()
- def excstore(self, exc_value=None, exc_type=None, exc_tb=None,
- captureVars=None):
- if exc_value is None:
- self._rawexcinfo = sys.exc_info()
- else:
- if exc_type is None:
- exc_type = type(exc_value)
- self._rawexcinfo = (exc_type, exc_value, exc_tb)
- try:
- Failure__init__(self, exc_value, exc_type, exc_tb,
- captureVars=captureVars)
- except TypeError:
- Failure__init__(self, exc_value, exc_type, exc_tb)
- ut.Failure.__init__ = excstore
- yield
- ut.Failure.__init__ = Failure__init__
- else:
- yield
-
-
-def check_testcase_implements_trial_reporter(done=[]):
- if done:
- return
- from zope.interface import classImplements
- from twisted.trial.itrial import IReporter
- classImplements(TestCaseFunction, IReporter)
- done.append(1)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/README.md b/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/README.md
deleted file mode 100644
index eab7c714fb0..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-This directory vendors the `pluggy` module.
-
-For a more detailed discussion for the reasons to vendoring this
-package, please see [this issue](https://github.com/pytest-dev/pytest/issues/944).
-
-To update the current version, execute:
-
-```
-$ pip install -U pluggy==<version> --no-compile --target=_pytest/vendored_packages
-```
-
-And commit the modified files. The `pluggy-<version>.dist-info` directory
-created by `pip` should be ignored.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/DESCRIPTION.rst b/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/DESCRIPTION.rst
deleted file mode 100644
index aa3bbf81297..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/DESCRIPTION.rst
+++ /dev/null
@@ -1,10 +0,0 @@
-Plugin registration and hook calling for Python
-===============================================
-
-This is the plugin manager as used by pytest but stripped
-of pytest specific details.
-
-During the 0.x series this plugin does not have much documentation
-except extensive docstrings in the pluggy.py module.
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/METADATA b/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/METADATA
deleted file mode 100644
index ec81f0a6be0..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/METADATA
+++ /dev/null
@@ -1,39 +0,0 @@
-Metadata-Version: 2.0
-Name: pluggy
-Version: 0.3.1
-Summary: plugin and hook calling mechanisms for python
-Home-page: UNKNOWN
-Author: Holger Krekel
-Author-email: holger at merlinux.eu
-License: MIT license
-Platform: unix
-Platform: linux
-Platform: osx
-Platform: win32
-Classifier: Development Status :: 4 - Beta
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: POSIX
-Classifier: Operating System :: Microsoft :: Windows
-Classifier: Operating System :: MacOS :: MacOS X
-Classifier: Topic :: Software Development :: Testing
-Classifier: Topic :: Software Development :: Libraries
-Classifier: Topic :: Utilities
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-
-Plugin registration and hook calling for Python
-===============================================
-
-This is the plugin manager as used by pytest but stripped
-of pytest specific details.
-
-During the 0.x series this plugin does not have much documentation
-except extensive docstrings in the pluggy.py module.
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/RECORD b/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/RECORD
deleted file mode 100644
index 9626673c43c..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/RECORD
+++ /dev/null
@@ -1,8 +0,0 @@
-pluggy.py,sha256=v_RfWzyW6DPU1cJu_EFoL_OHq3t13qloVdR6UaMCXQA,29862
-pluggy-0.3.1.dist-info/top_level.txt,sha256=xKSCRhai-v9MckvMuWqNz16c1tbsmOggoMSwTgcpYHE,7
-pluggy-0.3.1.dist-info/pbr.json,sha256=xX3s6__wOcAyF-AZJX1sdZyW6PUXT-FkfBlM69EEUCg,47
-pluggy-0.3.1.dist-info/RECORD,,
-pluggy-0.3.1.dist-info/metadata.json,sha256=nLKltOT78dMV-00uXD6Aeemp4xNsz2q59j6ORSDeLjw,1027
-pluggy-0.3.1.dist-info/METADATA,sha256=1b85Ho2u4iK30M099k7axMzcDDhLcIMb-A82JUJZnSo,1334
-pluggy-0.3.1.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110
-pluggy-0.3.1.dist-info/DESCRIPTION.rst,sha256=P5Akh1EdIBR6CeqtV2P8ZwpGSpZiTKPw0NyS7jEiD-g,306
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/metadata.json b/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/metadata.json
deleted file mode 100644
index 426a3a7ade1..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/metadata.json
+++ /dev/null
@@ -1 +0,0 @@
-{"license": "MIT license", "name": "pluggy", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "plugin and hook calling mechanisms for python", "platform": "unix", "version": "0.3.1", "extensions": {"python.details": {"document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "holger at merlinux.eu", "name": "Holger Krekel"}]}}, "classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Topic :: Software Development :: Testing", "Topic :: Software Development :: Libraries", "Topic :: Utilities", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5"]} \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/pbr.json b/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/pbr.json
deleted file mode 100644
index d6b79864019..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/pbr.json
+++ /dev/null
@@ -1 +0,0 @@
-{"is_release": false, "git_version": "7d4c9cd"} \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/top_level.txt b/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/top_level.txt
deleted file mode 100644
index 11bdb5c1f5f..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-pluggy
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy.py b/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy.py
deleted file mode 100644
index 2f848b23d35..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy.py
+++ /dev/null
@@ -1,777 +0,0 @@
-"""
-PluginManager, basic initialization and tracing.
-
-pluggy is the cristallized core of plugin management as used
-by some 150 plugins for pytest.
-
-Pluggy uses semantic versioning. Breaking changes are only foreseen for
-Major releases (incremented X in "X.Y.Z"). If you want to use pluggy in
-your project you should thus use a dependency restriction like
-"pluggy>=0.1.0,<1.0" to avoid surprises.
-
-pluggy is concerned with hook specification, hook implementations and hook
-calling. For any given hook specification a hook call invokes up to N implementations.
-A hook implementation can influence its position and type of execution:
-if attributed "tryfirst" or "trylast" it will be tried to execute
-first or last. However, if attributed "hookwrapper" an implementation
-can wrap all calls to non-hookwrapper implementations. A hookwrapper
-can thus execute some code ahead and after the execution of other hooks.
-
-Hook specification is done by way of a regular python function where
-both the function name and the names of all its arguments are significant.
-Each hook implementation function is verified against the original specification
-function, including the names of all its arguments. To allow for hook specifications
-to evolve over the livetime of a project, hook implementations can
-accept less arguments. One can thus add new arguments and semantics to
-a hook specification by adding another argument typically without breaking
-existing hook implementations.
-
-The chosen approach is meant to let a hook designer think carefuly about
-which objects are needed by an extension writer. By contrast, subclass-based
-extension mechanisms often expose a lot more state and behaviour than needed,
-thus restricting future developments.
-
-Pluggy currently consists of functionality for:
-
-- a way to register new hook specifications. Without a hook
- specification no hook calling can be performed.
-
-- a registry of plugins which contain hook implementation functions. It
- is possible to register plugins for which a hook specification is not yet
- known and validate all hooks when the system is in a more referentially
- consistent state. Setting an "optionalhook" attribution to a hook
- implementation will avoid PluginValidationError's if a specification
- is missing. This allows to have optional integration between plugins.
-
-- a "hook" relay object from which you can launch 1:N calls to
- registered hook implementation functions
-
-- a mechanism for ordering hook implementation functions
-
-- mechanisms for two different type of 1:N calls: "firstresult" for when
- the call should stop when the first implementation returns a non-None result.
- And the other (default) way of guaranteeing that all hook implementations
- will be called and their non-None result collected.
-
-- mechanisms for "historic" extension points such that all newly
- registered functions will receive all hook calls that happened
- before their registration.
-
-- a mechanism for discovering plugin objects which are based on
- setuptools based entry points.
-
-- a simple tracing mechanism, including tracing of plugin calls and
- their arguments.
-
-"""
-import sys
-import inspect
-
-__version__ = '0.3.1'
-__all__ = ["PluginManager", "PluginValidationError",
- "HookspecMarker", "HookimplMarker"]
-
-_py3 = sys.version_info > (3, 0)
-
-
-class HookspecMarker:
- """ Decorator helper class for marking functions as hook specifications.
-
- You can instantiate it with a project_name to get a decorator.
- Calling PluginManager.add_hookspecs later will discover all marked functions
- if the PluginManager uses the same project_name.
- """
-
- def __init__(self, project_name):
- self.project_name = project_name
-
- def __call__(self, function=None, firstresult=False, historic=False):
- """ if passed a function, directly sets attributes on the function
- which will make it discoverable to add_hookspecs(). If passed no
- function, returns a decorator which can be applied to a function
- later using the attributes supplied.
-
- If firstresult is True the 1:N hook call (N being the number of registered
- hook implementation functions) will stop at I<=N when the I'th function
- returns a non-None result.
-
- If historic is True calls to a hook will be memorized and replayed
- on later registered plugins.
-
- """
- def setattr_hookspec_opts(func):
- if historic and firstresult:
- raise ValueError("cannot have a historic firstresult hook")
- setattr(func, self.project_name + "_spec",
- dict(firstresult=firstresult, historic=historic))
- return func
-
- if function is not None:
- return setattr_hookspec_opts(function)
- else:
- return setattr_hookspec_opts
-
-
-class HookimplMarker:
- """ Decorator helper class for marking functions as hook implementations.
-
- You can instantiate with a project_name to get a decorator.
- Calling PluginManager.register later will discover all marked functions
- if the PluginManager uses the same project_name.
- """
- def __init__(self, project_name):
- self.project_name = project_name
-
- def __call__(self, function=None, hookwrapper=False, optionalhook=False,
- tryfirst=False, trylast=False):
-
- """ if passed a function, directly sets attributes on the function
- which will make it discoverable to register(). If passed no function,
- returns a decorator which can be applied to a function later using
- the attributes supplied.
-
- If optionalhook is True a missing matching hook specification will not result
- in an error (by default it is an error if no matching spec is found).
-
- If tryfirst is True this hook implementation will run as early as possible
- in the chain of N hook implementations for a specfication.
-
- If trylast is True this hook implementation will run as late as possible
- in the chain of N hook implementations.
-
- If hookwrapper is True the hook implementations needs to execute exactly
- one "yield". The code before the yield is run early before any non-hookwrapper
- function is run. The code after the yield is run after all non-hookwrapper
- function have run. The yield receives an ``_CallOutcome`` object representing
- the exception or result outcome of the inner calls (including other hookwrapper
- calls).
-
- """
- def setattr_hookimpl_opts(func):
- setattr(func, self.project_name + "_impl",
- dict(hookwrapper=hookwrapper, optionalhook=optionalhook,
- tryfirst=tryfirst, trylast=trylast))
- return func
-
- if function is None:
- return setattr_hookimpl_opts
- else:
- return setattr_hookimpl_opts(function)
-
-
-def normalize_hookimpl_opts(opts):
- opts.setdefault("tryfirst", False)
- opts.setdefault("trylast", False)
- opts.setdefault("hookwrapper", False)
- opts.setdefault("optionalhook", False)
-
-
-class _TagTracer:
- def __init__(self):
- self._tag2proc = {}
- self.writer = None
- self.indent = 0
-
- def get(self, name):
- return _TagTracerSub(self, (name,))
-
- def format_message(self, tags, args):
- if isinstance(args[-1], dict):
- extra = args[-1]
- args = args[:-1]
- else:
- extra = {}
-
- content = " ".join(map(str, args))
- indent = " " * self.indent
-
- lines = [
- "%s%s [%s]\n" % (indent, content, ":".join(tags))
- ]
-
- for name, value in extra.items():
- lines.append("%s %s: %s\n" % (indent, name, value))
- return lines
-
- def processmessage(self, tags, args):
- if self.writer is not None and args:
- lines = self.format_message(tags, args)
- self.writer(''.join(lines))
- try:
- self._tag2proc[tags](tags, args)
- except KeyError:
- pass
-
- def setwriter(self, writer):
- self.writer = writer
-
- def setprocessor(self, tags, processor):
- if isinstance(tags, str):
- tags = tuple(tags.split(":"))
- else:
- assert isinstance(tags, tuple)
- self._tag2proc[tags] = processor
-
-
-class _TagTracerSub:
- def __init__(self, root, tags):
- self.root = root
- self.tags = tags
-
- def __call__(self, *args):
- self.root.processmessage(self.tags, args)
-
- def setmyprocessor(self, processor):
- self.root.setprocessor(self.tags, processor)
-
- def get(self, name):
- return self.__class__(self.root, self.tags + (name,))
-
-
-def _raise_wrapfail(wrap_controller, msg):
- co = wrap_controller.gi_code
- raise RuntimeError("wrap_controller at %r %s:%d %s" %
- (co.co_name, co.co_filename, co.co_firstlineno, msg))
-
-
-def _wrapped_call(wrap_controller, func):
- """ Wrap calling to a function with a generator which needs to yield
- exactly once. The yield point will trigger calling the wrapped function
- and return its _CallOutcome to the yield point. The generator then needs
- to finish (raise StopIteration) in order for the wrapped call to complete.
- """
- try:
- next(wrap_controller) # first yield
- except StopIteration:
- _raise_wrapfail(wrap_controller, "did not yield")
- call_outcome = _CallOutcome(func)
- try:
- wrap_controller.send(call_outcome)
- _raise_wrapfail(wrap_controller, "has second yield")
- except StopIteration:
- pass
- return call_outcome.get_result()
-
-
-class _CallOutcome:
- """ Outcome of a function call, either an exception or a proper result.
- Calling the ``get_result`` method will return the result or reraise
- the exception raised when the function was called. """
- excinfo = None
-
- def __init__(self, func):
- try:
- self.result = func()
- except BaseException:
- self.excinfo = sys.exc_info()
-
- def force_result(self, result):
- self.result = result
- self.excinfo = None
-
- def get_result(self):
- if self.excinfo is None:
- return self.result
- else:
- ex = self.excinfo
- if _py3:
- raise ex[1].with_traceback(ex[2])
- _reraise(*ex) # noqa
-
-if not _py3:
- exec("""
-def _reraise(cls, val, tb):
- raise cls, val, tb
-""")
-
-
-class _TracedHookExecution:
- def __init__(self, pluginmanager, before, after):
- self.pluginmanager = pluginmanager
- self.before = before
- self.after = after
- self.oldcall = pluginmanager._inner_hookexec
- assert not isinstance(self.oldcall, _TracedHookExecution)
- self.pluginmanager._inner_hookexec = self
-
- def __call__(self, hook, hook_impls, kwargs):
- self.before(hook.name, hook_impls, kwargs)
- outcome = _CallOutcome(lambda: self.oldcall(hook, hook_impls, kwargs))
- self.after(outcome, hook.name, hook_impls, kwargs)
- return outcome.get_result()
-
- def undo(self):
- self.pluginmanager._inner_hookexec = self.oldcall
-
-
-class PluginManager(object):
- """ Core Pluginmanager class which manages registration
- of plugin objects and 1:N hook calling.
-
- You can register new hooks by calling ``addhooks(module_or_class)``.
- You can register plugin objects (which contain hooks) by calling
- ``register(plugin)``. The Pluginmanager is initialized with a
- prefix that is searched for in the names of the dict of registered
- plugin objects. An optional excludefunc allows to blacklist names which
- are not considered as hooks despite a matching prefix.
-
- For debugging purposes you can call ``enable_tracing()``
- which will subsequently send debug information to the trace helper.
- """
-
- def __init__(self, project_name, implprefix=None):
- """ if implprefix is given implementation functions
- will be recognized if their name matches the implprefix. """
- self.project_name = project_name
- self._name2plugin = {}
- self._plugin2hookcallers = {}
- self._plugin_distinfo = []
- self.trace = _TagTracer().get("pluginmanage")
- self.hook = _HookRelay(self.trace.root.get("hook"))
- self._implprefix = implprefix
- self._inner_hookexec = lambda hook, methods, kwargs: \
- _MultiCall(methods, kwargs, hook.spec_opts).execute()
-
- def _hookexec(self, hook, methods, kwargs):
- # called from all hookcaller instances.
- # enable_tracing will set its own wrapping function at self._inner_hookexec
- return self._inner_hookexec(hook, methods, kwargs)
-
- def register(self, plugin, name=None):
- """ Register a plugin and return its canonical name or None if the name
- is blocked from registering. Raise a ValueError if the plugin is already
- registered. """
- plugin_name = name or self.get_canonical_name(plugin)
-
- if plugin_name in self._name2plugin or plugin in self._plugin2hookcallers:
- if self._name2plugin.get(plugin_name, -1) is None:
- return # blocked plugin, return None to indicate no registration
- raise ValueError("Plugin already registered: %s=%s\n%s" %
- (plugin_name, plugin, self._name2plugin))
-
- # XXX if an error happens we should make sure no state has been
- # changed at point of return
- self._name2plugin[plugin_name] = plugin
-
- # register matching hook implementations of the plugin
- self._plugin2hookcallers[plugin] = hookcallers = []
- for name in dir(plugin):
- hookimpl_opts = self.parse_hookimpl_opts(plugin, name)
- if hookimpl_opts is not None:
- normalize_hookimpl_opts(hookimpl_opts)
- method = getattr(plugin, name)
- hookimpl = HookImpl(plugin, plugin_name, method, hookimpl_opts)
- hook = getattr(self.hook, name, None)
- if hook is None:
- hook = _HookCaller(name, self._hookexec)
- setattr(self.hook, name, hook)
- elif hook.has_spec():
- self._verify_hook(hook, hookimpl)
- hook._maybe_apply_history(hookimpl)
- hook._add_hookimpl(hookimpl)
- hookcallers.append(hook)
- return plugin_name
-
- def parse_hookimpl_opts(self, plugin, name):
- method = getattr(plugin, name)
- res = getattr(method, self.project_name + "_impl", None)
- if res is not None and not isinstance(res, dict):
- # false positive
- res = None
- elif res is None and self._implprefix and name.startswith(self._implprefix):
- res = {}
- return res
-
- def unregister(self, plugin=None, name=None):
- """ unregister a plugin object and all its contained hook implementations
- from internal data structures. """
- if name is None:
- assert plugin is not None, "one of name or plugin needs to be specified"
- name = self.get_name(plugin)
-
- if plugin is None:
- plugin = self.get_plugin(name)
-
- # if self._name2plugin[name] == None registration was blocked: ignore
- if self._name2plugin.get(name):
- del self._name2plugin[name]
-
- for hookcaller in self._plugin2hookcallers.pop(plugin, []):
- hookcaller._remove_plugin(plugin)
-
- return plugin
-
- def set_blocked(self, name):
- """ block registrations of the given name, unregister if already registered. """
- self.unregister(name=name)
- self._name2plugin[name] = None
-
- def is_blocked(self, name):
- """ return True if the name blogs registering plugins of that name. """
- return name in self._name2plugin and self._name2plugin[name] is None
-
- def add_hookspecs(self, module_or_class):
- """ add new hook specifications defined in the given module_or_class.
- Functions are recognized if they have been decorated accordingly. """
- names = []
- for name in dir(module_or_class):
- spec_opts = self.parse_hookspec_opts(module_or_class, name)
- if spec_opts is not None:
- hc = getattr(self.hook, name, None)
- if hc is None:
- hc = _HookCaller(name, self._hookexec, module_or_class, spec_opts)
- setattr(self.hook, name, hc)
- else:
- # plugins registered this hook without knowing the spec
- hc.set_specification(module_or_class, spec_opts)
- for hookfunction in (hc._wrappers + hc._nonwrappers):
- self._verify_hook(hc, hookfunction)
- names.append(name)
-
- if not names:
- raise ValueError("did not find any %r hooks in %r" %
- (self.project_name, module_or_class))
-
- def parse_hookspec_opts(self, module_or_class, name):
- method = getattr(module_or_class, name)
- return getattr(method, self.project_name + "_spec", None)
-
- def get_plugins(self):
- """ return the set of registered plugins. """
- return set(self._plugin2hookcallers)
-
- def is_registered(self, plugin):
- """ Return True if the plugin is already registered. """
- return plugin in self._plugin2hookcallers
-
- def get_canonical_name(self, plugin):
- """ Return canonical name for a plugin object. Note that a plugin
- may be registered under a different name which was specified
- by the caller of register(plugin, name). To obtain the name
- of an registered plugin use ``get_name(plugin)`` instead."""
- return getattr(plugin, "__name__", None) or str(id(plugin))
-
- def get_plugin(self, name):
- """ Return a plugin or None for the given name. """
- return self._name2plugin.get(name)
-
- def get_name(self, plugin):
- """ Return name for registered plugin or None if not registered. """
- for name, val in self._name2plugin.items():
- if plugin == val:
- return name
-
- def _verify_hook(self, hook, hookimpl):
- if hook.is_historic() and hookimpl.hookwrapper:
- raise PluginValidationError(
- "Plugin %r\nhook %r\nhistoric incompatible to hookwrapper" %
- (hookimpl.plugin_name, hook.name))
-
- for arg in hookimpl.argnames:
- if arg not in hook.argnames:
- raise PluginValidationError(
- "Plugin %r\nhook %r\nargument %r not available\n"
- "plugin definition: %s\n"
- "available hookargs: %s" %
- (hookimpl.plugin_name, hook.name, arg,
- _formatdef(hookimpl.function), ", ".join(hook.argnames)))
-
- def check_pending(self):
- """ Verify that all hooks which have not been verified against
- a hook specification are optional, otherwise raise PluginValidationError"""
- for name in self.hook.__dict__:
- if name[0] != "_":
- hook = getattr(self.hook, name)
- if not hook.has_spec():
- for hookimpl in (hook._wrappers + hook._nonwrappers):
- if not hookimpl.optionalhook:
- raise PluginValidationError(
- "unknown hook %r in plugin %r" %
- (name, hookimpl.plugin))
-
- def load_setuptools_entrypoints(self, entrypoint_name):
- """ Load modules from querying the specified setuptools entrypoint name.
- Return the number of loaded plugins. """
- from pkg_resources import iter_entry_points, DistributionNotFound
- for ep in iter_entry_points(entrypoint_name):
- # is the plugin registered or blocked?
- if self.get_plugin(ep.name) or self.is_blocked(ep.name):
- continue
- try:
- plugin = ep.load()
- except DistributionNotFound:
- continue
- self.register(plugin, name=ep.name)
- self._plugin_distinfo.append((plugin, ep.dist))
- return len(self._plugin_distinfo)
-
- def list_plugin_distinfo(self):
- """ return list of distinfo/plugin tuples for all setuptools registered
- plugins. """
- return list(self._plugin_distinfo)
-
- def list_name_plugin(self):
- """ return list of name/plugin pairs. """
- return list(self._name2plugin.items())
-
- def get_hookcallers(self, plugin):
- """ get all hook callers for the specified plugin. """
- return self._plugin2hookcallers.get(plugin)
-
- def add_hookcall_monitoring(self, before, after):
- """ add before/after tracing functions for all hooks
- and return an undo function which, when called,
- will remove the added tracers.
-
- ``before(hook_name, hook_impls, kwargs)`` will be called ahead
- of all hook calls and receive a hookcaller instance, a list
- of HookImpl instances and the keyword arguments for the hook call.
-
- ``after(outcome, hook_name, hook_impls, kwargs)`` receives the
- same arguments as ``before`` but also a :py:class:`_CallOutcome`` object
- which represents the result of the overall hook call.
- """
- return _TracedHookExecution(self, before, after).undo
-
- def enable_tracing(self):
- """ enable tracing of hook calls and return an undo function. """
- hooktrace = self.hook._trace
-
- def before(hook_name, methods, kwargs):
- hooktrace.root.indent += 1
- hooktrace(hook_name, kwargs)
-
- def after(outcome, hook_name, methods, kwargs):
- if outcome.excinfo is None:
- hooktrace("finish", hook_name, "-->", outcome.result)
- hooktrace.root.indent -= 1
-
- return self.add_hookcall_monitoring(before, after)
-
- def subset_hook_caller(self, name, remove_plugins):
- """ Return a new _HookCaller instance for the named method
- which manages calls to all registered plugins except the
- ones from remove_plugins. """
- orig = getattr(self.hook, name)
- plugins_to_remove = [plug for plug in remove_plugins if hasattr(plug, name)]
- if plugins_to_remove:
- hc = _HookCaller(orig.name, orig._hookexec, orig._specmodule_or_class,
- orig.spec_opts)
- for hookimpl in (orig._wrappers + orig._nonwrappers):
- plugin = hookimpl.plugin
- if plugin not in plugins_to_remove:
- hc._add_hookimpl(hookimpl)
- # we also keep track of this hook caller so it
- # gets properly removed on plugin unregistration
- self._plugin2hookcallers.setdefault(plugin, []).append(hc)
- return hc
- return orig
-
-
-class _MultiCall:
- """ execute a call into multiple python functions/methods. """
-
- # XXX note that the __multicall__ argument is supported only
- # for pytest compatibility reasons. It was never officially
- # supported there and is explicitly deprecated since 2.8
- # so we can remove it soon, allowing to avoid the below recursion
- # in execute() and simplify/speed up the execute loop.
-
- def __init__(self, hook_impls, kwargs, specopts={}):
- self.hook_impls = hook_impls
- self.kwargs = kwargs
- self.kwargs["__multicall__"] = self
- self.specopts = specopts
-
- def execute(self):
- all_kwargs = self.kwargs
- self.results = results = []
- firstresult = self.specopts.get("firstresult")
-
- while self.hook_impls:
- hook_impl = self.hook_impls.pop()
- args = [all_kwargs[argname] for argname in hook_impl.argnames]
- if hook_impl.hookwrapper:
- return _wrapped_call(hook_impl.function(*args), self.execute)
- res = hook_impl.function(*args)
- if res is not None:
- if firstresult:
- return res
- results.append(res)
-
- if not firstresult:
- return results
-
- def __repr__(self):
- status = "%d meths" % (len(self.hook_impls),)
- if hasattr(self, "results"):
- status = ("%d results, " % len(self.results)) + status
- return "<_MultiCall %s, kwargs=%r>" % (status, self.kwargs)
-
-
-def varnames(func, startindex=None):
- """ return argument name tuple for a function, method, class or callable.
-
- In case of a class, its "__init__" method is considered.
- For methods the "self" parameter is not included unless you are passing
- an unbound method with Python3 (which has no supports for unbound methods)
- """
- cache = getattr(func, "__dict__", {})
- try:
- return cache["_varnames"]
- except KeyError:
- pass
- if inspect.isclass(func):
- try:
- func = func.__init__
- except AttributeError:
- return ()
- startindex = 1
- else:
- if not inspect.isfunction(func) and not inspect.ismethod(func):
- func = getattr(func, '__call__', func)
- if startindex is None:
- startindex = int(inspect.ismethod(func))
-
- try:
- rawcode = func.__code__
- except AttributeError:
- return ()
- try:
- x = rawcode.co_varnames[startindex:rawcode.co_argcount]
- except AttributeError:
- x = ()
- else:
- defaults = func.__defaults__
- if defaults:
- x = x[:-len(defaults)]
- try:
- cache["_varnames"] = x
- except TypeError:
- pass
- return x
-
-
-class _HookRelay:
- """ hook holder object for performing 1:N hook calls where N is the number
- of registered plugins.
-
- """
-
- def __init__(self, trace):
- self._trace = trace
-
-
-class _HookCaller(object):
- def __init__(self, name, hook_execute, specmodule_or_class=None, spec_opts=None):
- self.name = name
- self._wrappers = []
- self._nonwrappers = []
- self._hookexec = hook_execute
- if specmodule_or_class is not None:
- assert spec_opts is not None
- self.set_specification(specmodule_or_class, spec_opts)
-
- def has_spec(self):
- return hasattr(self, "_specmodule_or_class")
-
- def set_specification(self, specmodule_or_class, spec_opts):
- assert not self.has_spec()
- self._specmodule_or_class = specmodule_or_class
- specfunc = getattr(specmodule_or_class, self.name)
- argnames = varnames(specfunc, startindex=inspect.isclass(specmodule_or_class))
- assert "self" not in argnames # sanity check
- self.argnames = ["__multicall__"] + list(argnames)
- self.spec_opts = spec_opts
- if spec_opts.get("historic"):
- self._call_history = []
-
- def is_historic(self):
- return hasattr(self, "_call_history")
-
- def _remove_plugin(self, plugin):
- def remove(wrappers):
- for i, method in enumerate(wrappers):
- if method.plugin == plugin:
- del wrappers[i]
- return True
- if remove(self._wrappers) is None:
- if remove(self._nonwrappers) is None:
- raise ValueError("plugin %r not found" % (plugin,))
-
- def _add_hookimpl(self, hookimpl):
- if hookimpl.hookwrapper:
- methods = self._wrappers
- else:
- methods = self._nonwrappers
-
- if hookimpl.trylast:
- methods.insert(0, hookimpl)
- elif hookimpl.tryfirst:
- methods.append(hookimpl)
- else:
- # find last non-tryfirst method
- i = len(methods) - 1
- while i >= 0 and methods[i].tryfirst:
- i -= 1
- methods.insert(i + 1, hookimpl)
-
- def __repr__(self):
- return "<_HookCaller %r>" % (self.name,)
-
- def __call__(self, **kwargs):
- assert not self.is_historic()
- return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
-
- def call_historic(self, proc=None, kwargs=None):
- self._call_history.append((kwargs or {}, proc))
- # historizing hooks don't return results
- self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
-
- def call_extra(self, methods, kwargs):
- """ Call the hook with some additional temporarily participating
- methods using the specified kwargs as call parameters. """
- old = list(self._nonwrappers), list(self._wrappers)
- for method in methods:
- opts = dict(hookwrapper=False, trylast=False, tryfirst=False)
- hookimpl = HookImpl(None, "<temp>", method, opts)
- self._add_hookimpl(hookimpl)
- try:
- return self(**kwargs)
- finally:
- self._nonwrappers, self._wrappers = old
-
- def _maybe_apply_history(self, method):
- if self.is_historic():
- for kwargs, proc in self._call_history:
- res = self._hookexec(self, [method], kwargs)
- if res and proc is not None:
- proc(res[0])
-
-
-class HookImpl:
- def __init__(self, plugin, plugin_name, function, hook_impl_opts):
- self.function = function
- self.argnames = varnames(self.function)
- self.plugin = plugin
- self.opts = hook_impl_opts
- self.plugin_name = plugin_name
- self.__dict__.update(hook_impl_opts)
-
-
-class PluginValidationError(Exception):
- """ plugin failed validation. """
-
-
-if hasattr(inspect, 'signature'):
- def _formatdef(func):
- return "%s%s" % (
- func.__name__,
- str(inspect.signature(func))
- )
-else:
- def _formatdef(func):
- return "%s%s" % (
- func.__name__,
- inspect.formatargspec(*inspect.getargspec(func))
- )
diff --git a/tests/wpt/web-platform-tests/tools/pytest/appveyor.yml b/tests/wpt/web-platform-tests/tools/pytest/appveyor.yml
deleted file mode 100644
index 4b73645f735..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/appveyor.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-environment:
- COVERALLS_REPO_TOKEN:
- secure: 2NJ5Ct55cHJ9WEg3xbSqCuv0rdgzzb6pnzOIG5OkMbTndw3wOBrXntWFoQrXiMFi
- # this is pytest's token in coveralls.io, encrypted
- # using pytestbot account as detailed here:
- # https://www.appveyor.com/docs/build-configuration#secure-variables
-
-install:
- - echo Installed Pythons
- - dir c:\Python*
-
- # install pypy using choco (redirect to a file and write to console in case
- # choco install returns non-zero, because choco install python.pypy is too
- # noisy)
- - choco install python.pypy > pypy-inst.log 2>&1 || (type pypy-inst.log & exit /b 1)
- - set PATH=C:\tools\pypy\pypy;%PATH% # so tox can find pypy
- - echo PyPy installed
- - pypy --version
-
- - C:\Python35\python -m pip install tox
-
-build: false # Not a C# project, build stuff at the test step instead.
-
-test_script:
- - C:\Python35\python -m tox
- # coveralls is not in tox's envlist, plus for PRs the secure variable
- # is not defined so we have to check for it
- - if defined COVERALLS_REPO_TOKEN C:\Python35\python -m tox -e coveralls
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/Makefile b/tests/wpt/web-platform-tests/tools/pytest/doc/en/Makefile
deleted file mode 100644
index 8621f779c70..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/Makefile
+++ /dev/null
@@ -1,164 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = _build
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-REGENDOC_ARGS := \
- --normalize "/={8,} (.*) ={8,}/======= \1 ========/" \
- --normalize "/_{8,} (.*) _{8,}/_______ \1 ________/" \
- --normalize "/in \d+.\d+ seconds/in 0.12 seconds/" \
- --normalize "@/tmp/pytest-of-.*/pytest-\d+@PYTEST_TMPDIR@" \
-
-
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
-
-
-help:
- @echo "Please use \`make <target>' where <target> is one of"
- @echo " html to make standalone HTML files"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " showtarget to show the pytest.org target directory"
- @echo " install to install docs to pytest.org/SITETARGET"
- @echo " install-ldf to install the doc pdf to pytest.org/SITETARGET"
- @echo " regen to regenerate pytest examples using the installed pytest"
- @echo " linkcheck to check all external links for integrity"
-
-clean:
- -rm -rf $(BUILDDIR)/*
-
-SITETARGET=$(shell ./_getdoctarget.py)
-
-showtarget:
- @echo $(SITETARGET)
-
-install: html
- # for access talk to someone with login rights to
- # pytest-dev@pytest.org to add your ssh key
- rsync -avz _build/html/ pytest-dev@pytest.org:pytest.org/$(SITETARGET)
-
-installpdf: latexpdf
- @scp $(BUILDDIR)/latex/pytest.pdf pytest-dev@pytest.org:pytest.org/$(SITETARGET)
-
-installall: clean install installpdf
- @echo "done"
-
-regen:
- PYTHONDONTWRITEBYTECODE=1 COLUMNS=76 regendoc --update *.rst */*.rst ${REGENDOC_ARGS}
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pytest.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pytest.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/pytest"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pytest"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- make -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
-
-texinfo:
- mkdir -p $(BUILDDIR)/texinfo
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo
- @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
- @echo "Run \`make' in that directory to run these through makeinfo" \
- "(use \`make info' here to do that automatically)."
-
-info:
- mkdir -p $(BUILDDIR)/texinfo
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo "Running Texinfo files through makeinfo..."
- make -C $(BUILDDIR)/texinfo info
- @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_getdoctarget.py b/tests/wpt/web-platform-tests/tools/pytest/doc/en/_getdoctarget.py
deleted file mode 100755
index 20e487bb738..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_getdoctarget.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env python
-
-import py
-
-def get_version_string():
- fn = py.path.local(__file__).join("..", "..", "..",
- "_pytest", "__init__.py")
- for line in fn.readlines():
- if "version" in line and not line.strip().startswith('#'):
- return eval(line.split("=")[-1])
-
-def get_minor_version_string():
- return ".".join(get_version_string().split(".")[:2])
-
-if __name__ == "__main__":
- print (get_minor_version_string())
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/globaltoc.html b/tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/globaltoc.html
deleted file mode 100644
index af427198a62..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/globaltoc.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
-
-<ul>
- <li><a href="{{ pathto('index') }}">Home</a></li>
- <li><a href="{{ pathto('contents') }}">Contents</a></li>
- <li><a href="{{ pathto('getting-started') }}">Install</a></li>
- <li><a href="{{ pathto('example/index') }}">Examples</a></li>
- <li><a href="{{ pathto('customize') }}">Customize</a></li>
- <li><a href="{{ pathto('contact') }}">Contact</a></li>
- <li><a href="{{ pathto('talks') }}">Talks/Posts</a></li>
- <li><a href="{{ pathto('changelog') }}">Changelog</a></li>
- <li><a href="{{ pathto('license') }}">License</a></li>
-</ul>
-
-{%- if display_toc %}
- <hr>
- {{ toc }}
-{%- endif %}
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/layout.html b/tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/layout.html
deleted file mode 100644
index 0ce480be300..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/layout.html
+++ /dev/null
@@ -1,34 +0,0 @@
-{% extends "!layout.html" %}
-{% block header %}
-<div align="center" xmlns="http://www.w3.org/1999/html" style="background-color: lightgreen; padding: .5em">
- <h4>
- Want to help improve pytest? Please
- <a href="https://www.indiegogo.com/projects/python-testing-sprint-mid-2016#/">
- contribute to
- </a>
- or
- <a href="announce/sprint2016.html">
- join
- </a>
- our upcoming sprint in June 2016!
-
- </h4>
-</div>
- {{super()}}
-{% endblock %}
-{% block footer %}
-{{ super() }}
-<script type="text/javascript">
-
- var _gaq = _gaq || [];
- _gaq.push(['_setAccount', 'UA-7597274-13']);
- _gaq.push(['_trackPageview']);
-
- (function() {
- var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
- ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
- var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
- })();
-
-</script>
-{% endblock %}
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/links.html b/tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/links.html
deleted file mode 100644
index 200258e165f..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/links.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<h3>Useful Links</h3>
-<ul>
- <li>
- <a href="https://www.indiegogo.com/projects/python-testing-sprint-mid-2016#/">
- <b>Sprint funding campaign</b>
- </a>
- </li>
- <li><a href="{{ pathto('index') }}">The pytest Website</a></li>
- <li><a href="{{ pathto('contributing') }}">Contribution Guide</a></li>
- <li><a href="https://pypi.python.org/pypi/pytest">pytest @ PyPI</a></li>
- <li><a href="https://github.com/pytest-dev/pytest/">pytest @ GitHub</a></li>
- <li><a href="http://plugincompat.herokuapp.com/">3rd party plugins</a></li>
- <li><a href="https://github.com/pytest-dev/pytest/issues">Issue Tracker</a></li>
- <li><a href="http://pytest.org/latest/pytest.pdf">PDF Documentation</a>
-</ul>
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/adopt.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/adopt.rst
deleted file mode 100644
index aead96e7f3d..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/adopt.rst
+++ /dev/null
@@ -1,78 +0,0 @@
-
-April 2015 is "adopt pytest month"
-=============================================
-
-Are you an enthusiastic pytest user, the local testing guru in your workplace? Or are you considering using pytest for your open source project, but not sure how to get started? Then you may be interested in "adopt pytest month"!
-
-We will pair experienced pytest users with open source projects, for a month's effort of getting new development teams started with pytest.
-
-In 2015 we are trying this for the first time. In February and March 2015 we will gather volunteers on both sides, in April we will do the work, and in May we will evaluate how it went. This effort is being coordinated by Brianna Laugher. If you have any questions or comments, you can raise them on the `@pytestdotorg twitter account <https://twitter.com/pytestdotorg>`_ the `issue tracker`_ or the `pytest-dev mailing list`_.
-
-
-.. _`issue tracker`: https://github.com/pytest-dev/pytest/issues/676
-.. _`pytest-dev mailing list`: https://mail.python.org/mailman/listinfo/pytest-dev
-
-
-The ideal pytest helper
------------------------------------------
-
- - will be able to commit 2-4 hours a week to working with their particular project (this might involve joining their mailing list, installing the software and exploring any existing tests, offering advice, writing some example tests)
- - feels confident in using pytest (e.g. has explored command line options, knows how to write parametrized tests, has an idea about conftest contents)
- - does not need to be an expert in every aspect!
-
-`Pytest helpers, sign up here`_! (preferably in February, hard deadline 22 March)
-
-
-.. _`Pytest helpers, sign up here`: http://goo.gl/forms/nxqAhqWt1P
-
-
-The ideal partner project
------------------------------------------
-
- - is open source, and predominantly written in Python
- - has an automated/documented install process for developers
- - has more than one core developer
- - has at least one official release (e.g. is available on pypi)
- - has the support of the core development team, in trying out pytest adoption
- - has no tests... or 100% test coverage... or somewhere in between!
-
-`Partner projects, sign up here`_! (by 22 March)
-
-
-.. _`Partner projects, sign up here`: http://goo.gl/forms/ZGyqlHiwk3
-
-
-What does it mean to "adopt pytest"?
------------------------------------------
-
-There can be many different definitions of "success". Pytest can run many `nose and unittest`_ tests by default, so using pytest as your testrunner may be possible from day 1. Job done, right?
-
-Progressive success might look like:
-
- - tests can be run (by pytest) without errors (there may be failures)
- - tests can be run (by pytest) without failures
- - test runner is integrated into CI server
- - existing tests are rewritten to take advantage of pytest features - this can happen in several iterations, for example:
- - changing to native assert_ statements (pycmd_ has a script to help with that, ``pyconvert_unittest.py``)
- - changing `setUp/tearDown methods`_ to fixtures_
- - adding markers_
- - other changes to reduce boilerplate
- - assess needs for future tests to be written, e.g. new fixtures, distributed_ testing tweaks
-
-"Success" should also include that the development team feels comfortable with their knowledge of how to use pytest. In fact this is probably more important than anything else. So spending a lot of time on communication, giving examples, etc will probably be important - both in running the tests, and in writing them.
-
-It may be after the month is up, the partner project decides that pytest is not right for it. That's okay - hopefully the pytest team will also learn something about its weaknesses or deficiencies.
-
-.. _`nose and unittest`: faq.html#how-does-pytest-relate-to-nose-and-unittest
-.. _assert: asserts.html
-.. _pycmd: https://bitbucket.org/hpk42/pycmd/overview
-.. _`setUp/tearDown methods`: xunit_setup.html
-.. _fixtures: fixture.html
-.. _markers: markers.html
-.. _distributed: xdist.html
-
-
-Other ways to help
------------------------------------------
-
-Promote! Do your favourite open source Python projects use pytest? If not, why not tell them about this page?
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/index.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/index.rst
deleted file mode 100644
index 877afff7777..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/index.rst
+++ /dev/null
@@ -1,48 +0,0 @@
-
-Release announcements
-===========================================
-
-.. toctree::
- :maxdepth: 2
-
-
- sprint2016
- release-2.9.0
- release-2.8.7
- release-2.8.6
- release-2.8.5
- release-2.8.4
- release-2.8.3
- release-2.8.2
- release-2.7.2
- release-2.7.1
- release-2.7.0
- release-2.6.3
- release-2.6.2
- release-2.6.1
- release-2.6.0
- release-2.5.2
- release-2.5.1
- release-2.5.0
- release-2.4.2
- release-2.4.1
- release-2.4.0
- release-2.3.5
- release-2.3.4
- release-2.3.3
- release-2.3.2
- release-2.3.1
- release-2.3.0
- release-2.2.4
- release-2.2.2
- release-2.2.1
- release-2.2.0
- release-2.1.3
- release-2.1.2
- release-2.1.1
- release-2.1.0
- release-2.0.3
- release-2.0.2
- release-2.0.1
- release-2.0.0
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.2.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.2.rst
deleted file mode 100644
index 733a9f7bdd8..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.2.rst
+++ /dev/null
@@ -1,73 +0,0 @@
-py.test 2.0.2: bug fixes, improved xfail/skip expressions, speed ups
-===========================================================================
-
-Welcome to pytest-2.0.2, a maintenance and bug fix release of pytest,
-a mature testing tool for Python, supporting CPython 2.4-3.2, Jython
-and latest PyPy interpreters. See the extensive docs with tested examples here:
-
- http://pytest.org/
-
-If you want to install or upgrade pytest, just type one of::
-
- pip install -U pytest # or
- easy_install -U pytest
-
-Many thanks to all issue reporters and people asking questions
-or complaining, particularly Jurko for his insistence,
-Laura, Victor and Brianna for helping with improving
-and Ronny for his general advise.
-
-best,
-holger krekel
-
-Changes between 2.0.1 and 2.0.2
-----------------------------------------------
-
-- tackle issue32 - speed up test runs of very quick test functions
- by reducing the relative overhead
-
-- fix issue30 - extended xfail/skipif handling and improved reporting.
- If you have a syntax error in your skip/xfail
- expressions you now get nice error reports.
-
- Also you can now access module globals from xfail/skipif
- expressions so that this for example works now::
-
- import pytest
- import mymodule
- @pytest.mark.skipif("mymodule.__version__[0] == "1")
- def test_function():
- pass
-
- This will not run the test function if the module's version string
- does not start with a "1". Note that specifying a string instead
- of a boolean expressions allows py.test to report meaningful information
- when summarizing a test run as to what conditions lead to skipping
- (or xfail-ing) tests.
-
-- fix issue28 - setup_method and pytest_generate_tests work together
- The setup_method fixture method now gets called also for
- test function invocations generated from the pytest_generate_tests
- hook.
-
-- fix issue27 - collectonly and keyword-selection (-k) now work together
- Also, if you do "py.test --collectonly -q" you now get a flat list
- of test ids that you can use to paste to the py.test commandline
- in order to execute a particular test.
-
-- fix issue25 avoid reported problems with --pdb and python3.2/encodings output
-
-- fix issue23 - tmpdir argument now works on Python3.2 and WindowsXP
- Starting with Python3.2 os.symlink may be supported. By requiring
- a newer py lib version the py.path.local() implementation acknowledges
- this.
-
-- fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular
- thanks to Laura Creighton who also revieved parts of the documentation.
-
-- fix slighly wrong output of verbose progress reporting for classes
- (thanks Amaury)
-
-- more precise (avoiding of) deprecation warnings for node.Class|Function accesses
-
-- avoid std unittest assertion helper code in tracebacks (thanks Ronny)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.3.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.3.rst
deleted file mode 100644
index ed746e8519b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.3.rst
+++ /dev/null
@@ -1,40 +0,0 @@
-py.test 2.0.3: bug fixes and speed ups
-===========================================================================
-
-Welcome to pytest-2.0.3, a maintenance and bug fix release of pytest,
-a mature testing tool for Python, supporting CPython 2.4-3.2, Jython
-and latest PyPy interpreters. See the extensive docs with tested examples here:
-
- http://pytest.org/
-
-If you want to install or upgrade pytest, just type one of::
-
- pip install -U pytest # or
- easy_install -U pytest
-
-There also is a bugfix release 1.6 of pytest-xdist, the plugin
-that enables seemless distributed and "looponfail" testing for Python.
-
-best,
-holger krekel
-
-Changes between 2.0.2 and 2.0.3
-----------------------------------------------
-
-- fix issue38: nicer tracebacks on calls to hooks, particularly early
- configure/sessionstart ones
-
-- fix missing skip reason/meta information in junitxml files, reported
- via http://lists.idyll.org/pipermail/testing-in-python/2011-March/003928.html
-
-- fix issue34: avoid collection failure with "test" prefixed classes
- deriving from object.
-
-- don't require zlib (and other libs) for genscript plugin without
- --genscript actually being used.
-
-- speed up skips (by not doing a full traceback represenation
- internally)
-
-- fix issue37: avoid invalid characters in junitxml's output
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.1.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.1.rst
deleted file mode 100644
index f9764634c72..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.1.rst
+++ /dev/null
@@ -1,41 +0,0 @@
-pytest-2.2.1: bug fixes, perfect teardowns
-===========================================================================
-
-
-pytest-2.2.1 is a minor backward-compatible release of the the py.test
-testing tool. It contains bug fixes and little improvements, including
-documentation fixes. If you are using the distributed testing
-pluginmake sure to upgrade it to pytest-xdist-1.8.
-
-For general information see here:
-
- http://pytest.org/
-
-To install or upgrade pytest:
-
- pip install -U pytest # or
- easy_install -U pytest
-
-Special thanks for helping on this release to Ronny Pfannschmidt, Jurko
-Gospodnetic and Ralf Schmitt.
-
-best,
-holger krekel
-
-
-Changes between 2.2.0 and 2.2.1
-----------------------------------------
-
-- fix issue99 (in pytest and py) internallerrors with resultlog now
- produce better output - fixed by normalizing pytest_internalerror
- input arguments.
-- fix issue97 / traceback issues (in pytest and py) improve traceback output
- in conjunction with jinja2 and cython which hack tracebacks
-- fix issue93 (in pytest and pytest-xdist) avoid "delayed teardowns":
- the final test in a test node will now run its teardown directly
- instead of waiting for the end of the session. Thanks Dave Hunt for
- the good reporting and feedback. The pytest_runtest_protocol as well
- as the pytest_runtest_teardown hooks now have "nextitem" available
- which will be None indicating the end of the test run.
-- fix collection crash due to unknown-source collected items, thanks
- to Ralf Schmitt (fixed by depending on a more recent pylib)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.4.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.4.rst
deleted file mode 100644
index 8720bdb28a7..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.4.rst
+++ /dev/null
@@ -1,39 +0,0 @@
-pytest-2.2.4: bug fixes, better junitxml/unittest/python3 compat
-===========================================================================
-
-pytest-2.2.4 is a minor backward-compatible release of the versatile
-py.test testing tool. It contains bug fixes and a few refinements
-to junitxml reporting, better unittest- and python3 compatibility.
-
-For general information see here:
-
- http://pytest.org/
-
-To install or upgrade pytest:
-
- pip install -U pytest # or
- easy_install -U pytest
-
-Special thanks for helping on this release to Ronny Pfannschmidt
-and Benjamin Peterson and the contributors of issues.
-
-best,
-holger krekel
-
-Changes between 2.2.3 and 2.2.4
------------------------------------
-
-- fix error message for rewritten assertions involving the % operator
-- fix issue 126: correctly match all invalid xml characters for junitxml
- binary escape
-- fix issue with unittest: now @unittest.expectedFailure markers should
- be processed correctly (you can also use @pytest.mark markers)
-- document integration with the extended distribute/setuptools test commands
-- fix issue 140: propperly get the real functions
- of bound classmethods for setup/teardown_class
-- fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net
-- fix issue #143: call unconfigure/sessionfinish always when
- configure/sessionstart where called
-- fix issue #144: better mangle test ids to junitxml classnames
-- upgrade distribute_setup.py to 0.6.27
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.0.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.0.rst
deleted file mode 100644
index 54fe3961fd8..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.0.rst
+++ /dev/null
@@ -1,134 +0,0 @@
-pytest-2.3: improved fixtures / better unittest integration
-=============================================================================
-
-pytest-2.3 comes with many major improvements for fixture/funcarg management
-and parametrized testing in Python. It is now easier, more efficient and
-more predicatable to re-run the same tests with different fixture
-instances. Also, you can directly declare the caching "scope" of
-fixtures so that dependent tests throughout your whole test suite can
-re-use database or other expensive fixture objects with ease. Lastly,
-it's possible for fixture functions (formerly known as funcarg
-factories) to use other fixtures, allowing for a completely modular and
-re-useable fixture design.
-
-For detailed info and tutorial-style examples, see:
-
- http://pytest.org/latest/fixture.html
-
-Moreover, there is now support for using pytest fixtures/funcargs with
-unittest-style suites, see here for examples:
-
- http://pytest.org/latest/unittest.html
-
-Besides, more unittest-test suites are now expected to "simply work"
-with pytest.
-
-All changes are backward compatible and you should be able to continue
-to run your test suites and 3rd party plugins that worked with
-pytest-2.2.4.
-
-If you are interested in the precise reasoning (including examples) of the
-pytest-2.3 fixture evolution, please consult
-http://pytest.org/latest/funcarg_compare.html
-
-For general info on installation and getting started:
-
- http://pytest.org/latest/getting-started.html
-
-Docs and PDF access as usual at:
-
- http://pytest.org
-
-and more details for those already in the knowing of pytest can be found
-in the CHANGELOG below.
-
-Particular thanks for this release go to Floris Bruynooghe, Alex Okrushko
-Carl Meyer, Ronny Pfannschmidt, Benjamin Peterson and Alex Gaynor for helping
-to get the new features right and well integrated. Ronny and Floris
-also helped to fix a number of bugs and yet more people helped by
-providing bug reports.
-
-have fun,
-holger krekel
-
-
-Changes between 2.2.4 and 2.3.0
------------------------------------
-
-- fix issue202 - better automatic names for parametrized test functions
-- fix issue139 - introduce @pytest.fixture which allows direct scoping
- and parametrization of funcarg factories. Introduce new @pytest.setup
- marker to allow the writing of setup functions which accept funcargs.
-- fix issue198 - conftest fixtures were not found on windows32 in some
- circumstances with nested directory structures due to path manipulation issues
-- fix issue193 skip test functions with were parametrized with empty
- parameter sets
-- fix python3.3 compat, mostly reporting bits that previously depended
- on dict ordering
-- introduce re-ordering of tests by resource and parametrization setup
- which takes precedence to the usual file-ordering
-- fix issue185 monkeypatching time.time does not cause pytest to fail
-- fix issue172 duplicate call of pytest.setup-decoratored setup_module
- functions
-- fix junitxml=path construction so that if tests change the
- current working directory and the path is a relative path
- it is constructed correctly from the original current working dir.
-- fix "python setup.py test" example to cause a proper "errno" return
-- fix issue165 - fix broken doc links and mention stackoverflow for FAQ
-- catch unicode-issues when writing failure representations
- to terminal to prevent the whole session from crashing
-- fix xfail/skip confusion: a skip-mark or an imperative pytest.skip
- will now take precedence before xfail-markers because we
- can't determine xfail/xpass status in case of a skip. see also:
- http://stackoverflow.com/questions/11105828/in-py-test-when-i-explicitly-skip-a-test-that-is-marked-as-xfail-how-can-i-get
-
-- always report installed 3rd party plugins in the header of a test run
-
-- fix issue160: a failing setup of an xfail-marked tests should
- be reported as xfail (not xpass)
-
-- fix issue128: show captured output when capsys/capfd are used
-
-- fix issue179: propperly show the dependency chain of factories
-
-- pluginmanager.register(...) now raises ValueError if the
- plugin has been already registered or the name is taken
-
-- fix issue159: improve http://pytest.org/latest/faq.html
- especially with respect to the "magic" history, also mention
- pytest-django, trial and unittest integration.
-
-- make request.keywords and node.keywords writable. All descendant
- collection nodes will see keyword values. Keywords are dictionaries
- containing markers and other info.
-
-- fix issue 178: xml binary escapes are now wrapped in py.xml.raw
-
-- fix issue 176: correctly catch the builtin AssertionError
- even when we replaced AssertionError with a subclass on the
- python level
-
-- factory discovery no longer fails with magic global callables
- that provide no sane __code__ object (mock.call for example)
-
-- fix issue 182: testdir.inprocess_run now considers passed plugins
-
-- fix issue 188: ensure sys.exc_info is clear on python2
- before calling into a test
-
-- fix issue 191: add unittest TestCase runTest method support
-- fix issue 156: monkeypatch correctly handles class level descriptors
-
-- reporting refinements:
-
- - pytest_report_header now receives a "startdir" so that
- you can use startdir.bestrelpath(yourpath) to show
- nice relative path
-
- - allow plugins to implement both pytest_report_header and
- pytest_sessionstart (sessionstart is invoked first).
-
- - don't show deselected reason line if there is none
-
- - py.test -vv will show all of assert comparisations instead of truncating
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.2.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.2.rst
deleted file mode 100644
index 948b374d43b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.2.rst
+++ /dev/null
@@ -1,57 +0,0 @@
-pytest-2.3.2: some fixes and more traceback-printing speed
-===========================================================================
-
-pytest-2.3.2 is a another stabilization release:
-
-- issue 205: fixes a regression with conftest detection
-- issue 208/29: fixes traceback-printing speed in some bad cases
-- fix teardown-ordering for parametrized setups
-- fix unittest and trial compat behaviour with respect to runTest() methods
-- issue 206 and others: some improvements to packaging
-- fix issue127 and others: improve some docs
-
-See
-
- http://pytest.org/
-
-for general information. To install or upgrade pytest:
-
- pip install -U pytest # or
- easy_install -U pytest
-
-best,
-holger krekel
-
-
-Changes between 2.3.1 and 2.3.2
------------------------------------
-
-- fix issue208 and fix issue29 use new py version to avoid long pauses
- when printing tracebacks in long modules
-
-- fix issue205 - conftests in subdirs customizing
- pytest_pycollect_makemodule and pytest_pycollect_makeitem
- now work properly
-
-- fix teardown-ordering for parametrized setups
-
-- fix issue127 - better documentation for pytest_addoption
- and related objects.
-
-- fix unittest behaviour: TestCase.runtest only called if there are
- test methods defined
-
-- improve trial support: don't collect its empty
- unittest.TestCase.runTest() method
-
-- "python setup.py test" now works with pytest itself
-
-- fix/improve internal/packaging related bits:
-
- - exception message check of test_nose.py now passes on python33 as well
-
- - issue206 - fix test_assertrewrite.py to work when a global
- PYTHONDONTWRITEBYTECODE=1 is present
-
- - add tox.ini to pytest distribution so that ignore-dirs and others config
- bits are properly distributed for maintainers who run pytest-own tests
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.3.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.3.rst
deleted file mode 100644
index 1d7c7027bed..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.3.rst
+++ /dev/null
@@ -1,62 +0,0 @@
-pytest-2.3.3: integration fixes, py24 suport, ``*/**`` shown in traceback
-===========================================================================
-
-pytest-2.3.3 is a another stabilization release of the py.test tool
-which offers uebersimple assertions, scalable fixture mechanisms
-and deep customization for testing with Python. Particularly,
-this release provides:
-
-- integration fixes and improvements related to flask, numpy, nose,
- unittest, mock
-
-- makes pytest work on py24 again (yes, people sometimes still need to use it)
-
-- show ``*,**`` args in pytest tracebacks
-
-Thanks to Manuel Jacob, Thomas Waldmann, Ronny Pfannschmidt, Pavel Repin
-and Andreas Taumoefolau for providing patches and all for the issues.
-
-See
-
- http://pytest.org/
-
-for general information. To install or upgrade pytest:
-
- pip install -U pytest # or
- easy_install -U pytest
-
-best,
-holger krekel
-
-Changes between 2.3.2 and 2.3.3
------------------------------------
-
-- fix issue214 - parse modules that contain special objects like e. g.
- flask's request object which blows up on getattr access if no request
- is active. thanks Thomas Waldmann.
-
-- fix issue213 - allow to parametrize with values like numpy arrays that
- do not support an __eq__ operator
-
-- fix issue215 - split test_python.org into multiple files
-
-- fix issue148 - @unittest.skip on classes is now recognized and avoids
- calling setUpClass/tearDownClass, thanks Pavel Repin
-
-- fix issue209 - reintroduce python2.4 support by depending on newer
- pylib which re-introduced statement-finding for pre-AST interpreters
-
-- nose support: only call setup if its a callable, thanks Andrew
- Taumoefolau
-
-- fix issue219 - add py2.4-3.3 classifiers to TROVE list
-
-- in tracebacks *,** arg values are now shown next to normal arguments
- (thanks Manuel Jacob)
-
-- fix issue217 - support mock.patch with pytest's fixtures - note that
- you need either mock-1.0.1 or the python3.3 builtin unittest.mock.
-
-- fix issue127 - improve documentation for pytest_addoption() and
- add a ``config.getoption(name)`` helper function for consistency.
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.5.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.5.rst
deleted file mode 100644
index c4e91e0e614..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.5.rst
+++ /dev/null
@@ -1,97 +0,0 @@
-pytest-2.3.5: bug fixes and little improvements
-===========================================================================
-
-pytest-2.3.5 is a maintenance release with many bug fixes and little
-improvements. See the changelog below for details. No backward
-compatibility issues are foreseen and all plugins which worked with the
-prior version are expected to work unmodified. Speaking of which, a
-few interesting new plugins saw the light last month:
-
-- pytest-instafail: show failure information while tests are running
-- pytest-qt: testing of GUI applications written with QT/Pyside
-- pytest-xprocess: managing external processes across test runs
-- pytest-random: randomize test ordering
-
-And several others like pytest-django saw maintenance releases.
-For a more complete list, check out
-https://pypi.python.org/pypi?%3Aaction=search&term=pytest&submit=search.
-
-For general information see:
-
- http://pytest.org/
-
-To install or upgrade pytest:
-
- pip install -U pytest # or
- easy_install -U pytest
-
-Particular thanks to Floris, Ronny, Benjamin and the many bug reporters
-and fix providers.
-
-may the fixtures be with you,
-holger krekel
-
-
-Changes between 2.3.4 and 2.3.5
------------------------------------
-
-- never consider a fixture function for test function collection
-
-- allow re-running of test items / helps to fix pytest-reruntests plugin
- and also help to keep less fixture/resource references alive
-
-- put captured stdout/stderr into junitxml output even for passing tests
- (thanks Adam Goucher)
-
-- Issue 265 - integrate nose setup/teardown with setupstate
- so it doesnt try to teardown if it did not setup
-
-- issue 271 - dont write junitxml on slave nodes
-
-- Issue 274 - dont try to show full doctest example
- when doctest does not know the example location
-
-- issue 280 - disable assertion rewriting on buggy CPython 2.6.0
-
-- inject "getfixture()" helper to retrieve fixtures from doctests,
- thanks Andreas Zeidler
-
-- issue 259 - when assertion rewriting, be consistent with the default
- source encoding of ASCII on Python 2
-
-- issue 251 - report a skip instead of ignoring classes with init
-
-- issue250 unicode/str mixes in parametrization names and values now works
-
-- issue257, assertion-triggered compilation of source ending in a
- comment line doesn't blow up in python2.5 (fixed through py>=1.4.13.dev6)
-
-- fix --genscript option to generate standalone scripts that also
- work with python3.3 (importer ordering)
-
-- issue171 - in assertion rewriting, show the repr of some
- global variables
-
-- fix option help for "-k"
-
-- move long description of distribution into README.rst
-
-- improve docstring for metafunc.parametrize()
-
-- fix bug where using capsys with pytest.set_trace() in a test
- function would break when looking at capsys.readouterr()
-
-- allow to specify prefixes starting with "_" when
- customizing python_functions test discovery. (thanks Graham Horler)
-
-- improve PYTEST_DEBUG tracing output by puting
- extra data on a new lines with additional indent
-
-- ensure OutcomeExceptions like skip/fail have initialized exception attributes
-
-- issue 260 - don't use nose special setup on plain unittest cases
-
-- fix issue134 - print the collect errors that prevent running specified test items
-
-- fix issue266 - accept unicode in MarkEvaluator expressions
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.4.0.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.4.0.rst
deleted file mode 100644
index 88130c481f1..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.4.0.rst
+++ /dev/null
@@ -1,225 +0,0 @@
-pytest-2.4.0: new fixture features/hooks and bug fixes
-===========================================================================
-
-The just released pytest-2.4.0 brings many improvements and numerous
-bug fixes while remaining plugin- and test-suite compatible apart
-from a few supposedly very minor incompatibilities. See below for
-a full list of details. A few feature highlights:
-
-- new yield-style fixtures `pytest.yield_fixture
- <http://pytest.org/latest/yieldfixture.html>`_, allowing to use
- existing with-style context managers in fixture functions.
-
-- improved pdb support: ``import pdb ; pdb.set_trace()`` now works
- without requiring prior disabling of stdout/stderr capturing.
- Also the ``--pdb`` options works now on collection and internal errors
- and we introduced a new experimental hook for IDEs/plugins to
- intercept debugging: ``pytest_exception_interact(node, call, report)``.
-
-- shorter monkeypatch variant to allow specifying an import path as
- a target, for example: ``monkeypatch.setattr("requests.get", myfunc)``
-
-- better unittest/nose compatibility: all teardown methods are now only
- called if the corresponding setup method succeeded.
-
-- integrate tab-completion on command line options if you
- have `argcomplete <http://pypi.python.org/pypi/argcomplete>`_
- configured.
-
-- allow boolean expression directly with skipif/xfail
- if a "reason" is also specified.
-
-- a new hook ``pytest_load_initial_conftests`` allows plugins like
- `pytest-django <http://pypi.python.org/pypi/pytest-django>`_ to
- influence the environment before conftest files import ``django``.
-
-- reporting: color the last line red or green depending if
- failures/errors occurred or everything passed.
-
-The documentation has been updated to accomodate the changes,
-see `http://pytest.org <http://pytest.org>`_
-
-To install or upgrade pytest::
-
- pip install -U pytest # or
- easy_install -U pytest
-
-
-**Many thanks to all who helped, including Floris Bruynooghe,
-Brianna Laugher, Andreas Pelme, Anthon van der Neut, Anatoly Bubenkoff,
-Vladimir Keleshev, Mathieu Agopian, Ronny Pfannschmidt, Christian
-Theunert and many others.**
-
-may passing tests be with you,
-
-holger krekel
-
-Changes between 2.3.5 and 2.4
------------------------------------
-
-known incompatibilities:
-
-- if calling --genscript from python2.7 or above, you only get a
- standalone script which works on python2.7 or above. Use Python2.6
- to also get a python2.5 compatible version.
-
-- all xunit-style teardown methods (nose-style, pytest-style,
- unittest-style) will not be called if the corresponding setup method failed,
- see issue322 below.
-
-- the pytest_plugin_unregister hook wasn't ever properly called
- and there is no known implementation of the hook - so it got removed.
-
-- pytest.fixture-decorated functions cannot be generators (i.e. use
- yield) anymore. This change might be reversed in 2.4.1 if it causes
- unforeseen real-life issues. However, you can always write and return
- an inner function/generator and change the fixture consumer to iterate
- over the returned generator. This change was done in lieu of the new
- ``pytest.yield_fixture`` decorator, see below.
-
-new features:
-
-- experimentally introduce a new ``pytest.yield_fixture`` decorator
- which accepts exactly the same parameters as pytest.fixture but
- mandates a ``yield`` statement instead of a ``return statement`` from
- fixture functions. This allows direct integration with "with-style"
- context managers in fixture functions and generally avoids registering
- of finalization callbacks in favour of treating the "after-yield" as
- teardown code. Thanks Andreas Pelme, Vladimir Keleshev, Floris
- Bruynooghe, Ronny Pfannschmidt and many others for discussions.
-
-- allow boolean expression directly with skipif/xfail
- if a "reason" is also specified. Rework skipping documentation
- to recommend "condition as booleans" because it prevents surprises
- when importing markers between modules. Specifying conditions
- as strings will remain fully supported.
-
-- reporting: color the last line red or green depending if
- failures/errors occurred or everything passed. thanks Christian
- Theunert.
-
-- make "import pdb ; pdb.set_trace()" work natively wrt capturing (no
- "-s" needed anymore), making ``pytest.set_trace()`` a mere shortcut.
-
-- fix issue181: --pdb now also works on collect errors (and
- on internal errors) . This was implemented by a slight internal
- refactoring and the introduction of a new hook
- ``pytest_exception_interact`` hook (see next item).
-
-- fix issue341: introduce new experimental hook for IDEs/terminals to
- intercept debugging: ``pytest_exception_interact(node, call, report)``.
-
-- new monkeypatch.setattr() variant to provide a shorter
- invocation for patching out classes/functions from modules:
-
- monkeypatch.setattr("requests.get", myfunc)
-
- will replace the "get" function of the "requests" module with ``myfunc``.
-
-- fix issue322: tearDownClass is not run if setUpClass failed. Thanks
- Mathieu Agopian for the initial fix. Also make all of pytest/nose
- finalizer mimick the same generic behaviour: if a setupX exists and
- fails, don't run teardownX. This internally introduces a new method
- "node.addfinalizer()" helper which can only be called during the setup
- phase of a node.
-
-- simplify pytest.mark.parametrize() signature: allow to pass a
- CSV-separated string to specify argnames. For example:
- ``pytest.mark.parametrize("input,expected", [(1,2), (2,3)])``
- works as well as the previous:
- ``pytest.mark.parametrize(("input", "expected"), ...)``.
-
-- add support for setUpModule/tearDownModule detection, thanks Brian Okken.
-
-- integrate tab-completion on options through use of "argcomplete".
- Thanks Anthon van der Neut for the PR.
-
-- change option names to be hyphen-separated long options but keep the
- old spelling backward compatible. py.test -h will only show the
- hyphenated version, for example "--collect-only" but "--collectonly"
- will remain valid as well (for backward-compat reasons). Many thanks to
- Anthon van der Neut for the implementation and to Hynek Schlawack for
- pushing us.
-
-- fix issue 308 - allow to mark/xfail/skip individual parameter sets
- when parametrizing. Thanks Brianna Laugher.
-
-- call new experimental pytest_load_initial_conftests hook to allow
- 3rd party plugins to do something before a conftest is loaded.
-
-Bug fixes:
-
-- fix issue358 - capturing options are now parsed more properly
- by using a new parser.parse_known_args method.
-
-- pytest now uses argparse instead of optparse (thanks Anthon) which
- means that "argparse" is added as a dependency if installing into python2.6
- environments or below.
-
-- fix issue333: fix a case of bad unittest/pytest hook interaction.
-
-- PR27: correctly handle nose.SkipTest during collection. Thanks
- Antonio Cuni, Ronny Pfannschmidt.
-
-- fix issue355: junitxml puts name="pytest" attribute to testsuite tag.
-
-- fix issue336: autouse fixture in plugins should work again.
-
-- fix issue279: improve object comparisons on assertion failure
- for standard datatypes and recognise collections.abc. Thanks to
- Brianna Laugher and Mathieu Agopian.
-
-- fix issue317: assertion rewriter support for the is_package method
-
-- fix issue335: document py.code.ExceptionInfo() object returned
- from pytest.raises(), thanks Mathieu Agopian.
-
-- remove implicit distribute_setup support from setup.py.
-
-- fix issue305: ignore any problems when writing pyc files.
-
-- SO-17664702: call fixture finalizers even if the fixture function
- partially failed (finalizers would not always be called before)
-
-- fix issue320 - fix class scope for fixtures when mixed with
- module-level functions. Thanks Anatloy Bubenkoff.
-
-- you can specify "-q" or "-qq" to get different levels of "quieter"
- reporting (thanks Katarzyna Jachim)
-
-- fix issue300 - Fix order of conftest loading when starting py.test
- in a subdirectory.
-
-- fix issue323 - sorting of many module-scoped arg parametrizations
-
-- make sessionfinish hooks execute with the same cwd-context as at
- session start (helps fix plugin behaviour which write output files
- with relative path such as pytest-cov)
-
-- fix issue316 - properly reference collection hooks in docs
-
-- fix issue 306 - cleanup of -k/-m options to only match markers/test
- names/keywords respectively. Thanks Wouter van Ackooy.
-
-- improved doctest counting for doctests in python modules --
- files without any doctest items will not show up anymore
- and doctest examples are counted as separate test items.
- thanks Danilo Bellini.
-
-- fix issue245 by depending on the released py-1.4.14
- which fixes py.io.dupfile to work with files with no
- mode. Thanks Jason R. Coombs.
-
-- fix junitxml generation when test output contains control characters,
- addressing issue267, thanks Jaap Broekhuizen
-
-- fix issue338: honor --tb style for setup/teardown errors as well. Thanks Maho.
-
-- fix issue307 - use yaml.safe_load in example, thanks Mark Eichin.
-
-- better parametrize error messages, thanks Brianna Laugher
-
-- pytest_terminal_summary(terminalreporter) hooks can now use
- ".section(title)" and ".line(msg)" methods to print extra
- information at the end of a test run.
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.5.0.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.5.0.rst
deleted file mode 100644
index b8f28d6fd5e..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.5.0.rst
+++ /dev/null
@@ -1,175 +0,0 @@
-pytest-2.5.0: now down to ZERO reported bugs!
-===========================================================================
-
-pytest-2.5.0 is a big fixing release, the result of two community bug
-fixing days plus numerous additional works from many people and
-reporters. The release should be fully compatible to 2.4.2, existing
-plugins and test suites. We aim at maintaining this level of ZERO reported
-bugs because it's no fun if your testing tool has bugs, is it? Under a
-condition, though: when submitting a bug report please provide
-clear information about the circumstances and a simple example which
-reproduces the problem.
-
-The issue tracker is of course not empty now. We have many remaining
-"enhacement" issues which we'll hopefully can tackle in 2014 with your
-help.
-
-For those who use older Python versions, please note that pytest is not
-automatically tested on python2.5 due to virtualenv, setuptools and tox
-not supporting it anymore. Manual verification shows that it mostly
-works fine but it's not going to be part of the automated release
-process and thus likely to break in the future.
-
-As usual, current docs are at
-
- http://pytest.org
-
-and you can upgrade from pypi via::
-
- pip install -U pytest
-
-Particular thanks for helping with this release go to Anatoly Bubenkoff,
-Floris Bruynooghe, Marc Abramowitz, Ralph Schmitt, Ronny Pfannschmidt,
-Donald Stufft, James Lan, Rob Dennis, Jason R. Coombs, Mathieu Agopian,
-Virgil Dupras, Bruno Oliveira, Alex Gaynor and others.
-
-have fun,
-holger krekel
-
-
-2.5.0
------------------------------------
-
-- dropped python2.5 from automated release testing of pytest itself
- which means it's probably going to break soon (but still works
- with this release we believe).
-
-- simplified and fixed implementation for calling finalizers when
- parametrized fixtures or function arguments are involved. finalization
- is now performed lazily at setup time instead of in the "teardown phase".
- While this might sound odd at first, it helps to ensure that we are
- correctly handling setup/teardown even in complex code. User-level code
- should not be affected unless it's implementing the pytest_runtest_teardown
- hook and expecting certain fixture instances are torn down within (very
- unlikely and would have been unreliable anyway).
-
-- PR90: add --color=yes|no|auto option to force terminal coloring
- mode ("auto" is default). Thanks Marc Abramowitz.
-
-- fix issue319 - correctly show unicode in assertion errors. Many
- thanks to Floris Bruynooghe for the complete PR. Also means
- we depend on py>=1.4.19 now.
-
-- fix issue396 - correctly sort and finalize class-scoped parametrized
- tests independently from number of methods on the class.
-
-- refix issue323 in a better way -- parametrization should now never
- cause Runtime Recursion errors because the underlying algorithm
- for re-ordering tests per-scope/per-fixture is not recursive
- anymore (it was tail-call recursive before which could lead
- to problems for more than >966 non-function scoped parameters).
-
-- fix issue290 - there is preliminary support now for parametrizing
- with repeated same values (sometimes useful to to test if calling
- a second time works as with the first time).
-
-- close issue240 - document precisely how pytest module importing
- works, discuss the two common test directory layouts, and how it
- interacts with PEP420-namespace packages.
-
-- fix issue246 fix finalizer order to be LIFO on independent fixtures
- depending on a parametrized higher-than-function scoped fixture.
- (was quite some effort so please bear with the complexity of this sentence :)
- Thanks Ralph Schmitt for the precise failure example.
-
-- fix issue244 by implementing special index for parameters to only use
- indices for paramentrized test ids
-
-- fix issue287 by running all finalizers but saving the exception
- from the first failing finalizer and re-raising it so teardown will
- still have failed. We reraise the first failing exception because
- it might be the cause for other finalizers to fail.
-
-- fix ordering when mock.patch or other standard decorator-wrappings
- are used with test methods. This fixues issue346 and should
- help with random "xdist" collection failures. Thanks to
- Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
-
-- fix issue357 - special case "-k" expressions to allow for
- filtering with simple strings that are not valid python expressions.
- Examples: "-k 1.3" matches all tests parametrized with 1.3.
- "-k None" filters all tests that have "None" in their name
- and conversely "-k 'not None'".
- Previously these examples would raise syntax errors.
-
-- fix issue384 by removing the trial support code
- since the unittest compat enhancements allow
- trial to handle it on its own
-
-- don't hide an ImportError when importing a plugin produces one.
- fixes issue375.
-
-- fix issue275 - allow usefixtures and autouse fixtures
- for running doctest text files.
-
-- fix issue380 by making --resultlog only rely on longrepr instead
- of the "reprcrash" attribute which only exists sometimes.
-
-- address issue122: allow @pytest.fixture(params=iterator) by exploding
- into a list early on.
-
-- fix pexpect-3.0 compatibility for pytest's own tests.
- (fixes issue386)
-
-- allow nested parametrize-value markers, thanks James Lan for the PR.
-
-- fix unicode handling with new monkeypatch.setattr(import_path, value)
- API. Thanks Rob Dennis. Fixes issue371.
-
-- fix unicode handling with junitxml, fixes issue368.
-
-- In assertion rewriting mode on Python 2, fix the detection of coding
- cookies. See issue #330.
-
-- make "--runxfail" turn imperative pytest.xfail calls into no ops
- (it already did neutralize pytest.mark.xfail markers)
-
-- refine pytest / pkg_resources interactions: The AssertionRewritingHook
- PEP302 compliant loader now registers itself with setuptools/pkg_resources
- properly so that the pkg_resources.resource_stream method works properly.
- Fixes issue366. Thanks for the investigations and full PR to Jason R. Coombs.
-
-- pytestconfig fixture is now session-scoped as it is the same object during the
- whole test run. Fixes issue370.
-
-- avoid one surprising case of marker malfunction/confusion::
-
- @pytest.mark.some(lambda arg: ...)
- def test_function():
-
- would not work correctly because pytest assumes @pytest.mark.some
- gets a function to be decorated already. We now at least detect if this
- arg is an lambda and thus the example will work. Thanks Alex Gaynor
- for bringing it up.
-
-- xfail a test on pypy that checks wrong encoding/ascii (pypy does
- not error out). fixes issue385.
-
-- internally make varnames() deal with classes's __init__,
- although it's not needed by pytest itself atm. Also
- fix caching. Fixes issue376.
-
-- fix issue221 - handle importing of namespace-package with no
- __init__.py properly.
-
-- refactor internal FixtureRequest handling to avoid monkeypatching.
- One of the positive user-facing effects is that the "request" object
- can now be used in closures.
-
-- fixed version comparison in pytest.importskip(modname, minverstring)
-
-- fix issue377 by clarifying in the nose-compat docs that pytest
- does not duplicate the unittest-API into the "plain" namespace.
-
-- fix verbose reporting for @mock'd test functions
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.5.2.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.5.2.rst
deleted file mode 100644
index 9308ffdd626..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.5.2.rst
+++ /dev/null
@@ -1,64 +0,0 @@
-pytest-2.5.2: fixes
-===========================================================================
-
-pytest is a mature Python testing tool with more than a 1000 tests
-against itself, passing on many different interpreters and platforms.
-
-The 2.5.2 release fixes a few bugs with two maybe-bugs remaining and
-actively being worked on (and waiting for the bug reporter's input).
-We also have a new contribution guide thanks to Piotr Banaszkiewicz
-and others.
-
-See docs at:
-
- http://pytest.org
-
-As usual, you can upgrade from pypi via::
-
- pip install -U pytest
-
-Thanks to the following people who contributed to this release:
-
- Anatoly Bubenkov
- Ronny Pfannschmidt
- Floris Bruynooghe
- Bruno Oliveira
- Andreas Pelme
- Jurko Gospodnetić
- Piotr Banaszkiewicz
- Simon Liedtke
- lakka
- Lukasz Balcerzak
- Philippe Muller
- Daniel Hahler
-
-have fun,
-holger krekel
-
-2.5.2
------------------------------------
-
-- fix issue409 -- better interoperate with cx_freeze by not
- trying to import from collections.abc which causes problems
- for py27/cx_freeze. Thanks Wolfgang L. for reporting and tracking it down.
-
-- fixed docs and code to use "pytest" instead of "py.test" almost everywhere.
- Thanks Jurko Gospodnetic for the complete PR.
-
-- fix issue425: mention at end of "py.test -h" that --markers
- and --fixtures work according to specified test path (or current dir)
-
-- fix issue413: exceptions with unicode attributes are now printed
- correctly also on python2 and with pytest-xdist runs. (the fix
- requires py-1.4.20)
-
-- copy, cleanup and integrate py.io capture
- from pylib 1.4.20.dev2 (rev 13d9af95547e)
-
-- address issue416: clarify docs as to conftest.py loading semantics
-
-- fix issue429: comparing byte strings with non-ascii chars in assert
- expressions now work better. Thanks Floris Bruynooghe.
-
-- make capfd/capsys.capture private, its unused and shouldnt be exposed
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.3.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.3.rst
deleted file mode 100644
index 13fae31b8a1..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.3.rst
+++ /dev/null
@@ -1,52 +0,0 @@
-pytest-2.6.3: fixes and little improvements
-===========================================================================
-
-pytest is a mature Python testing tool with more than a 1100 tests
-against itself, passing on many different interpreters and platforms.
-This release is drop-in compatible to 2.5.2 and 2.6.X.
-See below for the changes and see docs at:
-
- http://pytest.org
-
-As usual, you can upgrade from pypi via::
-
- pip install -U pytest
-
-Thanks to all who contributed, among them:
-
- Floris Bruynooghe
- Oleg Sinyavskiy
- Uwe Schmitt
- Charles Cloud
- Wolfgang Schnerring
-
-have fun,
-holger krekel
-
-Changes 2.6.3
-======================
-
-- fix issue575: xunit-xml was reporting collection errors as failures
- instead of errors, thanks Oleg Sinyavskiy.
-
-- fix issue582: fix setuptools example, thanks Laszlo Papp and Ronny
- Pfannschmidt.
-
-- Fix infinite recursion bug when pickling capture.EncodedFile, thanks
- Uwe Schmitt.
-
-- fix issue589: fix bad interaction with numpy and others when showing
- exceptions. Check for precise "maximum recursion depth exceed" exception
- instead of presuming any RuntimeError is that one (implemented in py
- dep). Thanks Charles Cloud for analysing the issue.
-
-- fix conftest related fixture visibility issue: when running with a
- CWD outside a test package pytest would get fixture discovery wrong.
- Thanks to Wolfgang Schnerring for figuring out a reproducable example.
-
-- Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the
- timeout when interactively entering pdb). Thanks Wolfgang Schnerring.
-
-- check xfail/skip also with non-python function test items. Thanks
- Floris Bruynooghe.
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.7.0.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.7.0.rst
deleted file mode 100644
index 07ae44ca1ac..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.7.0.rst
+++ /dev/null
@@ -1,101 +0,0 @@
-pytest-2.7.0: fixes, features, speed improvements
-===========================================================================
-
-pytest is a mature Python testing tool with more than a 1100 tests
-against itself, passing on many different interpreters and platforms.
-This release is supposed to be drop-in compatible to 2.6.X.
-
-See below for the changes and see docs at:
-
- http://pytest.org
-
-As usual, you can upgrade from pypi via::
-
- pip install -U pytest
-
-Thanks to all who contributed, among them:
-
- Anatoly Bubenkoff
- Floris Bruynooghe
- Brianna Laugher
- Eric Siegerman
- Daniel Hahler
- Charles Cloud
- Tom Viner
- Holger Peters
- Ldiary Translations
- almarklein
-
-have fun,
-holger krekel
-
-2.7.0 (compared to 2.6.4)
------------------------------
-
-- fix issue435: make reload() work when assert rewriting is active.
- Thanks Daniel Hahler.
-
-- fix issue616: conftest.py files and their contained fixutres are now
- properly considered for visibility, independently from the exact
- current working directory and test arguments that are used.
- Many thanks to Eric Siegerman and his PR235 which contains
- systematic tests for conftest visibility and now passes.
- This change also introduces the concept of a ``rootdir`` which
- is printed as a new pytest header and documented in the pytest
- customize web page.
-
-- change reporting of "diverted" tests, i.e. tests that are collected
- in one file but actually come from another (e.g. when tests in a test class
- come from a base class in a different file). We now show the nodeid
- and indicate via a postfix the other file.
-
-- add ability to set command line options by environment variable PYTEST_ADDOPTS.
-
-- added documentation on the new pytest-dev teams on bitbucket and
- github. See https://pytest.org/latest/contributing.html .
- Thanks to Anatoly for pushing and initial work on this.
-
-- fix issue650: new option ``--docttest-ignore-import-errors`` which
- will turn import errors in doctests into skips. Thanks Charles Cloud
- for the complete PR.
-
-- fix issue655: work around different ways that cause python2/3
- to leak sys.exc_info into fixtures/tests causing failures in 3rd party code
-
-- fix issue615: assertion re-writing did not correctly escape % signs
- when formatting boolean operations, which tripped over mixing
- booleans with modulo operators. Thanks to Tom Viner for the report,
- triaging and fix.
-
-- implement issue351: add ability to specify parametrize ids as a callable
- to generate custom test ids. Thanks Brianna Laugher for the idea and
- implementation.
-
-- introduce and document new hookwrapper mechanism useful for plugins
- which want to wrap the execution of certain hooks for their purposes.
- This supersedes the undocumented ``__multicall__`` protocol which
- pytest itself and some external plugins use. Note that pytest-2.8
- is scheduled to drop supporting the old ``__multicall__``
- and only support the hookwrapper protocol.
-
-- majorly speed up invocation of plugin hooks
-
-- use hookwrapper mechanism in builtin pytest plugins.
-
-- add a doctest ini option for doctest flags, thanks Holger Peters.
-
-- add note to docs that if you want to mark a parameter and the
- parameter is a callable, you also need to pass in a reason to disambiguate
- it from the "decorator" case. Thanks Tom Viner.
-
-- "python_classes" and "python_functions" options now support glob-patterns
- for test discovery, as discussed in issue600. Thanks Ldiary Translations.
-
-- allow to override parametrized fixtures with non-parametrized ones and vice versa (bubenkoff).
-
-- fix issue463: raise specific error for 'parameterize' misspelling (pfctdayelise).
-
-- On failure, the ``sys.last_value``, ``sys.last_type`` and
- ``sys.last_traceback`` are set, so that a user can inspect the error
- via postmortem debugging (almarklein).
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.7.1.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.7.1.rst
deleted file mode 100644
index cd37cad0cb8..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.7.1.rst
+++ /dev/null
@@ -1,58 +0,0 @@
-pytest-2.7.1: bug fixes
-=======================
-
-pytest is a mature Python testing tool with more than a 1100 tests
-against itself, passing on many different interpreters and platforms.
-This release is supposed to be drop-in compatible to 2.7.0.
-
-See below for the changes and see docs at:
-
- http://pytest.org
-
-As usual, you can upgrade from pypi via::
-
- pip install -U pytest
-
-Thanks to all who contributed to this release, among them:
-
- Bruno Oliveira
- Holger Krekel
- Ionel Maries Cristian
- Floris Bruynooghe
-
-Happy testing,
-The py.test Development Team
-
-
-2.7.1 (compared to 2.7.0)
--------------------------
-
-- fix issue731: do not get confused by the braces which may be present
- and unbalanced in an object's repr while collapsing False
- explanations. Thanks Carl Meyer for the report and test case.
-
-- fix issue553: properly handling inspect.getsourcelines failures in
- FixtureLookupError which would lead to to an internal error,
- obfuscating the original problem. Thanks talljosh for initial
- diagnose/patch and Bruno Oliveira for final patch.
-
-- fix issue660: properly report scope-mismatch-access errors
- independently from ordering of fixture arguments. Also
- avoid the pytest internal traceback which does not provide
- information to the user. Thanks Holger Krekel.
-
-- streamlined and documented release process. Also all versions
- (in setup.py and documentation generation) are now read
- from _pytest/__init__.py. Thanks Holger Krekel.
-
-- fixed docs to remove the notion that yield-fixtures are experimental.
- They are here to stay :) Thanks Bruno Oliveira.
-
-- Support building wheels by using environment markers for the
- requirements. Thanks Ionel Maries Cristian.
-
-- fixed regression to 2.6.4 which surfaced e.g. in lost stdout capture printing
- when tests raised SystemExit. Thanks Holger Krekel.
-
-- reintroduced _pytest fixture of the pytester plugin which is used
- at least by pytest-xdist.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.9.0.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.9.0.rst
deleted file mode 100644
index 99c1c631f17..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.9.0.rst
+++ /dev/null
@@ -1,159 +0,0 @@
-pytest-2.9.0
-============
-
-pytest is a mature Python testing tool with more than a 1100 tests
-against itself, passing on many different interpreters and platforms.
-
-See below for the changes and see docs at:
-
- http://pytest.org
-
-As usual, you can upgrade from pypi via::
-
- pip install -U pytest
-
-Thanks to all who contributed to this release, among them:
-
- Anatoly Bubenkov
- Bruno Oliveira
- Buck Golemon
- David Vierra
- Florian Bruhin
- Galaczi Endre
- Georgy Dyuldin
- Lukas Bednar
- Luke Murphy
- Marcin Biernat
- Matt Williams
- Michael Aquilina
- Raphael Pierzina
- Ronny Pfannschmidt
- Ryan Wooden
- Tiemo Kieft
- TomV
- holger krekel
- jab
-
-
-Happy testing,
-The py.test Development Team
-
-
-2.9.0 (compared to 2.8.7)
--------------------------
-
-**New Features**
-
-* New ``pytest.mark.skip`` mark, which unconditionally skips marked tests.
- Thanks `@MichaelAquilina`_ for the complete PR (`#1040`_).
-
-* ``--doctest-glob`` may now be passed multiple times in the command-line.
- Thanks `@jab`_ and `@nicoddemus`_ for the PR.
-
-* New ``-rp`` and ``-rP`` reporting options give the summary and full output
- of passing tests, respectively. Thanks to `@codewarrior0`_ for the PR.
-
-* ``pytest.mark.xfail`` now has a ``strict`` option which makes ``XPASS``
- tests to fail the test suite, defaulting to ``False``. There's also a
- ``xfail_strict`` ini option that can be used to configure it project-wise.
- Thanks `@rabbbit`_ for the request and `@nicoddemus`_ for the PR (`#1355`_).
-
-* ``Parser.addini`` now supports options of type ``bool``. Thanks
- `@nicoddemus`_ for the PR.
-
-* New ``ALLOW_BYTES`` doctest option strips ``b`` prefixes from byte strings
- in doctest output (similar to ``ALLOW_UNICODE``).
- Thanks `@jaraco`_ for the request and `@nicoddemus`_ for the PR (`#1287`_).
-
-* give a hint on KeyboardInterrupt to use the --fulltrace option to show the errors,
- this fixes `#1366`_.
- Thanks to `@hpk42`_ for the report and `@RonnyPfannschmidt`_ for the PR.
-
-* catch IndexError exceptions when getting exception source location. This fixes
- pytest internal error for dynamically generated code (fixtures and tests)
- where source lines are fake by intention
-
-**Changes**
-
-* **Important**: `py.code <http://pylib.readthedocs.org/en/latest/code.html>`_ has been
- merged into the ``pytest`` repository as ``pytest._code``. This decision
- was made because ``py.code`` had very few uses outside ``pytest`` and the
- fact that it was in a different repository made it difficult to fix bugs on
- its code in a timely manner. The team hopes with this to be able to better
- refactor out and improve that code.
- This change shouldn't affect users, but it is useful to let users aware
- if they encounter any strange behavior.
-
- Keep in mind that the code for ``pytest._code`` is **private** and
- **experimental**, so you definitely should not import it explicitly!
-
- Please note that the original ``py.code`` is still available in
- `pylib <http://pylib.readthedocs.org>`_.
-
-* ``pytest_enter_pdb`` now optionally receives the pytest config object.
- Thanks `@nicoddemus`_ for the PR.
-
-* Removed code and documentation for Python 2.5 or lower versions,
- including removal of the obsolete ``_pytest.assertion.oldinterpret`` module.
- Thanks `@nicoddemus`_ for the PR (`#1226`_).
-
-* Comparisons now always show up in full when ``CI`` or ``BUILD_NUMBER`` is
- found in the environment, even when -vv isn't used.
- Thanks `@The-Compiler`_ for the PR.
-
-* ``--lf`` and ``--ff`` now support long names: ``--last-failed`` and
- ``--failed-first`` respectively.
- Thanks `@MichaelAquilina`_ for the PR.
-
-* Added expected exceptions to pytest.raises fail message
-
-* Collection only displays progress ("collecting X items") when in a terminal.
- This avoids cluttering the output when using ``--color=yes`` to obtain
- colors in CI integrations systems (`#1397`_).
-
-**Bug Fixes**
-
-* The ``-s`` and ``-c`` options should now work under ``xdist``;
- ``Config.fromdictargs`` now represents its input much more faithfully.
- Thanks to `@bukzor`_ for the complete PR (`#680`_).
-
-* Fix (`#1290`_): support Python 3.5's ``@`` operator in assertion rewriting.
- Thanks `@Shinkenjoe`_ for report with test case and `@tomviner`_ for the PR.
-
-* Fix formatting utf-8 explanation messages (`#1379`_).
- Thanks `@biern`_ for the PR.
-
-* Fix `traceback style docs`_ to describe all of the available options
- (auto/long/short/line/native/no), with `auto` being the default since v2.6.
- Thanks `@hackebrot`_ for the PR.
-
-* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records
- with same name.
-
-
-.. _`traceback style docs`: https://pytest.org/latest/usage.html#modifying-python-traceback-printing
-
-.. _#1422: https://github.com/pytest-dev/pytest/issues/1422
-.. _#1379: https://github.com/pytest-dev/pytest/issues/1379
-.. _#1366: https://github.com/pytest-dev/pytest/issues/1366
-.. _#1040: https://github.com/pytest-dev/pytest/pull/1040
-.. _#680: https://github.com/pytest-dev/pytest/issues/680
-.. _#1287: https://github.com/pytest-dev/pytest/pull/1287
-.. _#1226: https://github.com/pytest-dev/pytest/pull/1226
-.. _#1290: https://github.com/pytest-dev/pytest/pull/1290
-.. _#1355: https://github.com/pytest-dev/pytest/pull/1355
-.. _#1397: https://github.com/pytest-dev/pytest/issues/1397
-.. _@biern: https://github.com/biern
-.. _@MichaelAquilina: https://github.com/MichaelAquilina
-.. _@bukzor: https://github.com/bukzor
-.. _@hpk42: https://github.com/hpk42
-.. _@nicoddemus: https://github.com/nicoddemus
-.. _@jab: https://github.com/jab
-.. _@codewarrior0: https://github.com/codewarrior0
-.. _@jaraco: https://github.com/jaraco
-.. _@The-Compiler: https://github.com/The-Compiler
-.. _@Shinkenjoe: https://github.com/Shinkenjoe
-.. _@tomviner: https://github.com/tomviner
-.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
-.. _@rabbbit: https://github.com/rabbbit
-.. _@hackebrot: https://github.com/hackebrot \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.9.1.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.9.1.rst
deleted file mode 100644
index 05a44843042..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.9.1.rst
+++ /dev/null
@@ -1,65 +0,0 @@
-pytest-2.9.1
-============
-
-pytest is a mature Python testing tool with more than a 1100 tests
-against itself, passing on many different interpreters and platforms.
-
-See below for the changes and see docs at:
-
- http://pytest.org
-
-As usual, you can upgrade from pypi via::
-
- pip install -U pytest
-
-Thanks to all who contributed to this release, among them:
-
- Bruno Oliveira
- Daniel Hahler
- Dmitry Malinovsky
- Florian Bruhin
- Floris Bruynooghe
- Matt Bachmann
- Ronny Pfannschmidt
- TomV
- Vladimir Bolshakov
- Zearin
- palaviv
-
-
-Happy testing,
-The py.test Development Team
-
-
-2.9.1 (compared to 2.9.0)
--------------------------
-
-**Bug Fixes**
-
-* Improve error message when a plugin fails to load.
- Thanks `@nicoddemus`_ for the PR.
-
-* Fix (`#1178 <https://github.com/pytest-dev/pytest/issues/1178>`_):
- ``pytest.fail`` with non-ascii characters raises an internal pytest error.
- Thanks `@nicoddemus`_ for the PR.
-
-* Fix (`#469`_): junit parses report.nodeid incorrectly, when params IDs
- contain ``::``. Thanks `@tomviner`_ for the PR (`#1431`_).
-
-* Fix (`#578 <https://github.com/pytest-dev/pytest/issues/578>`_): SyntaxErrors
- containing non-ascii lines at the point of failure generated an internal
- py.test error.
- Thanks `@asottile`_ for the report and `@nicoddemus`_ for the PR.
-
-* Fix (`#1437`_): When passing in a bytestring regex pattern to parameterize
- attempt to decode it as utf-8 ignoring errors.
-
-* Fix (`#649`_): parametrized test nodes cannot be specified to run on the command line.
-
-
-.. _#1437: https://github.com/pytest-dev/pytest/issues/1437
-.. _#469: https://github.com/pytest-dev/pytest/issues/469
-.. _#1431: https://github.com/pytest-dev/pytest/pull/1431
-.. _#649: https://github.com/pytest-dev/pytest/issues/649
-
-.. _@asottile: https://github.com/asottile
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/sprint2016.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/sprint2016.rst
deleted file mode 100644
index e59ccdda772..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/sprint2016.rst
+++ /dev/null
@@ -1,105 +0,0 @@
-python testing sprint June 20th-26th 2016
-======================================================
-
-.. image:: ../img/freiburg2.jpg
- :width: 400
-
-The pytest core group is heading towards the biggest sprint
-in its history, to take place in the black forest town Freiburg
-in Germany. As of February 2016 we have started a `funding
-campaign on Indiegogo to cover expenses
-<http://igg.me/at/pytest-sprint/x/4034848>`_ The page also mentions
-some preliminary topics:
-
-- improving pytest-xdist test scheduling to take into account
- fixture setups and explicit user hints.
-
-- provide info on fixture dependencies during --collect-only
-
-- tying pytest-xdist to tox so that you can do "py.test -e py34"
- to run tests in a particular tox-managed virtualenv. Also
- look into making pytest-xdist use tox environments on
- remote ssh-sides so that remote dependency management becomes
- easier.
-
-- refactoring the fixture system so more people understand it :)
-
-- integrating PyUnit setup methods as autouse fixtures.
- possibly adding ways to influence ordering of same-scoped
- fixtures (so you can make a choice of which fixtures come
- before others)
-
-- fixing bugs and issues from the tracker, really an endless source :)
-
-
-Participants
---------------
-
-Here are preliminary participants who said they are likely to come,
-given some expenses funding::
-
- Anatoly Bubenkoff, Netherlands
- Andreas Pelme, Personalkollen, Sweden
- Anthony Wang, Splunk, US
- Brianna Laugher, Australia
- Bruno Oliveira, Brazil
- Danielle Jenkins, Splunk, US
- Dave Hunt, UK
- Florian Bruhin, Switzerland
- Floris Bruynooghe, Cobe.io, UK
- Holger Krekel, merlinux, Germany
- Oliver Bestwalter, Avira, Germany
- Omar Kohl, Germany
- Raphael Pierzina, FanDuel, UK
- Tom Viner, UK
-
- <your name here?>
-
-Other contributors and experienced newcomers are invited to join as well
-but please send a mail to the pytest-dev mailing list if you intend to
-do so somewhat soon, also how much funding you need if so. And if you
-are working for a company and using pytest heavily you are welcome to
-join and we encourage your company to provide some funding for the
-sprint. They may see it, and rightfully so, as a very cheap and deep
-training which brings you together with the experts in the field :)
-
-
-Sprint organisation, schedule
--------------------------------
-
-tentative schedule:
-
-- 19/20th arrival in Freiburg
-- 20th social get together, initial hacking
-- 21/22th full sprint days
-- 23rd break day, hiking
-- 24/25th full sprint days
-- 26th departure
-
-We might adjust according to weather to make sure that if
-we do some hiking or excursion we'll have good weather.
-Freiburg is one of the sunniest places in Germany so
-it shouldn't be too much of a constraint.
-
-
-Accomodation
-----------------
-
-We'll see to arrange for renting a flat with multiple
-beds/rooms. Hotels are usually below 100 per night.
-The earlier we book the better.
-
-Money / funding
----------------
-
-The Indiegogo campaign asks for 11000 USD which should cover
-the costs for flights and accomodation, renting a sprint place
-and maybe a bit of food as well.
-
-If your organisation wants to support the sprint but prefers
-to give money according to an invoice, get in contact with
-holger at http://merlinux.eu who can invoice your organisation
-properly.
-
-If we have excess money we'll use for further sprint/travel
-funding for pytest/tox contributors.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/assert.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/assert.rst
deleted file mode 100644
index e7f14e8bd1a..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/assert.rst
+++ /dev/null
@@ -1,289 +0,0 @@
-
-The writing and reporting of assertions in tests
-==================================================
-
-.. _`assertfeedback`:
-.. _`assert with the assert statement`:
-.. _`assert`:
-
-
-Asserting with the ``assert`` statement
----------------------------------------------------------
-
-``pytest`` allows you to use the standard python ``assert`` for verifying
-expectations and values in Python tests. For example, you can write the
-following::
-
- # content of test_assert1.py
- def f():
- return 3
-
- def test_function():
- assert f() == 4
-
-to assert that your function returns a certain value. If this assertion fails
-you will see the return value of the function call::
-
- $ py.test test_assert1.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 1 items
-
- test_assert1.py F
-
- ======= FAILURES ========
- _______ test_function ________
-
- def test_function():
- > assert f() == 4
- E assert 3 == 4
- E + where 3 = f()
-
- test_assert1.py:5: AssertionError
- ======= 1 failed in 0.12 seconds ========
-
-``pytest`` has support for showing the values of the most common subexpressions
-including calls, attributes, comparisons, and binary and unary
-operators. (See :ref:`tbreportdemo`). This allows you to use the
-idiomatic python constructs without boilerplate code while not losing
-introspection information.
-
-However, if you specify a message with the assertion like this::
-
- assert a % 2 == 0, "value was odd, should be even"
-
-then no assertion introspection takes places at all and the message
-will be simply shown in the traceback.
-
-See :ref:`assert-details` for more information on assertion introspection.
-
-.. _`assertraises`:
-
-Assertions about expected exceptions
-------------------------------------------
-
-In order to write assertions about raised exceptions, you can use
-``pytest.raises`` as a context manager like this::
-
- import pytest
-
- def test_zero_division():
- with pytest.raises(ZeroDivisionError):
- 1 / 0
-
-and if you need to have access to the actual exception info you may use::
-
- def test_recursion_depth():
- with pytest.raises(RuntimeError) as excinfo:
- def f():
- f()
- f()
- assert 'maximum recursion' in str(excinfo.value)
-
-``excinfo`` is a ``ExceptionInfo`` instance, which is a wrapper around
-the actual exception raised. The main attributes of interest are
-``.type``, ``.value`` and ``.traceback``.
-
-If you want to write test code that works on Python 2.4 as well,
-you may also use two other ways to test for an expected exception::
-
- pytest.raises(ExpectedException, func, *args, **kwargs)
- pytest.raises(ExpectedException, "func(*args, **kwargs)")
-
-both of which execute the specified function with args and kwargs and
-asserts that the given ``ExpectedException`` is raised. The reporter will
-provide you with helpful output in case of failures such as *no
-exception* or *wrong exception*.
-
-Note that it is also possible to specify a "raises" argument to
-``pytest.mark.xfail``, which checks that the test is failing in a more
-specific way than just having any exception raised::
-
- @pytest.mark.xfail(raises=IndexError)
- def test_f():
- f()
-
-Using ``pytest.raises`` is likely to be better for cases where you are testing
-exceptions your own code is deliberately raising, whereas using
-``@pytest.mark.xfail`` with a check function is probably better for something
-like documenting unfixed bugs (where the test describes what "should" happen)
-or bugs in dependencies.
-
-
-.. _`assertwarns`:
-
-Assertions about expected warnings
------------------------------------------
-
-.. versionadded:: 2.8
-
-You can check that code raises a particular warning using
-:ref:`pytest.warns <warns>`.
-
-
-.. _newreport:
-
-Making use of context-sensitive comparisons
--------------------------------------------------
-
-.. versionadded:: 2.0
-
-``pytest`` has rich support for providing context-sensitive information
-when it encounters comparisons. For example::
-
- # content of test_assert2.py
-
- def test_set_comparison():
- set1 = set("1308")
- set2 = set("8035")
- assert set1 == set2
-
-if you run this module::
-
- $ py.test test_assert2.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 1 items
-
- test_assert2.py F
-
- ======= FAILURES ========
- _______ test_set_comparison ________
-
- def test_set_comparison():
- set1 = set("1308")
- set2 = set("8035")
- > assert set1 == set2
- E assert set(['0', '1', '3', '8']) == set(['0', '3', '5', '8'])
- E Extra items in the left set:
- E '1'
- E Extra items in the right set:
- E '5'
- E Use -v to get the full diff
-
- test_assert2.py:5: AssertionError
- ======= 1 failed in 0.12 seconds ========
-
-Special comparisons are done for a number of cases:
-
-* comparing long strings: a context diff is shown
-* comparing long sequences: first failing indices
-* comparing dicts: different entries
-
-See the :ref:`reporting demo <tbreportdemo>` for many more examples.
-
-Defining your own assertion comparison
-----------------------------------------------
-
-It is possible to add your own detailed explanations by implementing
-the ``pytest_assertrepr_compare`` hook.
-
-.. autofunction:: _pytest.hookspec.pytest_assertrepr_compare
-
-As an example consider adding the following hook in a conftest.py which
-provides an alternative explanation for ``Foo`` objects::
-
- # content of conftest.py
- from test_foocompare import Foo
- def pytest_assertrepr_compare(op, left, right):
- if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
- return ['Comparing Foo instances:',
- ' vals: %s != %s' % (left.val, right.val)]
-
-now, given this test module::
-
- # content of test_foocompare.py
- class Foo:
- def __init__(self, val):
- self.val = val
-
- def __eq__(self, other):
- return self.val == other.val
-
- def test_compare():
- f1 = Foo(1)
- f2 = Foo(2)
- assert f1 == f2
-
-you can run the test module and get the custom output defined in
-the conftest file::
-
- $ py.test -q test_foocompare.py
- F
- ======= FAILURES ========
- _______ test_compare ________
-
- def test_compare():
- f1 = Foo(1)
- f2 = Foo(2)
- > assert f1 == f2
- E assert Comparing Foo instances:
- E vals: 1 != 2
-
- test_foocompare.py:11: AssertionError
- 1 failed in 0.12 seconds
-
-.. _assert-details:
-.. _`assert introspection`:
-
-Advanced assertion introspection
-----------------------------------
-
-.. versionadded:: 2.1
-
-
-Reporting details about a failing assertion is achieved either by rewriting
-assert statements before they are run or re-evaluating the assert expression and
-recording the intermediate values. Which technique is used depends on the
-location of the assert, ``pytest`` configuration, and Python version being used
-to run ``pytest``.
-
-By default, ``pytest`` rewrites assert statements in test modules.
-Rewritten assert statements put introspection information into the assertion failure message.
-``pytest`` only rewrites test modules directly discovered by its test collection process, so
-asserts in supporting modules which are not themselves test modules will not be
-rewritten.
-
-.. note::
-
- ``pytest`` rewrites test modules on import. It does this by using an import
- hook to write a new pyc files. Most of the time this works transparently.
- However, if you are messing with import yourself, the import hook may
- interfere. If this is the case, simply use ``--assert=reinterp`` or
- ``--assert=plain``. Additionally, rewriting will fail silently if it cannot
- write new pycs, i.e. in a read-only filesystem or a zipfile.
-
-If an assert statement has not been rewritten or the Python version is less than
-2.6, ``pytest`` falls back on assert reinterpretation. In assert
-reinterpretation, ``pytest`` walks the frame of the function containing the
-assert statement to discover sub-expression results of the failing assert
-statement. You can force ``pytest`` to always use assertion reinterpretation by
-passing the ``--assert=reinterp`` option.
-
-Assert reinterpretation has a caveat not present with assert rewriting: If
-evaluating the assert expression has side effects you may get a warning that the
-intermediate values could not be determined safely. A common example of this
-issue is an assertion which reads from a file::
-
- assert f.read() != '...'
-
-If this assertion fails then the re-evaluation will probably succeed!
-This is because ``f.read()`` will return an empty string when it is
-called the second time during the re-evaluation. However, it is
-easy to rewrite the assertion and avoid any trouble::
-
- content = f.read()
- assert content != '...'
-
-All assert introspection can be turned off by passing ``--assert=plain``.
-
-For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_.
-
-.. versionadded:: 2.1
- Add assert rewriting as an alternate introspection technique.
-
-.. versionchanged:: 2.1
- Introduce the ``--assert`` option. Deprecate ``--no-assert`` and
- ``--nomagic``.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/bash-completion.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/bash-completion.rst
deleted file mode 100644
index b2a52fa637b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/bash-completion.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-
-.. _bash_completion:
-
-Setting up bash completion
-==========================
-
-When using bash as your shell, ``pytest`` can use argcomplete
-(https://argcomplete.readthedocs.org/) for auto-completion.
-For this ``argcomplete`` needs to be installed **and** enabled.
-
-Install argcomplete using::
-
- sudo pip install 'argcomplete>=0.5.7'
-
-For global activation of all argcomplete enabled python applications run::
-
- sudo activate-global-python-argcomplete
-
-For permanent (but not global) ``pytest`` activation, use::
-
- register-python-argcomplete py.test >> ~/.bashrc
-
-For one-time activation of argcomplete for ``pytest`` only, use::
-
- eval "$(register-python-argcomplete py.test)"
-
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/builtin.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/builtin.rst
deleted file mode 100644
index b18c3f8280a..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/builtin.rst
+++ /dev/null
@@ -1,134 +0,0 @@
-
-.. _`pytest helpers`:
-
-Pytest API and builtin fixtures
-================================================
-
-This is a list of ``pytest.*`` API functions and fixtures.
-
-For information on plugin hooks and objects, see :ref:`plugins`.
-
-For information on the ``pytest.mark`` mechanism, see :ref:`mark`.
-
-For the below objects, you can also interactively ask for help, e.g. by
-typing on the Python interactive prompt something like::
-
- import pytest
- help(pytest)
-
-.. currentmodule:: pytest
-
-Invoking pytest interactively
----------------------------------------------------
-
-.. autofunction:: main
-
-More examples at :ref:`pytest.main-usage`
-
-
-Helpers for assertions about Exceptions/Warnings
---------------------------------------------------------
-
-.. autofunction:: raises
-
-Examples at :ref:`assertraises`.
-
-.. autofunction:: deprecated_call
-
-Raising a specific test outcome
---------------------------------------
-
-You can use the following functions in your test, fixture or setup
-functions to force a certain test outcome. Note that most often
-you can rather use declarative marks, see :ref:`skipping`.
-
-.. autofunction:: _pytest.runner.fail
-.. autofunction:: _pytest.runner.skip
-.. autofunction:: _pytest.runner.importorskip
-.. autofunction:: _pytest.skipping.xfail
-.. autofunction:: _pytest.runner.exit
-
-fixtures and requests
------------------------------------------------------
-
-To mark a fixture function:
-
-.. autofunction:: _pytest.python.fixture
-
-Tutorial at :ref:`fixtures`.
-
-The ``request`` object that can be used from fixture functions.
-
-.. autoclass:: _pytest.python.FixtureRequest()
- :members:
-
-
-.. _builtinfixtures:
-.. _builtinfuncargs:
-
-Builtin fixtures/function arguments
------------------------------------------
-
-You can ask for available builtin or project-custom
-:ref:`fixtures <fixtures>` by typing::
-
- $ py.test -q --fixtures
- cache
- Return a cache object that can persist state between testing sessions.
-
- cache.get(key, default)
- cache.set(key, value)
-
- Keys must be a ``/`` separated value, where the first part is usually the
- name of your plugin or application to avoid clashes with other cache users.
-
- Values can be any object handled by the json stdlib module.
- capsys
- enables capturing of writes to sys.stdout/sys.stderr and makes
- captured output available via ``capsys.readouterr()`` method calls
- which return a ``(out, err)`` tuple.
- capfd
- enables capturing of writes to file descriptors 1 and 2 and makes
- captured output available via ``capfd.readouterr()`` method calls
- which return a ``(out, err)`` tuple.
- record_xml_property
- Fixture that adds extra xml properties to the tag for the calling test.
- The fixture is callable with (name, value), with value being automatically
- xml-encoded.
- monkeypatch
- The returned ``monkeypatch`` funcarg provides these
- helper methods to modify objects, dictionaries or os.environ::
-
- monkeypatch.setattr(obj, name, value, raising=True)
- monkeypatch.delattr(obj, name, raising=True)
- monkeypatch.setitem(mapping, name, value)
- monkeypatch.delitem(obj, name, raising=True)
- monkeypatch.setenv(name, value, prepend=False)
- monkeypatch.delenv(name, value, raising=True)
- monkeypatch.syspath_prepend(path)
- monkeypatch.chdir(path)
-
- All modifications will be undone after the requesting
- test function has finished. The ``raising``
- parameter determines if a KeyError or AttributeError
- will be raised if the set/deletion operation has no target.
- pytestconfig
- the pytest config object with access to command line opts.
- recwarn
- Return a WarningsRecorder instance that provides these methods:
-
- * ``pop(category=None)``: return last warning matching the category.
- * ``clear()``: clear list of warnings
-
- See http://docs.python.org/library/warnings.html for information
- on warning categories.
- tmpdir_factory
- Return a TempdirFactory instance for the test session.
- tmpdir
- return a temporary directory path object
- which is unique to each test function invocation,
- created as a sub directory of the base temporary
- directory. The returned object is a `py.path.local`_
- path object.
-
- no tests ran in 0.12 seconds
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/cache.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/cache.rst
deleted file mode 100644
index 52abb52a0df..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/cache.rst
+++ /dev/null
@@ -1,278 +0,0 @@
-Cache: working with cross-testrun state
-=======================================
-
-.. versionadded:: 2.8
-
-.. warning::
-
- The functionality of this core plugin was previously distributed
- as a third party plugin named ``pytest-cache``. The core plugin
- is compatible regarding command line options and API usage except that you
- can only store/receive data between test runs that is json-serializable.
-
-
-Usage
----------
-
-The plugin provides two command line options to rerun failures from the
-last ``py.test`` invocation:
-
-* ``--lf``, ``--last-failed`` - to only re-run the failures.
-* ``--ff``, ``--failed-first`` - to run the failures first and then the rest of
- the tests.
-
-For cleanup (usually not needed), a ``--cache-clear`` option allows to remove
-all cross-session cache contents ahead of a test run.
-
-Other plugins may access the `config.cache`_ object to set/get
-**json encodable** values between ``py.test`` invocations.
-
-.. note::
-
- This plugin is enabled by default, but can be disabled if needed: see
- :ref:`cmdunregister` (the internal name for this plugin is
- ``cacheprovider``).
-
-
-Rerunning only failures or failures first
------------------------------------------------
-
-First, let's create 50 test invocation of which only 2 fail::
-
- # content of test_50.py
- import pytest
-
- @pytest.mark.parametrize("i", range(50))
- def test_num(i):
- if i in (17, 25):
- pytest.fail("bad luck")
-
-If you run this for the first time you will see two failures::
-
- $ py.test -q
- .................F.......F........................
- ======= FAILURES ========
- _______ test_num[17] ________
-
- i = 17
-
- @pytest.mark.parametrize("i", range(50))
- def test_num(i):
- if i in (17, 25):
- > pytest.fail("bad luck")
- E Failed: bad luck
-
- test_50.py:6: Failed
- _______ test_num[25] ________
-
- i = 25
-
- @pytest.mark.parametrize("i", range(50))
- def test_num(i):
- if i in (17, 25):
- > pytest.fail("bad luck")
- E Failed: bad luck
-
- test_50.py:6: Failed
- 2 failed, 48 passed in 0.12 seconds
-
-If you then run it with ``--lf``::
-
- $ py.test --lf
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- run-last-failure: rerun last 2 failures
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 50 items
-
- test_50.py FF
-
- ======= FAILURES ========
- _______ test_num[17] ________
-
- i = 17
-
- @pytest.mark.parametrize("i", range(50))
- def test_num(i):
- if i in (17, 25):
- > pytest.fail("bad luck")
- E Failed: bad luck
-
- test_50.py:6: Failed
- _______ test_num[25] ________
-
- i = 25
-
- @pytest.mark.parametrize("i", range(50))
- def test_num(i):
- if i in (17, 25):
- > pytest.fail("bad luck")
- E Failed: bad luck
-
- test_50.py:6: Failed
- ======= 2 failed, 48 deselected in 0.12 seconds ========
-
-You have run only the two failing test from the last run, while 48 tests have
-not been run ("deselected").
-
-Now, if you run with the ``--ff`` option, all tests will be run but the first
-previous failures will be executed first (as can be seen from the series
-of ``FF`` and dots)::
-
- $ py.test --ff
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- run-last-failure: rerun last 2 failures first
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 50 items
-
- test_50.py FF................................................
-
- ======= FAILURES ========
- _______ test_num[17] ________
-
- i = 17
-
- @pytest.mark.parametrize("i", range(50))
- def test_num(i):
- if i in (17, 25):
- > pytest.fail("bad luck")
- E Failed: bad luck
-
- test_50.py:6: Failed
- _______ test_num[25] ________
-
- i = 25
-
- @pytest.mark.parametrize("i", range(50))
- def test_num(i):
- if i in (17, 25):
- > pytest.fail("bad luck")
- E Failed: bad luck
-
- test_50.py:6: Failed
- ======= 2 failed, 48 passed in 0.12 seconds ========
-
-.. _`config.cache`:
-
-The new config.cache object
---------------------------------
-
-.. regendoc:wipe
-
-Plugins or conftest.py support code can get a cached value using the
-pytest ``config`` object. Here is a basic example plugin which
-implements a :ref:`fixture` which re-uses previously created state
-across py.test invocations::
-
- # content of test_caching.py
- import pytest
- import time
-
- @pytest.fixture
- def mydata(request):
- val = request.config.cache.get("example/value", None)
- if val is None:
- time.sleep(9*0.6) # expensive computation :)
- val = 42
- request.config.cache.set("example/value", val)
- return val
-
- def test_function(mydata):
- assert mydata == 23
-
-If you run this command once, it will take a while because
-of the sleep::
-
- $ py.test -q
- F
- ======= FAILURES ========
- _______ test_function ________
-
- mydata = 42
-
- def test_function(mydata):
- > assert mydata == 23
- E assert 42 == 23
-
- test_caching.py:14: AssertionError
- 1 failed in 0.12 seconds
-
-If you run it a second time the value will be retrieved from
-the cache and this will be quick::
-
- $ py.test -q
- F
- ======= FAILURES ========
- _______ test_function ________
-
- mydata = 42
-
- def test_function(mydata):
- > assert mydata == 23
- E assert 42 == 23
-
- test_caching.py:14: AssertionError
- 1 failed in 0.12 seconds
-
-See the `cache-api`_ for more details.
-
-
-Inspecting Cache content
--------------------------------
-
-You can always peek at the content of the cache using the
-``--cache-clear`` command line option::
-
- $ py.test --cache-clear
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 1 items
-
- test_caching.py F
-
- ======= FAILURES ========
- _______ test_function ________
-
- mydata = 42
-
- def test_function(mydata):
- > assert mydata == 23
- E assert 42 == 23
-
- test_caching.py:14: AssertionError
- ======= 1 failed in 0.12 seconds ========
-
-Clearing Cache content
--------------------------------
-
-You can instruct pytest to clear all cache files and values
-by adding the ``--cache-clear`` option like this::
-
- py.test --cache-clear
-
-This is recommended for invocations from Continous Integration
-servers where isolation and correctness is more important
-than speed.
-
-
-.. _`cache-api`:
-
-config.cache API
-------------------
-
-The ``config.cache`` object allows other plugins,
-including ``conftest.py`` files,
-to safely and flexibly store and retrieve values across
-test runs because the ``config`` object is available
-in many places.
-
-Under the hood, the cache plugin uses the simple
-dumps/loads API of the json stdlib module
-
-.. currentmodule:: _pytest.cacheprovider
-
-.. automethod:: Cache.get
-.. automethod:: Cache.set
-.. automethod:: Cache.makedir
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/capture.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/capture.rst
deleted file mode 100644
index 8892f5be756..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/capture.rst
+++ /dev/null
@@ -1,118 +0,0 @@
-
-.. _`captures`:
-
-Capturing of the stdout/stderr output
-=========================================================
-
-Default stdout/stderr/stdin capturing behaviour
----------------------------------------------------------
-
-During test execution any output sent to ``stdout`` and ``stderr`` is
-captured. If a test or a setup method fails its according captured
-output will usually be shown along with the failure traceback.
-
-In addition, ``stdin`` is set to a "null" object which will
-fail on attempts to read from it because it is rarely desired
-to wait for interactive input when running automated tests.
-
-By default capturing is done by intercepting writes to low level
-file descriptors. This allows to capture output from simple
-print statements as well as output from a subprocess started by
-a test.
-
-Setting capturing methods or disabling capturing
--------------------------------------------------
-
-There are two ways in which ``pytest`` can perform capturing:
-
-* file descriptor (FD) level capturing (default): All writes going to the
- operating system file descriptors 1 and 2 will be captured.
-
-* ``sys`` level capturing: Only writes to Python files ``sys.stdout``
- and ``sys.stderr`` will be captured. No capturing of writes to
- filedescriptors is performed.
-
-.. _`disable capturing`:
-
-You can influence output capturing mechanisms from the command line::
-
- py.test -s # disable all capturing
- py.test --capture=sys # replace sys.stdout/stderr with in-mem files
- py.test --capture=fd # also point filedescriptors 1 and 2 to temp file
-
-.. _printdebugging:
-
-Using print statements for debugging
----------------------------------------------------
-
-One primary benefit of the default capturing of stdout/stderr output
-is that you can use print statements for debugging::
-
- # content of test_module.py
-
- def setup_function(function):
- print ("setting up %s" % function)
-
- def test_func1():
- assert True
-
- def test_func2():
- assert False
-
-and running this module will show you precisely the output
-of the failing function and hide the other one::
-
- $ py.test
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 2 items
-
- test_module.py .F
-
- ======= FAILURES ========
- _______ test_func2 ________
-
- def test_func2():
- > assert False
- E assert False
-
- test_module.py:9: AssertionError
- -------------------------- Captured stdout setup ---------------------------
- setting up <function test_func2 at 0xdeadbeef>
- ======= 1 failed, 1 passed in 0.12 seconds ========
-
-Accessing captured output from a test function
----------------------------------------------------
-
-The ``capsys`` and ``capfd`` fixtures allow to access stdout/stderr
-output created during test execution. Here is an example test function
-that performs some output related checks:
-
-.. code-block:: python
-
- def test_myoutput(capsys): # or use "capfd" for fd-level
- print ("hello")
- sys.stderr.write("world\n")
- out, err = capsys.readouterr()
- assert out == "hello\n"
- assert err == "world\n"
- print "next"
- out, err = capsys.readouterr()
- assert out == "next\n"
-
-The ``readouterr()`` call snapshots the output so far -
-and capturing will be continued. After the test
-function finishes the original streams will
-be restored. Using ``capsys`` this way frees your
-test from having to care about setting/resetting
-output streams and also interacts well with pytest's
-own per-test capturing.
-
-If you want to capture on filedescriptor level you can use
-the ``capfd`` function argument which offers the exact
-same interface but allows to also capture output from
-libraries or subprocesses that directly write to operating
-system level output streams (FD1 and FD2).
-
-.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/conf.py b/tests/wpt/web-platform-tests/tools/pytest/doc/en/conf.py
deleted file mode 100644
index aca0442c5d6..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/conf.py
+++ /dev/null
@@ -1,326 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# pytest documentation build configuration file, created by
-# sphinx-quickstart on Fri Oct 8 17:54:28 2010.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The full version, including alpha/beta/rc tags.
-# The short X.Y version.
-
-import os, sys
-sys.path.insert(0, os.path.dirname(__file__))
-import _getdoctarget
-
-version = _getdoctarget.get_minor_version_string()
-release = _getdoctarget.get_version_string()
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-autodoc_member_order = "bysource"
-todo_include_todos = 1
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.autosummary',
- 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'contents'
-
-# General information about the project.
-project = u'pytest'
-copyright = u'2015, holger krekel and pytest-dev team'
-
-
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['links.inc', '_build', 'naming20.rst', 'test/*',
- "old_*",
- '*attic*',
- '*/attic*',
- 'funcargs.rst',
- 'setup.rst',
- 'example/remoteinterp.rst',
- ]
-
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-add_module_names = False
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-sys.path.append(os.path.abspath('_themes'))
-html_theme_path = ['_themes']
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-html_theme = 'flask'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-html_theme_options = {
- 'index_logo': None
-}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-html_short_title = "pytest-%s" % release
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-html_logo = "img/pytest1.png"
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-html_favicon = "img/pytest1favi.ico"
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-#html_sidebars = {'index': 'indexsidebar.html'}
-
-html_sidebars = {
- 'index': [
- 'sidebarintro.html',
- 'globaltoc.html',
- 'links.html',
- 'sourcelink.html',
- 'searchbox.html'
- ],
- '**': [
- 'globaltoc.html',
- 'relations.html',
- 'links.html',
- 'sourcelink.html',
- 'searchbox.html'
- ]
-}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-#html_additional_pages = {'index': 'index.html'}
-
-
-# If false, no module index is generated.
-html_domain_indices = True
-
-# If false, no index is generated.
-html_use_index = False
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-html_show_sourcelink = False
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it. The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'pytestdoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
- ('contents', 'pytest.tex', u'pytest Documentation',
- u'holger krekel, trainer and consultant, http://merlinux.eu', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-latex_logo = 'img/pytest1.png'
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-latex_domain_indices = False
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('usage', 'pytest', u'pytest usage',
- [u'holger krekel at merlinux eu'], 1)
-]
-
-
-# -- Options for Epub output ---------------------------------------------------
-
-# Bibliographic Dublin Core info.
-epub_title = u'pytest'
-epub_author = u'holger krekel at merlinux eu'
-epub_publisher = u'holger krekel at merlinux eu'
-epub_copyright = u'2013, holger krekel et alii'
-
-# The language of the text. It defaults to the language option
-# or en if the language is not set.
-#epub_language = ''
-
-# The scheme of the identifier. Typical schemes are ISBN or URL.
-#epub_scheme = ''
-
-# The unique identifier of the text. This can be a ISBN number
-# or the project homepage.
-#epub_identifier = ''
-
-# A unique identification for the text.
-#epub_uid = ''
-
-# HTML files that should be inserted before the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_pre_files = []
-
-# HTML files shat should be inserted after the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_post_files = []
-
-# A list of files that should not be packed into the epub file.
-#epub_exclude_files = []
-
-# The depth of the table of contents in toc.ncx.
-#epub_tocdepth = 3
-
-# Allow duplicate toc entries.
-#epub_tocdup = True
-
-
-# -- Options for texinfo output ------------------------------------------------
-
-texinfo_documents = [
- (master_doc, 'pytest', 'pytest Documentation',
- ('Holger Krekel@*Benjamin Peterson@*Ronny Pfannschmidt@*'
- 'Floris Bruynooghe@*others'),
- 'pytest',
- 'simple powerful testing with Pytho',
- 'Programming',
- 1),
-]
-
-
-# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {'python': ('http://docs.python.org/', None),
-# 'lib': ("http://docs.python.org/2.7library/", None),
- }
-
-
-def setup(app):
- #from sphinx.ext.autodoc import cut_lines
- #app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
- app.add_description_unit('confval', 'confval',
- objname='configuration value',
- indextemplate='pair: %s; configuration value')
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/contact.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/contact.rst
deleted file mode 100644
index d4a1a03dee3..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/contact.rst
+++ /dev/null
@@ -1,51 +0,0 @@
-
-.. _`contact channels`:
-.. _`contact`:
-
-Contact channels
-===================================
-
-- `pytest issue tracker`_ to report bugs or suggest features (for version
- 2.0 and above).
-
-- `pytest on stackoverflow.com <http://stackoverflow.com/search?q=pytest>`_
- to post questions with the tag ``pytest``. New Questions will usually
- be seen by pytest users or developers and answered quickly.
-
-- `Testing In Python`_: a mailing list for Python testing tools and discussion.
-
-- `pytest-dev at python.org (mailing list)`_ pytest specific announcements and discussions.
-
-- `pytest-commit at python.org (mailing list)`_: for commits and new issues
-
-- :doc:`contribution guide <contributing>` for help on submitting pull
- requests to bitbucket (including using git via gitifyhg).
-
-- #pylib on irc.freenode.net IRC channel for random questions.
-
-- private mail to Holger.Krekel at gmail com if you want to communicate sensitive issues
-
-
-- `merlinux.eu`_ offers pytest and tox-related professional teaching and
- consulting.
-
-.. _`pytest issue tracker`: https://github.com/pytest-dev/pytest/issues
-.. _`old issue tracker`: http://bitbucket.org/hpk42/py-trunk/issues/
-
-.. _`merlinux.eu`: http://merlinux.eu
-
-.. _`get an account`:
-
-.. _tetamap: http://tetamap.wordpress.com
-
-.. _`@pylibcommit`: http://twitter.com/pylibcommit
-
-
-.. _`Testing in Python`: http://lists.idyll.org/listinfo/testing-in-python
-.. _FOAF: http://en.wikipedia.org/wiki/FOAF
-.. _`py-dev`:
-.. _`development mailing list`:
-.. _`pytest-dev at python.org (mailing list)`: http://mail.python.org/mailman/listinfo/pytest-dev
-.. _`py-svn`:
-.. _`pytest-commit at python.org (mailing list)`: http://mail.python.org/mailman/listinfo/pytest-commit
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/contents.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/contents.rst
deleted file mode 100644
index 48c3471b5e2..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/contents.rst
+++ /dev/null
@@ -1,39 +0,0 @@
-.. _toc:
-
-Full pytest documentation
-===========================
-
-`Download latest version as PDF <pytest.pdf>`_
-
-.. `Download latest version as EPUB <http://media.readthedocs.org/epub/pytest/latest/pytest.epub>`_
-
-.. toctree::
- :maxdepth: 2
-
- overview
- apiref
- example/index
- monkeypatch
- tmpdir
- capture
- recwarn
- cache
- plugins
-
- contributing
- talks
-
-.. only:: html
-
- .. toctree::
-
- funcarg_compare
- announce/index
-
-.. only:: html
-
- .. toctree::
- :hidden:
-
- changelog
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/customize.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/customize.rst
deleted file mode 100644
index 34e319c246a..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/customize.rst
+++ /dev/null
@@ -1,228 +0,0 @@
-Basic test configuration
-===================================
-
-Command line options and configuration file settings
------------------------------------------------------------------
-
-You can get help on command line options and values in INI-style
-configurations files by using the general help option::
-
- py.test -h # prints options _and_ config file settings
-
-This will display command line and configuration file settings
-which were registered by installed plugins.
-
-.. _rootdir:
-.. _inifiles:
-
-initialization: determining rootdir and inifile
------------------------------------------------
-
-.. versionadded:: 2.7
-
-pytest determines a "rootdir" for each test run which depends on
-the command line arguments (specified test files, paths) and on
-the existence of inifiles. The determined rootdir and ini-file are
-printed as part of the pytest header. The rootdir is used for constructing
-"nodeids" during collection and may also be used by plugins to store
-project/testrun-specific information.
-
-Here is the algorithm which finds the rootdir from ``args``:
-
-- determine the common ancestor directory for the specified ``args``.
-
-- look for ``pytest.ini``, ``tox.ini`` and ``setup.cfg`` files in the
- ancestor directory and upwards. If one is matched, it becomes the
- ini-file and its directory becomes the rootdir. An existing
- ``pytest.ini`` file will always be considered a match whereas
- ``tox.ini`` and ``setup.cfg`` will only match if they contain
- a ``[pytest]`` section.
-
-- if no ini-file was found, look for ``setup.py`` upwards from
- the common ancestor directory to determine the ``rootdir``.
-
-- if no ini-file and no ``setup.py`` was found, use the already
- determined common ancestor as root directory. This allows to
- work with pytest in structures that are not part of a package
- and don't have any particular ini-file configuration.
-
-Note that options from multiple ini-files candidates are never merged,
-the first one wins (``pytest.ini`` always wins even if it does not
-contain a ``[pytest]`` section).
-
-The ``config`` object will subsequently carry these attributes:
-
-- ``config.rootdir``: the determined root directory, guaranteed to exist.
-
-- ``config.inifile``: the determined ini-file, may be ``None``.
-
-The rootdir is used a reference directory for constructing test
-addresses ("nodeids") and can be used also by plugins for storing
-per-testrun information.
-
-Example::
-
- py.test path/to/testdir path/other/
-
-will determine the common ancestor as ``path`` and then
-check for ini-files as follows::
-
- # first look for pytest.ini files
- path/pytest.ini
- path/setup.cfg # must also contain [pytest] section to match
- path/tox.ini # must also contain [pytest] section to match
- pytest.ini
- ... # all the way down to the root
-
- # now look for setup.py
- path/setup.py
- setup.py
- ... # all the way down to the root
-
-
-.. _`how to change command line options defaults`:
-.. _`adding default options`:
-
-How to change command line options defaults
-------------------------------------------------
-
-It can be tedious to type the same series of command line options
-every time you use ``pytest``. For example, if you always want to see
-detailed info on skipped and xfailed tests, as well as have terser "dot"
-progress output, you can write it into a configuration file:
-
-.. code-block:: ini
-
- # content of pytest.ini
- # (or tox.ini or setup.cfg)
- [pytest]
- addopts = -rsxX -q
-
-Alternatively, you can set a PYTEST_ADDOPTS environment variable to add command
-line options while the environment is in use::
-
- export PYTEST_ADDOPTS="-rsxX -q"
-
-From now on, running ``pytest`` will add the specified options.
-
-
-
-Builtin configuration file options
-----------------------------------------------
-
-.. confval:: minversion
-
- Specifies a minimal pytest version required for running tests.
-
- minversion = 2.1 # will fail if we run with pytest-2.0
-
-.. confval:: addopts
-
- Add the specified ``OPTS`` to the set of command line arguments as if they
- had been specified by the user. Example: if you have this ini file content:
-
- .. code-block:: ini
-
- [pytest]
- addopts = --maxfail=2 -rf # exit after 2 failures, report fail info
-
- issuing ``py.test test_hello.py`` actually means::
-
- py.test --maxfail=2 -rf test_hello.py
-
- Default is to add no options.
-
-.. confval:: norecursedirs
-
- Set the directory basename patterns to avoid when recursing
- for test discovery. The individual (fnmatch-style) patterns are
- applied to the basename of a directory to decide if to recurse into it.
- Pattern matching characters::
-
- * matches everything
- ? matches any single character
- [seq] matches any character in seq
- [!seq] matches any char not in seq
-
- Default patterns are ``'.*', 'CVS', '_darcs', '{arch}', '*.egg'``.
- Setting a ``norecursedirs`` replaces the default. Here is an example of
- how to avoid certain directories:
-
- .. code-block:: ini
-
- # content of setup.cfg
- [pytest]
- norecursedirs = .svn _build tmp*
-
- This would tell ``pytest`` to not look into typical subversion or
- sphinx-build directories or into any ``tmp`` prefixed directory.
-
-.. confval:: testpaths
-
- .. versionadded:: 2.8
-
- Sets list of directories that should be searched for tests when
- no specific directories, files or test ids are given in the command line when
- executing pytest from the :ref:`rootdir <rootdir>` directory.
- Useful when all project tests are in a known location to speed up
- test collection and to avoid picking up undesired tests by accident.
-
- .. code-block:: ini
-
- # content of pytest.ini
- [pytest]
- testpaths = testing doc
-
- This tells pytest to only look for tests in ``testing`` and ``doc``
- directories when executing from the root directory.
-
-.. confval:: python_files
-
- One or more Glob-style file patterns determining which python files
- are considered as test modules.
-
-.. confval:: python_classes
-
- One or more name prefixes or glob-style patterns determining which classes
- are considered for test collection. Here is an example of how to collect
- tests from classes that end in ``Suite``:
-
- .. code-block:: ini
-
- # content of pytest.ini
- [pytest]
- python_classes = *Suite
-
- Note that ``unittest.TestCase`` derived classes are always collected
- regardless of this option, as ``unittest``'s own collection framework is used
- to collect those tests.
-
-.. confval:: python_functions
-
- One or more name prefixes or glob-patterns determining which test functions
- and methods are considered tests. Here is an example of how
- to collect test functions and methods that end in ``_test``:
-
- .. code-block:: ini
-
- # content of pytest.ini
- [pytest]
- python_functions = *_test
-
- Note that this has no effect on methods that live on a ``unittest
- .TestCase`` derived class, as ``unittest``'s own collection framework is used
- to collect those tests.
-
- See :ref:`change naming conventions` for more detailed examples.
-
-.. confval:: doctest_optionflags
-
- One or more doctest flag names from the standard ``doctest`` module.
- :doc:`See how py.test handles doctests <doctest>`.
-
-.. confval:: confcutdir
-
- Sets a directory where search upwards for ``conftest.py`` files stops.
- By default, pytest will stop searching for ``conftest.py`` files upwards
- from ``pytest.ini``/``tox.ini``/``setup.cfg`` of the project if any,
- or up to the file-system root.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/doctest.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/doctest.rst
deleted file mode 100644
index db764141ecb..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/doctest.rst
+++ /dev/null
@@ -1,105 +0,0 @@
-
-Doctest integration for modules and test files
-=========================================================
-
-By default all files matching the ``test*.txt`` pattern will
-be run through the python standard ``doctest`` module. You
-can change the pattern by issuing::
-
- py.test --doctest-glob='*.rst'
-
-on the command line. Since version ``2.9``, ``--doctest-glob``
-can be given multiple times in the command-line.
-
-You can also trigger running of doctests
-from docstrings in all python modules (including regular
-python test modules)::
-
- py.test --doctest-modules
-
-You can make these changes permanent in your project by
-putting them into a pytest.ini file like this:
-
-.. code-block:: ini
-
- # content of pytest.ini
- [pytest]
- addopts = --doctest-modules
-
-If you then have a text file like this::
-
- # content of example.rst
-
- hello this is a doctest
- >>> x = 3
- >>> x
- 3
-
-and another like this::
-
- # content of mymodule.py
- def something():
- """ a doctest in a docstring
- >>> something()
- 42
- """
- return 42
-
-then you can just invoke ``py.test`` without command line options::
-
- $ py.test
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
- collected 1 items
-
- mymodule.py .
-
- ======= 1 passed in 0.12 seconds ========
-
-It is possible to use fixtures using the ``getfixture`` helper::
-
- # content of example.rst
- >>> tmp = getfixture('tmpdir')
- >>> ...
- >>>
-
-Also, :ref:`usefixtures` and :ref:`autouse` fixtures are supported
-when executing text doctest files.
-
-The standard ``doctest`` module provides some setting flags to configure the
-strictness of doctest tests. In py.test You can enable those flags those flags
-using the configuration file. To make pytest ignore trailing whitespaces and
-ignore lengthy exception stack traces you can just write:
-
-.. code-block:: ini
-
- [pytest]
- doctest_optionflags= NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL
-
-py.test also introduces new options to allow doctests to run in Python 2 and
-Python 3 unchanged:
-
-* ``ALLOW_UNICODE``: when enabled, the ``u`` prefix is stripped from unicode
- strings in expected doctest output.
-
-* ``ALLOW_BYTES``: when enabled, the ``b`` prefix is stripped from byte strings
- in expected doctest output.
-
-As with any other option flag, these flags can be enabled in ``pytest.ini`` using
-the ``doctest_optionflags`` ini option:
-
-.. code-block:: ini
-
- [pytest]
- doctest_optionflags = ALLOW_UNICODE ALLOW_BYTES
-
-
-Alternatively, it can be enabled by an inline comment in the doc test
-itself::
-
- # content of example.rst
- >>> get_unicode_greeting() # doctest: +ALLOW_UNICODE
- 'Hello'
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/failure_demo.py b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/failure_demo.py
deleted file mode 100644
index a4ff758b18c..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/failure_demo.py
+++ /dev/null
@@ -1,238 +0,0 @@
-from pytest import raises
-import _pytest._code
-import py
-
-def otherfunc(a,b):
- assert a==b
-
-def somefunc(x,y):
- otherfunc(x,y)
-
-def otherfunc_multi(a,b):
- assert (a ==
- b)
-
-def test_generative(param1, param2):
- assert param1 * 2 < param2
-
-def pytest_generate_tests(metafunc):
- if 'param1' in metafunc.fixturenames:
- metafunc.addcall(funcargs=dict(param1=3, param2=6))
-
-class TestFailing(object):
- def test_simple(self):
- def f():
- return 42
- def g():
- return 43
-
- assert f() == g()
-
- def test_simple_multiline(self):
- otherfunc_multi(
- 42,
- 6*9)
-
- def test_not(self):
- def f():
- return 42
- assert not f()
-
-class TestSpecialisedExplanations(object):
- def test_eq_text(self):
- assert 'spam' == 'eggs'
-
- def test_eq_similar_text(self):
- assert 'foo 1 bar' == 'foo 2 bar'
-
- def test_eq_multiline_text(self):
- assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
-
- def test_eq_long_text(self):
- a = '1'*100 + 'a' + '2'*100
- b = '1'*100 + 'b' + '2'*100
- assert a == b
-
- def test_eq_long_text_multiline(self):
- a = '1\n'*100 + 'a' + '2\n'*100
- b = '1\n'*100 + 'b' + '2\n'*100
- assert a == b
-
- def test_eq_list(self):
- assert [0, 1, 2] == [0, 1, 3]
-
- def test_eq_list_long(self):
- a = [0]*100 + [1] + [3]*100
- b = [0]*100 + [2] + [3]*100
- assert a == b
-
- def test_eq_dict(self):
- assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
-
- def test_eq_set(self):
- assert set([0, 10, 11, 12]) == set([0, 20, 21])
-
- def test_eq_longer_list(self):
- assert [1,2] == [1,2,3]
-
- def test_in_list(self):
- assert 1 in [0, 2, 3, 4, 5]
-
- def test_not_in_text_multiline(self):
- text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail'
- assert 'foo' not in text
-
- def test_not_in_text_single(self):
- text = 'single foo line'
- assert 'foo' not in text
-
- def test_not_in_text_single_long(self):
- text = 'head ' * 50 + 'foo ' + 'tail ' * 20
- assert 'foo' not in text
-
- def test_not_in_text_single_long_term(self):
- text = 'head ' * 50 + 'f'*70 + 'tail ' * 20
- assert 'f'*70 not in text
-
-
-def test_attribute():
- class Foo(object):
- b = 1
- i = Foo()
- assert i.b == 2
-
-
-def test_attribute_instance():
- class Foo(object):
- b = 1
- assert Foo().b == 2
-
-
-def test_attribute_failure():
- class Foo(object):
- def _get_b(self):
- raise Exception('Failed to get attrib')
- b = property(_get_b)
- i = Foo()
- assert i.b == 2
-
-
-def test_attribute_multiple():
- class Foo(object):
- b = 1
- class Bar(object):
- b = 2
- assert Foo().b == Bar().b
-
-
-def globf(x):
- return x+1
-
-class TestRaises:
- def test_raises(self):
- s = 'qwe'
- raises(TypeError, "int(s)")
-
- def test_raises_doesnt(self):
- raises(IOError, "int('3')")
-
- def test_raise(self):
- raise ValueError("demo error")
-
- def test_tupleerror(self):
- a,b = [1]
-
- def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
- l = [1,2,3]
- print ("l is %r" % l)
- a,b = l.pop()
-
- def test_some_error(self):
- if namenotexi:
- pass
-
- def func1(self):
- assert 41 == 42
-
-
-# thanks to Matthew Scott for this test
-def test_dynamic_compile_shows_nicely():
- src = 'def foo():\n assert 1 == 0\n'
- name = 'abc-123'
- module = py.std.imp.new_module(name)
- code = _pytest._code.compile(src, name, 'exec')
- py.builtin.exec_(code, module.__dict__)
- py.std.sys.modules[name] = module
- module.foo()
-
-
-
-class TestMoreErrors:
- def test_complex_error(self):
- def f():
- return 44
- def g():
- return 43
- somefunc(f(), g())
-
- def test_z1_unpack_error(self):
- l = []
- a,b = l
-
- def test_z2_type_error(self):
- l = 3
- a,b = l
-
- def test_startswith(self):
- s = "123"
- g = "456"
- assert s.startswith(g)
-
- def test_startswith_nested(self):
- def f():
- return "123"
- def g():
- return "456"
- assert f().startswith(g())
-
- def test_global_func(self):
- assert isinstance(globf(42), float)
-
- def test_instance(self):
- self.x = 6*7
- assert self.x != 42
-
- def test_compare(self):
- assert globf(10) < 5
-
- def test_try_finally(self):
- x = 1
- try:
- assert x == 0
- finally:
- x = 0
-
-
-class TestCustomAssertMsg:
-
- def test_single_line(self):
- class A:
- a = 1
- b = 2
- assert A.a == b, "A.a appears not to be b"
-
- def test_multiline(self):
- class A:
- a = 1
- b = 2
- assert A.a == b, "A.a appears not to be b\n" \
- "or does not appear to be b\none of those"
-
- def test_custom_repr(self):
- class JSON:
- a = 1
- def __repr__(self):
- return "This is JSON\n{\n 'foo': 'bar'\n}"
- a = JSON()
- b = 2
- assert a.a == b, a
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/test_setup_flow_example.py b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/test_setup_flow_example.py
deleted file mode 100644
index 512330cb4c7..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/test_setup_flow_example.py
+++ /dev/null
@@ -1,42 +0,0 @@
-def setup_module(module):
- module.TestStateFullThing.classcount = 0
-
-class TestStateFullThing:
- def setup_class(cls):
- cls.classcount += 1
-
- def teardown_class(cls):
- cls.classcount -= 1
-
- def setup_method(self, method):
- self.id = eval(method.__name__[5:])
-
- def test_42(self):
- assert self.classcount == 1
- assert self.id == 42
-
- def test_23(self):
- assert self.classcount == 1
- assert self.id == 23
-
-def teardown_module(module):
- assert module.TestStateFullThing.classcount == 0
-
-""" For this example the control flow happens as follows::
- import test_setup_flow_example
- setup_module(test_setup_flow_example)
- setup_class(TestStateFullThing)
- instance = TestStateFullThing()
- setup_method(instance, instance.test_42)
- instance.test_42()
- setup_method(instance, instance.test_23)
- instance.test_23()
- teardown_class(TestStateFullThing)
- teardown_module(test_setup_flow_example)
-
-Note that ``setup_class(TestStateFullThing)`` is called and not
-``TestStateFullThing.setup_class()`` which would require you
-to insert ``setup_class = classmethod(setup_class)`` to make
-your setup function callable.
-"""
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/attic.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/attic.rst
deleted file mode 100644
index 1bc32b2837d..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/attic.rst
+++ /dev/null
@@ -1,79 +0,0 @@
-
-.. _`accept example`:
-
-example: specifying and selecting acceptance tests
---------------------------------------------------------------
-
-.. sourcecode:: python
-
- # ./conftest.py
- def pytest_option(parser):
- group = parser.getgroup("myproject")
- group.addoption("-A", dest="acceptance", action="store_true",
- help="run (slow) acceptance tests")
-
- def pytest_funcarg__accept(request):
- return AcceptFixture(request)
-
- class AcceptFixture:
- def __init__(self, request):
- if not request.config.option.acceptance:
- pytest.skip("specify -A to run acceptance tests")
- self.tmpdir = request.config.mktemp(request.function.__name__, numbered=True)
-
- def run(self, cmd):
- """ called by test code to execute an acceptance test. """
- self.tmpdir.chdir()
- return py.process.cmdexec(cmd)
-
-
-and the actual test function example:
-
-.. sourcecode:: python
-
- def test_some_acceptance_aspect(accept):
- accept.tmpdir.mkdir("somesub")
- result = accept.run("ls -la")
- assert "somesub" in result
-
-If you run this test without specifying a command line option
-the test will get skipped with an appropriate message. Otherwise
-you can start to add convenience and test support methods
-to your AcceptFixture and drive running of tools or
-applications and provide ways to do assertions about
-the output.
-
-.. _`decorate a funcarg`:
-
-example: decorating a funcarg in a test module
---------------------------------------------------------------
-
-For larger scale setups it's sometimes useful to decorate
-a funcarg just for a particular test module. We can
-extend the `accept example`_ by putting this in our test module:
-
-.. sourcecode:: python
-
- def pytest_funcarg__accept(request):
- # call the next factory (living in our conftest.py)
- arg = request.getfuncargvalue("accept")
- # create a special layout in our tempdir
- arg.tmpdir.mkdir("special")
- return arg
-
- class TestSpecialAcceptance:
- def test_sometest(self, accept):
- assert accept.tmpdir.join("special").check()
-
-Our module level factory will be invoked first and it can
-ask its request object to call the next factory and then
-decorate its result. This mechanism allows us to stay
-ignorant of how/where the function argument is provided -
-in our example from a `conftest plugin`_.
-
-sidenote: the temporary directory used here are instances of
-the `py.path.local`_ class which provides many of the os.path
-methods in a convenient way.
-
-.. _`py.path.local`: ../path.html#local
-.. _`conftest plugin`: customize.html#conftestplugin
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/conftest.py b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/conftest.py
deleted file mode 100644
index d689c11b207..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/conftest.py
+++ /dev/null
@@ -1,18 +0,0 @@
-
-import pytest
-
-@pytest.fixture("session")
-def setup(request):
- setup = CostlySetup()
- request.addfinalizer(setup.finalize)
- return setup
-
-class CostlySetup:
- def __init__(self):
- import time
- print ("performing costly setup")
- time.sleep(5)
- self.timecostly = 1
-
- def finalize(self):
- del self.timecostly
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/index.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/index.rst
deleted file mode 100644
index 363de5ab714..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/index.rst
+++ /dev/null
@@ -1,34 +0,0 @@
-
-.. _examples:
-
-Usages and Examples
-===========================================
-
-Here is a (growing) list of examples. :ref:`Contact <contact>` us if you
-need more examples or have questions. Also take a look at the
-:ref:`comprehensive documentation <toc>` which contains many example
-snippets as well. Also, `pytest on stackoverflow.com
-<http://stackoverflow.com/search?q=pytest>`_ often comes with example
-answers.
-
-For basic examples, see
-
-- :doc:`../getting-started` for basic introductory examples
-- :ref:`assert` for basic assertion examples
-- :ref:`fixtures` for basic fixture/setup examples
-- :ref:`parametrize` for basic test function parametrization
-- :doc:`../unittest` for basic unittest integration
-- :doc:`../nose` for basic nosetests integration
-
-The following examples aim at various use cases you might encounter.
-
-.. toctree::
- :maxdepth: 2
-
- reportingdemo
- simple
- parametrize
- markers
- special
- pythoncollection
- nonpython
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/layout1/setup.cfg b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/layout1/setup.cfg
deleted file mode 100644
index 02d3750ee40..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/layout1/setup.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-[pytest]
-testfilepatterns =
- ${topdir}/tests/unit/test_${basename}
- ${topdir}/tests/functional/*.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/markers.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/markers.rst
deleted file mode 100644
index 6bdc603479c..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/markers.rst
+++ /dev/null
@@ -1,592 +0,0 @@
-
-.. _`mark examples`:
-
-Working with custom markers
-=================================================
-
-Here are some example using the :ref:`mark` mechanism.
-
-Marking test functions and selecting them for a run
-----------------------------------------------------
-
-You can "mark" a test function with custom metadata like this::
-
- # content of test_server.py
-
- import pytest
- @pytest.mark.webtest
- def test_send_http():
- pass # perform some webtest test for your app
- def test_something_quick():
- pass
- def test_another():
- pass
- class TestClass:
- def test_method(self):
- pass
-
-.. versionadded:: 2.2
-
-You can then restrict a test run to only run tests marked with ``webtest``::
-
- $ py.test -v -m webtest
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- rootdir: $REGENDOC_TMPDIR, inifile:
- collecting ... collected 4 items
-
- test_server.py::test_send_http PASSED
-
- ======= 3 tests deselected by "-m 'webtest'" ========
- ======= 1 passed, 3 deselected in 0.12 seconds ========
-
-Or the inverse, running all tests except the webtest ones::
-
- $ py.test -v -m "not webtest"
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- rootdir: $REGENDOC_TMPDIR, inifile:
- collecting ... collected 4 items
-
- test_server.py::test_something_quick PASSED
- test_server.py::test_another PASSED
- test_server.py::TestClass::test_method PASSED
-
- ======= 1 tests deselected by "-m 'not webtest'" ========
- ======= 3 passed, 1 deselected in 0.12 seconds ========
-
-Selecting tests based on their node ID
---------------------------------------
-
-You can provide one or more :ref:`node IDs <node-id>` as positional
-arguments to select only specified tests. This makes it easy to select
-tests based on their module, class, method, or function name::
-
- $ py.test -v test_server.py::TestClass::test_method
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- rootdir: $REGENDOC_TMPDIR, inifile:
- collecting ... collected 5 items
-
- test_server.py::TestClass::test_method PASSED
-
- ======= 1 passed in 0.12 seconds ========
-
-You can also select on the class::
-
- $ py.test -v test_server.py::TestClass
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- rootdir: $REGENDOC_TMPDIR, inifile:
- collecting ... collected 4 items
-
- test_server.py::TestClass::test_method PASSED
-
- ======= 1 passed in 0.12 seconds ========
-
-Or select multiple nodes::
-
- $ py.test -v test_server.py::TestClass test_server.py::test_send_http
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- rootdir: $REGENDOC_TMPDIR, inifile:
- collecting ... collected 8 items
-
- test_server.py::TestClass::test_method PASSED
- test_server.py::test_send_http PASSED
-
- ======= 2 passed in 0.12 seconds ========
-
-.. _node-id:
-
-.. note::
-
- Node IDs are of the form ``module.py::class::method`` or
- ``module.py::function``. Node IDs control which tests are
- collected, so ``module.py::class`` will select all test methods
- on the class. Nodes are also created for each parameter of a
- parametrized fixture or test, so selecting a parametrized test
- must include the parameter value, e.g.
- ``module.py::function[param]``.
-
- Node IDs for failing tests are displayed in the test summary info
- when running py.test with the ``-rf`` option. You can also
- construct Node IDs from the output of ``py.test --collectonly``.
-
-Using ``-k expr`` to select tests based on their name
--------------------------------------------------------
-
-.. versionadded: 2.0/2.3.4
-
-You can use the ``-k`` command line option to specify an expression
-which implements a substring match on the test names instead of the
-exact match on markers that ``-m`` provides. This makes it easy to
-select tests based on their names::
-
- $ py.test -v -k http # running with the above defined example module
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- rootdir: $REGENDOC_TMPDIR, inifile:
- collecting ... collected 4 items
-
- test_server.py::test_send_http PASSED
-
- ======= 3 tests deselected by '-khttp' ========
- ======= 1 passed, 3 deselected in 0.12 seconds ========
-
-And you can also run all tests except the ones that match the keyword::
-
- $ py.test -k "not send_http" -v
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- rootdir: $REGENDOC_TMPDIR, inifile:
- collecting ... collected 4 items
-
- test_server.py::test_something_quick PASSED
- test_server.py::test_another PASSED
- test_server.py::TestClass::test_method PASSED
-
- ======= 1 tests deselected by '-knot send_http' ========
- ======= 3 passed, 1 deselected in 0.12 seconds ========
-
-Or to select "http" and "quick" tests::
-
- $ py.test -k "http or quick" -v
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- rootdir: $REGENDOC_TMPDIR, inifile:
- collecting ... collected 4 items
-
- test_server.py::test_send_http PASSED
- test_server.py::test_something_quick PASSED
-
- ======= 2 tests deselected by '-khttp or quick' ========
- ======= 2 passed, 2 deselected in 0.12 seconds ========
-
-.. note::
-
- If you are using expressions such as "X and Y" then both X and Y
- need to be simple non-keyword names. For example, "pass" or "from"
- will result in SyntaxErrors because "-k" evaluates the expression.
-
- However, if the "-k" argument is a simple string, no such restrictions
- apply. Also "-k 'not STRING'" has no restrictions. You can also
- specify numbers like "-k 1.3" to match tests which are parametrized
- with the float "1.3".
-
-Registering markers
--------------------------------------
-
-.. versionadded:: 2.2
-
-.. ini-syntax for custom markers:
-
-Registering markers for your test suite is simple::
-
- # content of pytest.ini
- [pytest]
- markers =
- webtest: mark a test as a webtest.
-
-You can ask which markers exist for your test suite - the list includes our just defined ``webtest`` markers::
-
- $ py.test --markers
- @pytest.mark.webtest: mark a test as a webtest.
-
- @pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
-
- @pytest.mark.xfail(condition, reason=None, run=True, raises=None): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
-
- @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
-
- @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
-
- @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
-
- @pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
-
-
-For an example on how to add and work with markers from a plugin, see
-:ref:`adding a custom marker from a plugin`.
-
-.. note::
-
- It is recommended to explicitly register markers so that:
-
- * there is one place in your test suite defining your markers
-
- * asking for existing markers via ``py.test --markers`` gives good output
-
- * typos in function markers are treated as an error if you use
- the ``--strict`` option. Future versions of ``pytest`` are probably
- going to start treating non-registered markers as errors at some point.
-
-.. _`scoped-marking`:
-
-Marking whole classes or modules
-----------------------------------------------------
-
-You may use ``pytest.mark`` decorators with classes to apply markers to all of
-its test methods::
-
- # content of test_mark_classlevel.py
- import pytest
- @pytest.mark.webtest
- class TestClass:
- def test_startup(self):
- pass
- def test_startup_and_more(self):
- pass
-
-This is equivalent to directly applying the decorator to the
-two test functions.
-
-To remain backward-compatible with Python 2.4 you can also set a
-``pytestmark`` attribute on a TestClass like this::
-
- import pytest
-
- class TestClass:
- pytestmark = pytest.mark.webtest
-
-or if you need to use multiple markers you can use a list::
-
- import pytest
-
- class TestClass:
- pytestmark = [pytest.mark.webtest, pytest.mark.slowtest]
-
-You can also set a module level marker::
-
- import pytest
- pytestmark = pytest.mark.webtest
-
-in which case it will be applied to all functions and
-methods defined in the module.
-
-.. _`marking individual tests when using parametrize`:
-
-Marking individual tests when using parametrize
------------------------------------------------
-
-When using parametrize, applying a mark will make it apply
-to each individual test. However it is also possible to
-apply a marker to an individual test instance::
-
- import pytest
-
- @pytest.mark.foo
- @pytest.mark.parametrize(("n", "expected"), [
- (1, 2),
- pytest.mark.bar((1, 3)),
- (2, 3),
- ])
- def test_increment(n, expected):
- assert n + 1 == expected
-
-In this example the mark "foo" will apply to each of the three
-tests, whereas the "bar" mark is only applied to the second test.
-Skip and xfail marks can also be applied in this way, see :ref:`skip/xfail with parametrize`.
-
-.. note::
-
- If the data you are parametrizing happen to be single callables, you need to be careful
- when marking these items. `pytest.mark.xfail(my_func)` won't work because it's also the
- signature of a function being decorated. To resolve this ambiguity, you need to pass a
- reason argument:
- `pytest.mark.xfail(func_bar, reason="Issue#7")`.
-
-
-.. _`adding a custom marker from a plugin`:
-
-Custom marker and command line option to control test runs
-----------------------------------------------------------
-
-.. regendoc:wipe
-
-Plugins can provide custom markers and implement specific behaviour
-based on it. This is a self-contained example which adds a command
-line option and a parametrized test function marker to run tests
-specifies via named environments::
-
- # content of conftest.py
-
- import pytest
- def pytest_addoption(parser):
- parser.addoption("-E", action="store", metavar="NAME",
- help="only run tests matching the environment NAME.")
-
- def pytest_configure(config):
- # register an additional marker
- config.addinivalue_line("markers",
- "env(name): mark test to run only on named environment")
-
- def pytest_runtest_setup(item):
- envmarker = item.get_marker("env")
- if envmarker is not None:
- envname = envmarker.args[0]
- if envname != item.config.getoption("-E"):
- pytest.skip("test requires env %r" % envname)
-
-A test file using this local plugin::
-
- # content of test_someenv.py
-
- import pytest
- @pytest.mark.env("stage1")
- def test_basic_db_operation():
- pass
-
-and an example invocations specifying a different environment than what
-the test needs::
-
- $ py.test -E stage2
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 1 items
-
- test_someenv.py s
-
- ======= 1 skipped in 0.12 seconds ========
-
-and here is one that specifies exactly the environment needed::
-
- $ py.test -E stage1
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 1 items
-
- test_someenv.py .
-
- ======= 1 passed in 0.12 seconds ========
-
-The ``--markers`` option always gives you a list of available markers::
-
- $ py.test --markers
- @pytest.mark.env(name): mark test to run only on named environment
-
- @pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
-
- @pytest.mark.xfail(condition, reason=None, run=True, raises=None): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
-
- @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
-
- @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
-
- @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
-
- @pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
-
-
-Reading markers which were set from multiple places
-----------------------------------------------------
-
-.. versionadded: 2.2.2
-
-.. regendoc:wipe
-
-If you are heavily using markers in your test suite you may encounter the case where a marker is applied several times to a test function. From plugin
-code you can read over all such settings. Example::
-
- # content of test_mark_three_times.py
- import pytest
- pytestmark = pytest.mark.glob("module", x=1)
-
- @pytest.mark.glob("class", x=2)
- class TestClass:
- @pytest.mark.glob("function", x=3)
- def test_something(self):
- pass
-
-Here we have the marker "glob" applied three times to the same
-test function. From a conftest file we can read it like this::
-
- # content of conftest.py
- import sys
-
- def pytest_runtest_setup(item):
- g = item.get_marker("glob")
- if g is not None:
- for info in g:
- print ("glob args=%s kwargs=%s" %(info.args, info.kwargs))
- sys.stdout.flush()
-
-Let's run this without capturing output and see what we get::
-
- $ py.test -q -s
- glob args=('function',) kwargs={'x': 3}
- glob args=('class',) kwargs={'x': 2}
- glob args=('module',) kwargs={'x': 1}
- .
- 1 passed in 0.12 seconds
-
-marking platform specific tests with pytest
---------------------------------------------------------------
-
-.. regendoc:wipe
-
-Consider you have a test suite which marks tests for particular platforms,
-namely ``pytest.mark.darwin``, ``pytest.mark.win32`` etc. and you
-also have tests that run on all platforms and have no specific
-marker. If you now want to have a way to only run the tests
-for your particular platform, you could use the following plugin::
-
- # content of conftest.py
- #
- import sys
- import pytest
-
- ALL = set("darwin linux2 win32".split())
-
- def pytest_runtest_setup(item):
- if isinstance(item, item.Function):
- plat = sys.platform
- if not item.get_marker(plat):
- if ALL.intersection(item.keywords):
- pytest.skip("cannot run on platform %s" %(plat))
-
-then tests will be skipped if they were specified for a different platform.
-Let's do a little test file to show how this looks like::
-
- # content of test_plat.py
-
- import pytest
-
- @pytest.mark.darwin
- def test_if_apple_is_evil():
- pass
-
- @pytest.mark.linux2
- def test_if_linux_works():
- pass
-
- @pytest.mark.win32
- def test_if_win32_crashes():
- pass
-
- def test_runs_everywhere():
- pass
-
-then you will see two test skipped and two executed tests as expected::
-
- $ py.test -rs # this option reports skip reasons
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 4 items
-
- test_plat.py sss.
- ======= short test summary info ========
- SKIP [3] $REGENDOC_TMPDIR/conftest.py:12: cannot run on platform linux
-
- ======= 1 passed, 3 skipped in 0.12 seconds ========
-
-Note that if you specify a platform via the marker-command line option like this::
-
- $ py.test -m linux2
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 4 items
-
- test_plat.py s
-
- ======= 3 tests deselected by "-m 'linux2'" ========
- ======= 1 skipped, 3 deselected in 0.12 seconds ========
-
-then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests.
-
-Automatically adding markers based on test names
---------------------------------------------------------
-
-.. regendoc:wipe
-
-If you a test suite where test function names indicate a certain
-type of test, you can implement a hook that automatically defines
-markers so that you can use the ``-m`` option with it. Let's look
-at this test module::
-
- # content of test_module.py
-
- def test_interface_simple():
- assert 0
-
- def test_interface_complex():
- assert 0
-
- def test_event_simple():
- assert 0
-
- def test_something_else():
- assert 0
-
-We want to dynamically define two markers and can do it in a
-``conftest.py`` plugin::
-
- # content of conftest.py
-
- import pytest
- def pytest_collection_modifyitems(items):
- for item in items:
- if "interface" in item.nodeid:
- item.add_marker(pytest.mark.interface)
- elif "event" in item.nodeid:
- item.add_marker(pytest.mark.event)
-
-We can now use the ``-m option`` to select one set::
-
- $ py.test -m interface --tb=short
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 4 items
-
- test_module.py FF
-
- ======= FAILURES ========
- _______ test_interface_simple ________
- test_module.py:3: in test_interface_simple
- assert 0
- E assert 0
- _______ test_interface_complex ________
- test_module.py:6: in test_interface_complex
- assert 0
- E assert 0
- ======= 2 tests deselected by "-m 'interface'" ========
- ======= 2 failed, 2 deselected in 0.12 seconds ========
-
-or to select both "event" and "interface" tests::
-
- $ py.test -m "interface or event" --tb=short
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 4 items
-
- test_module.py FFF
-
- ======= FAILURES ========
- _______ test_interface_simple ________
- test_module.py:3: in test_interface_simple
- assert 0
- E assert 0
- _______ test_interface_complex ________
- test_module.py:6: in test_interface_complex
- assert 0
- E assert 0
- _______ test_event_simple ________
- test_module.py:9: in test_event_simple
- assert 0
- E assert 0
- ======= 1 tests deselected by "-m 'interface or event'" ========
- ======= 3 failed, 1 deselected in 0.12 seconds ========
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/multipython.py b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/multipython.py
deleted file mode 100644
index 66a368a1267..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/multipython.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""
-module containing a parametrized tests testing cross-python
-serialization via the pickle module.
-"""
-import py
-import pytest
-import _pytest._code
-
-pythonlist = ['python2.6', 'python2.7', 'python3.3']
-@pytest.fixture(params=pythonlist)
-def python1(request, tmpdir):
- picklefile = tmpdir.join("data.pickle")
- return Python(request.param, picklefile)
-
-@pytest.fixture(params=pythonlist)
-def python2(request, python1):
- return Python(request.param, python1.picklefile)
-
-class Python:
- def __init__(self, version, picklefile):
- self.pythonpath = py.path.local.sysfind(version)
- if not self.pythonpath:
- pytest.skip("%r not found" %(version,))
- self.picklefile = picklefile
- def dumps(self, obj):
- dumpfile = self.picklefile.dirpath("dump.py")
- dumpfile.write(_pytest._code.Source("""
- import pickle
- f = open(%r, 'wb')
- s = pickle.dump(%r, f, protocol=2)
- f.close()
- """ % (str(self.picklefile), obj)))
- py.process.cmdexec("%s %s" %(self.pythonpath, dumpfile))
-
- def load_and_is_true(self, expression):
- loadfile = self.picklefile.dirpath("load.py")
- loadfile.write(_pytest._code.Source("""
- import pickle
- f = open(%r, 'rb')
- obj = pickle.load(f)
- f.close()
- res = eval(%r)
- if not res:
- raise SystemExit(1)
- """ % (str(self.picklefile), expression)))
- print (loadfile)
- py.process.cmdexec("%s %s" %(self.pythonpath, loadfile))
-
-@pytest.mark.parametrize("obj", [42, {}, {1:3},])
-def test_basic_objects(python1, python2, obj):
- python1.dumps(obj)
- python2.load_and_is_true("obj == %s" % obj)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython.rst
deleted file mode 100644
index 6437e398457..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython.rst
+++ /dev/null
@@ -1,91 +0,0 @@
-
-.. _`non-python tests`:
-
-Working with non-python tests
-====================================================
-
-.. _`yaml plugin`:
-
-A basic example for specifying tests in Yaml files
---------------------------------------------------------------
-
-.. _`pytest-yamlwsgi`: http://bitbucket.org/aafshar/pytest-yamlwsgi/src/tip/pytest_yamlwsgi.py
-.. _`PyYAML`: http://pypi.python.org/pypi/PyYAML/
-
-Here is an example ``conftest.py`` (extracted from Ali Afshnars special purpose `pytest-yamlwsgi`_ plugin). This ``conftest.py`` will collect ``test*.yml`` files and will execute the yaml-formatted content as custom tests:
-
-.. include:: nonpython/conftest.py
- :literal:
-
-You can create a simple example file:
-
-.. include:: nonpython/test_simple.yml
- :literal:
-
-and if you installed `PyYAML`_ or a compatible YAML-parser you can
-now execute the test specification::
-
- nonpython $ py.test test_simple.yml
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
- collected 2 items
-
- test_simple.yml F.
-
- ======= FAILURES ========
- _______ usecase: hello ________
- usecase execution failed
- spec failed: 'some': 'other'
- no further details known at this point.
- ======= 1 failed, 1 passed in 0.12 seconds ========
-
-.. regendoc:wipe
-
-You get one dot for the passing ``sub1: sub1`` check and one failure.
-Obviously in the above ``conftest.py`` you'll want to implement a more
-interesting interpretation of the yaml-values. You can easily write
-your own domain specific testing language this way.
-
-.. note::
-
- ``repr_failure(excinfo)`` is called for representing test failures.
- If you create custom collection nodes you can return an error
- representation string of your choice. It
- will be reported as a (red) string.
-
-``reportinfo()`` is used for representing the test location and is also
-consulted when reporting in ``verbose`` mode::
-
- nonpython $ py.test -v
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
- collecting ... collected 2 items
-
- test_simple.yml::hello FAILED
- test_simple.yml::ok PASSED
-
- ======= FAILURES ========
- _______ usecase: hello ________
- usecase execution failed
- spec failed: 'some': 'other'
- no further details known at this point.
- ======= 1 failed, 1 passed in 0.12 seconds ========
-
-.. regendoc:wipe
-
-While developing your custom test collection and execution it's also
-interesting to just look at the collection tree::
-
- nonpython $ py.test --collect-only
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
- collected 2 items
- <YamlFile 'test_simple.yml'>
- <YamlItem 'hello'>
- <YamlItem 'ok'>
-
- ======= no tests ran in 0.12 seconds ========
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython/conftest.py b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython/conftest.py
deleted file mode 100644
index 2406e8f10bb..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython/conftest.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# content of conftest.py
-
-import pytest
-
-def pytest_collect_file(parent, path):
- if path.ext == ".yml" and path.basename.startswith("test"):
- return YamlFile(path, parent)
-
-class YamlFile(pytest.File):
- def collect(self):
- import yaml # we need a yaml parser, e.g. PyYAML
- raw = yaml.safe_load(self.fspath.open())
- for name, spec in raw.items():
- yield YamlItem(name, self, spec)
-
-class YamlItem(pytest.Item):
- def __init__(self, name, parent, spec):
- super(YamlItem, self).__init__(name, parent)
- self.spec = spec
-
- def runtest(self):
- for name, value in self.spec.items():
- # some custom test execution (dumb example follows)
- if name != value:
- raise YamlException(self, name, value)
-
- def repr_failure(self, excinfo):
- """ called when self.runtest() raises an exception. """
- if isinstance(excinfo.value, YamlException):
- return "\n".join([
- "usecase execution failed",
- " spec failed: %r: %r" % excinfo.value.args[1:3],
- " no further details known at this point."
- ])
-
- def reportinfo(self):
- return self.fspath, 0, "usecase: %s" % self.name
-
-class YamlException(Exception):
- """ custom exception for error reporting. """
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/parametrize.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/parametrize.rst
deleted file mode 100644
index 5d637ffcbf9..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/parametrize.rst
+++ /dev/null
@@ -1,475 +0,0 @@
-
-.. _paramexamples:
-
-Parametrizing tests
-=================================================
-
-.. currentmodule:: _pytest.python
-
-``pytest`` allows to easily parametrize test functions.
-For basic docs, see :ref:`parametrize-basics`.
-
-In the following we provide some examples using
-the builtin mechanisms.
-
-Generating parameters combinations, depending on command line
-----------------------------------------------------------------------------
-
-.. regendoc:wipe
-
-Let's say we want to execute a test with different computation
-parameters and the parameter range shall be determined by a command
-line argument. Let's first write a simple (do-nothing) computation test::
-
- # content of test_compute.py
-
- def test_compute(param1):
- assert param1 < 4
-
-Now we add a test configuration like this::
-
- # content of conftest.py
-
- def pytest_addoption(parser):
- parser.addoption("--all", action="store_true",
- help="run all combinations")
-
- def pytest_generate_tests(metafunc):
- if 'param1' in metafunc.fixturenames:
- if metafunc.config.option.all:
- end = 5
- else:
- end = 2
- metafunc.parametrize("param1", range(end))
-
-This means that we only run 2 tests if we do not pass ``--all``::
-
- $ py.test -q test_compute.py
- ..
- 2 passed in 0.12 seconds
-
-We run only two computations, so we see two dots.
-let's run the full monty::
-
- $ py.test -q --all
- ....F
- ======= FAILURES ========
- _______ test_compute[4] ________
-
- param1 = 4
-
- def test_compute(param1):
- > assert param1 < 4
- E assert 4 < 4
-
- test_compute.py:3: AssertionError
- 1 failed, 4 passed in 0.12 seconds
-
-As expected when running the full range of ``param1`` values
-we'll get an error on the last one.
-
-
-Different options for test IDs
-------------------------------------
-
-pytest will build a string that is the test ID for each set of values in a
-parametrized test. These IDs can be used with ``-k`` to select specific cases
-to run, and they will also identify the specific case when one is failing.
-Running pytest with ``--collect-only`` will show the generated IDs.
-
-Numbers, strings, booleans and None will have their usual string representation
-used in the test ID. For other objects, pytest will make a string based on
-the argument name::
-
- # content of test_time.py
-
- import pytest
-
- from datetime import datetime, timedelta
-
- testdata = [
- (datetime(2001, 12, 12), datetime(2001, 12, 11), timedelta(1)),
- (datetime(2001, 12, 11), datetime(2001, 12, 12), timedelta(-1)),
- ]
-
-
- @pytest.mark.parametrize("a,b,expected", testdata)
- def test_timedistance_v0(a, b, expected):
- diff = a - b
- assert diff == expected
-
-
- @pytest.mark.parametrize("a,b,expected", testdata, ids=["forward", "backward"])
- def test_timedistance_v1(a, b, expected):
- diff = a - b
- assert diff == expected
-
-
- def idfn(val):
- if isinstance(val, (datetime,)):
- # note this wouldn't show any hours/minutes/seconds
- return val.strftime('%Y%m%d')
-
-
- @pytest.mark.parametrize("a,b,expected", testdata, ids=idfn)
- def test_timedistance_v2(a, b, expected):
- diff = a - b
- assert diff == expected
-
-
-In ``test_timedistance_v0``, we let pytest generate the test IDs.
-
-In ``test_timedistance_v1``, we specified ``ids`` as a list of strings which were
-used as the test IDs. These are succinct, but can be a pain to maintain.
-
-In ``test_timedistance_v2``, we specified ``ids`` as a function that can generate a
-string representation to make part of the test ID. So our ``datetime`` values use the
-label generated by ``idfn``, but because we didn't generate a label for ``timedelta``
-objects, they are still using the default pytest representation::
-
-
- $ py.test test_time.py --collect-only
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 6 items
- <Module 'test_time.py'>
- <Function 'test_timedistance_v0[a0-b0-expected0]'>
- <Function 'test_timedistance_v0[a1-b1-expected1]'>
- <Function 'test_timedistance_v1[forward]'>
- <Function 'test_timedistance_v1[backward]'>
- <Function 'test_timedistance_v2[20011212-20011211-expected0]'>
- <Function 'test_timedistance_v2[20011211-20011212-expected1]'>
-
- ======= no tests ran in 0.12 seconds ========
-
-A quick port of "testscenarios"
-------------------------------------
-
-.. _`test scenarios`: http://pypi.python.org/pypi/testscenarios/
-
-Here is a quick port to run tests configured with `test scenarios`_,
-an add-on from Robert Collins for the standard unittest framework. We
-only have to work a bit to construct the correct arguments for pytest's
-:py:func:`Metafunc.parametrize`::
-
- # content of test_scenarios.py
-
- def pytest_generate_tests(metafunc):
- idlist = []
- argvalues = []
- for scenario in metafunc.cls.scenarios:
- idlist.append(scenario[0])
- items = scenario[1].items()
- argnames = [x[0] for x in items]
- argvalues.append(([x[1] for x in items]))
- metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class")
-
- scenario1 = ('basic', {'attribute': 'value'})
- scenario2 = ('advanced', {'attribute': 'value2'})
-
- class TestSampleWithScenarios:
- scenarios = [scenario1, scenario2]
-
- def test_demo1(self, attribute):
- assert isinstance(attribute, str)
-
- def test_demo2(self, attribute):
- assert isinstance(attribute, str)
-
-this is a fully self-contained example which you can run with::
-
- $ py.test test_scenarios.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 4 items
-
- test_scenarios.py ....
-
- ======= 4 passed in 0.12 seconds ========
-
-If you just collect tests you'll also nicely see 'advanced' and 'basic' as variants for the test function::
-
-
- $ py.test --collect-only test_scenarios.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 4 items
- <Module 'test_scenarios.py'>
- <Class 'TestSampleWithScenarios'>
- <Instance '()'>
- <Function 'test_demo1[basic]'>
- <Function 'test_demo2[basic]'>
- <Function 'test_demo1[advanced]'>
- <Function 'test_demo2[advanced]'>
-
- ======= no tests ran in 0.12 seconds ========
-
-Note that we told ``metafunc.parametrize()`` that your scenario values
-should be considered class-scoped. With pytest-2.3 this leads to a
-resource-based ordering.
-
-Deferring the setup of parametrized resources
----------------------------------------------------
-
-.. regendoc:wipe
-
-The parametrization of test functions happens at collection
-time. It is a good idea to setup expensive resources like DB
-connections or subprocess only when the actual test is run.
-Here is a simple example how you can achieve that, first
-the actual test requiring a ``db`` object::
-
- # content of test_backends.py
-
- import pytest
- def test_db_initialized(db):
- # a dummy test
- if db.__class__.__name__ == "DB2":
- pytest.fail("deliberately failing for demo purposes")
-
-We can now add a test configuration that generates two invocations of
-the ``test_db_initialized`` function and also implements a factory that
-creates a database object for the actual test invocations::
-
- # content of conftest.py
- import pytest
-
- def pytest_generate_tests(metafunc):
- if 'db' in metafunc.fixturenames:
- metafunc.parametrize("db", ['d1', 'd2'], indirect=True)
-
- class DB1:
- "one database object"
- class DB2:
- "alternative database object"
-
- @pytest.fixture
- def db(request):
- if request.param == "d1":
- return DB1()
- elif request.param == "d2":
- return DB2()
- else:
- raise ValueError("invalid internal test config")
-
-Let's first see how it looks like at collection time::
-
- $ py.test test_backends.py --collect-only
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 2 items
- <Module 'test_backends.py'>
- <Function 'test_db_initialized[d1]'>
- <Function 'test_db_initialized[d2]'>
-
- ======= no tests ran in 0.12 seconds ========
-
-And then when we run the test::
-
- $ py.test -q test_backends.py
- .F
- ======= FAILURES ========
- _______ test_db_initialized[d2] ________
-
- db = <conftest.DB2 object at 0xdeadbeef>
-
- def test_db_initialized(db):
- # a dummy test
- if db.__class__.__name__ == "DB2":
- > pytest.fail("deliberately failing for demo purposes")
- E Failed: deliberately failing for demo purposes
-
- test_backends.py:6: Failed
- 1 failed, 1 passed in 0.12 seconds
-
-The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``db`` fixture function has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase.
-
-.. regendoc:wipe
-
-Apply indirect on particular arguments
----------------------------------------------------
-
-Very often parametrization uses more than one argument name. There is opportunity to apply ``indirect``
-parameter on particular arguments. It can be done by passing list or tuple of
-arguments' names to ``indirect``. In the example below there is a function ``test_indirect`` which uses
-two fixtures: ``x`` and ``y``. Here we give to indirect the list, which contains the name of the
-fixture ``x``. The indirect parameter will be applied to this argument only, and the value ``a``
-will be passed to respective fixture function::
-
- # content of test_indirect_list.py
-
- import pytest
- @pytest.fixture(scope='function')
- def x(request):
- return request.param * 3
-
- @pytest.fixture(scope='function')
- def y(request):
- return request.param * 2
-
- @pytest.mark.parametrize('x, y', [('a', 'b')], indirect=['x'])
- def test_indirect(x,y):
- assert x == 'aaa'
- assert y == 'b'
-
-The result of this test will be successful::
-
- $ py.test test_indirect_list.py --collect-only
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 1 items
- <Module 'test_indirect_list.py'>
- <Function 'test_indirect[a-b]'>
-
- ======= no tests ran in 0.12 seconds ========
-
-.. regendoc:wipe
-
-Parametrizing test methods through per-class configuration
---------------------------------------------------------------
-
-.. _`unittest parametrizer`: https://github.com/testing-cabal/unittest-ext/blob/master/params.py
-
-
-Here is an example ``pytest_generate_function`` function implementing a
-parametrization scheme similar to Michael Foord's `unittest
-parametrizer`_ but in a lot less code::
-
- # content of ./test_parametrize.py
- import pytest
-
- def pytest_generate_tests(metafunc):
- # called once per each test function
- funcarglist = metafunc.cls.params[metafunc.function.__name__]
- argnames = list(funcarglist[0])
- metafunc.parametrize(argnames, [[funcargs[name] for name in argnames]
- for funcargs in funcarglist])
-
- class TestClass:
- # a map specifying multiple argument sets for a test method
- params = {
- 'test_equals': [dict(a=1, b=2), dict(a=3, b=3), ],
- 'test_zerodivision': [dict(a=1, b=0), ],
- }
-
- def test_equals(self, a, b):
- assert a == b
-
- def test_zerodivision(self, a, b):
- pytest.raises(ZeroDivisionError, "a/b")
-
-Our test generator looks up a class-level definition which specifies which
-argument sets to use for each test function. Let's run it::
-
- $ py.test -q
- F..
- ======= FAILURES ========
- _______ TestClass.test_equals[1-2] ________
-
- self = <test_parametrize.TestClass object at 0xdeadbeef>, a = 1, b = 2
-
- def test_equals(self, a, b):
- > assert a == b
- E assert 1 == 2
-
- test_parametrize.py:18: AssertionError
- 1 failed, 2 passed in 0.12 seconds
-
-Indirect parametrization with multiple fixtures
---------------------------------------------------------------
-
-Here is a stripped down real-life example of using parametrized
-testing for testing serialization of objects between different python
-interpreters. We define a ``test_basic_objects`` function which
-is to be run with different sets of arguments for its three arguments:
-
-* ``python1``: first python interpreter, run to pickle-dump an object to a file
-* ``python2``: second interpreter, run to pickle-load an object from a file
-* ``obj``: object to be dumped/loaded
-
-.. literalinclude:: multipython.py
-
-Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
-
- . $ py.test -rs -q multipython.py
- ssssssssssss...ssssssssssss
- ======= short test summary info ========
- SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:23: 'python3.3' not found
- SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:23: 'python2.6' not found
- 3 passed, 24 skipped in 0.12 seconds
-
-Indirect parametrization of optional implementations/imports
---------------------------------------------------------------------
-
-If you want to compare the outcomes of several implementations of a given
-API, you can write test functions that receive the already imported implementations
-and get skipped in case the implementation is not importable/available. Let's
-say we have a "base" implementation and the other (possibly optimized ones)
-need to provide similar results::
-
- # content of conftest.py
-
- import pytest
-
- @pytest.fixture(scope="session")
- def basemod(request):
- return pytest.importorskip("base")
-
- @pytest.fixture(scope="session", params=["opt1", "opt2"])
- def optmod(request):
- return pytest.importorskip(request.param)
-
-And then a base implementation of a simple function::
-
- # content of base.py
- def func1():
- return 1
-
-And an optimized version::
-
- # content of opt1.py
- def func1():
- return 1.0001
-
-And finally a little test module::
-
- # content of test_module.py
-
- def test_func1(basemod, optmod):
- assert round(basemod.func1(), 3) == round(optmod.func1(), 3)
-
-
-If you run this with reporting for skips enabled::
-
- $ py.test -rs test_module.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 2 items
-
- test_module.py .s
- ======= short test summary info ========
- SKIP [1] $REGENDOC_TMPDIR/conftest.py:10: could not import 'opt2'
-
- ======= 1 passed, 1 skipped in 0.12 seconds ========
-
-You'll see that we don't have a ``opt2`` module and thus the second test run
-of our ``test_func1`` was skipped. A few notes:
-
-- the fixture functions in the ``conftest.py`` file are "session-scoped" because we
- don't need to import more than once
-
-- if you have multiple test functions and a skipped import, you will see
- the ``[1]`` count increasing in the report
-
-- you can put :ref:`@pytest.mark.parametrize <@pytest.mark.parametrize>` style
- parametrization on the test functions to parametrize input/output
- values as well.
-
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/pythoncollection.py b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/pythoncollection.py
deleted file mode 100644
index 05858eb854b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/pythoncollection.py
+++ /dev/null
@@ -1,11 +0,0 @@
-
-# run this with $ py.test --collect-only test_collectonly.py
-#
-def test_function():
- pass
-
-class TestClass:
- def test_method(self):
- pass
- def test_anothermethod(self):
- pass
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/pythoncollection.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/pythoncollection.rst
deleted file mode 100644
index 5faf4c6c8ad..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/pythoncollection.rst
+++ /dev/null
@@ -1,192 +0,0 @@
-Changing standard (Python) test discovery
-===============================================
-
-Ignore paths during test collection
------------------------------------
-
-You can easily ignore certain test directories and modules during collection
-by passing the ``--ignore=path`` option on the cli. ``pytest`` allows multiple
-``--ignore`` options. Example::
-
- tests/
- ├── example
- │   ├── test_example_01.py
- │   ├── test_example_02.py
- │   └── test_example_03.py
- ├── foobar
- │   ├── test_foobar_01.py
- │   ├── test_foobar_02.py
- │   └── test_foobar_03.py
- └── hello
- └── world
- ├── test_world_01.py
- ├── test_world_02.py
- └── test_world_03.py
-
-Now if you invoke ``pytest`` with ``--ignore=tests/foobar/test_foobar_03.py --ignore=tests/hello/``,
-you will see that ``pytest`` only collects test-modules, which do not match the patterns specified::
-
- ========= test session starts ==========
- platform darwin -- Python 2.7.10, pytest-2.8.2, py-1.4.30, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 5 items
-
- tests/example/test_example_01.py .
- tests/example/test_example_02.py .
- tests/example/test_example_03.py .
- tests/foobar/test_foobar_01.py .
- tests/foobar/test_foobar_02.py .
-
- ======= 5 passed in 0.02 seconds =======
-
-
-Changing directory recursion
------------------------------------------------------
-
-You can set the :confval:`norecursedirs` option in an ini-file, for example your ``setup.cfg`` in the project root directory::
-
- # content of setup.cfg
- [pytest]
- norecursedirs = .svn _build tmp*
-
-This would tell ``pytest`` to not recurse into typical subversion or sphinx-build directories or into any ``tmp`` prefixed directory.
-
-.. _`change naming conventions`:
-
-Changing naming conventions
------------------------------------------------------
-
-You can configure different naming conventions by setting
-the :confval:`python_files`, :confval:`python_classes` and
-:confval:`python_functions` configuration options. Example::
-
- # content of setup.cfg
- # can also be defined in in tox.ini or pytest.ini file
- [pytest]
- python_files=check_*.py
- python_classes=Check
- python_functions=*_check
-
-This would make ``pytest`` look for tests in files that match the ``check_*
-.py`` glob-pattern, ``Check`` prefixes in classes, and functions and methods
-that match ``*_check``. For example, if we have::
-
- # content of check_myapp.py
- class CheckMyApp:
- def simple_check(self):
- pass
- def complex_check(self):
- pass
-
-then the test collection looks like this::
-
- $ py.test --collect-only
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile: setup.cfg
- collected 2 items
- <Module 'check_myapp.py'>
- <Class 'CheckMyApp'>
- <Instance '()'>
- <Function 'simple_check'>
- <Function 'complex_check'>
-
- ======= no tests ran in 0.12 seconds ========
-
-.. note::
-
- the ``python_functions`` and ``python_classes`` options has no effect
- for ``unittest.TestCase`` test discovery because pytest delegates
- detection of test case methods to unittest code.
-
-Interpreting cmdline arguments as Python packages
------------------------------------------------------
-
-You can use the ``--pyargs`` option to make ``pytest`` try
-interpreting arguments as python package names, deriving
-their file system path and then running the test. For
-example if you have unittest2 installed you can type::
-
- py.test --pyargs unittest2.test.test_skipping -q
-
-which would run the respective test module. Like with
-other options, through an ini-file and the :confval:`addopts` option you
-can make this change more permanently::
-
- # content of pytest.ini
- [pytest]
- addopts = --pyargs
-
-Now a simple invocation of ``py.test NAME`` will check
-if NAME exists as an importable package/module and otherwise
-treat it as a filesystem path.
-
-Finding out what is collected
------------------------------------------------
-
-You can always peek at the collection tree without running tests like this::
-
- . $ py.test --collect-only pythoncollection.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
- collected 3 items
- <Module 'CWD/pythoncollection.py'>
- <Function 'test_function'>
- <Class 'TestClass'>
- <Instance '()'>
- <Function 'test_method'>
- <Function 'test_anothermethod'>
-
- ======= no tests ran in 0.12 seconds ========
-
-customizing test collection to find all .py files
----------------------------------------------------------
-
-.. regendoc:wipe
-
-You can easily instruct ``pytest`` to discover tests from every python file::
-
-
- # content of pytest.ini
- [pytest]
- python_files = *.py
-
-However, many projects will have a ``setup.py`` which they don't want to be imported. Moreover, there may files only importable by a specific python version.
-For such cases you can dynamically define files to be ignored by listing
-them in a ``conftest.py`` file::
-
- # content of conftest.py
- import sys
-
- collect_ignore = ["setup.py"]
- if sys.version_info[0] > 2:
- collect_ignore.append("pkg/module_py2.py")
-
-And then if you have a module file like this::
-
- # content of pkg/module_py2.py
- def test_only_on_python2():
- try:
- assert 0
- except Exception, e:
- pass
-
-and a setup.py dummy file like this::
-
- # content of setup.py
- 0/0 # will raise exception if imported
-
-then a pytest run on python2 will find the one test when run with a python2
-interpreters and will leave out the setup.py file::
-
- $ py.test --collect-only
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
- collected 0 items
-
- ======= no tests ran in 0.12 seconds ========
-
-If you run with a Python3 interpreter the moduled added through the conftest.py file will not be considered for test collection.
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/reportingdemo.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/reportingdemo.rst
deleted file mode 100644
index 28624aa07b8..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/reportingdemo.rst
+++ /dev/null
@@ -1,598 +0,0 @@
-
-.. _`tbreportdemo`:
-
-Demo of Python failure reports with pytest
-==================================================
-
-Here is a nice run of several tens of failures
-and how ``pytest`` presents things (unfortunately
-not showing the nice colors here in the HTML that you
-get on the terminal - we are working on that):
-
-.. code-block:: python
-
- assertion $ py.test failure_demo.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR/assertion, inifile:
- collected 42 items
-
- failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-
- ======= FAILURES ========
- _______ test_generative[0] ________
-
- param1 = 3, param2 = 6
-
- def test_generative(param1, param2):
- > assert param1 * 2 < param2
- E assert (3 * 2) < 6
-
- failure_demo.py:16: AssertionError
- _______ TestFailing.test_simple ________
-
- self = <failure_demo.TestFailing object at 0xdeadbeef>
-
- def test_simple(self):
- def f():
- return 42
- def g():
- return 43
-
- > assert f() == g()
- E assert 42 == 43
- E + where 42 = <function TestFailing.test_simple.<locals>.f at 0xdeadbeef>()
- E + and 43 = <function TestFailing.test_simple.<locals>.g at 0xdeadbeef>()
-
- failure_demo.py:29: AssertionError
- _______ TestFailing.test_simple_multiline ________
-
- self = <failure_demo.TestFailing object at 0xdeadbeef>
-
- def test_simple_multiline(self):
- otherfunc_multi(
- 42,
- > 6*9)
-
- failure_demo.py:34:
- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
- a = 42, b = 54
-
- def otherfunc_multi(a,b):
- > assert (a ==
- b)
- E assert 42 == 54
-
- failure_demo.py:12: AssertionError
- _______ TestFailing.test_not ________
-
- self = <failure_demo.TestFailing object at 0xdeadbeef>
-
- def test_not(self):
- def f():
- return 42
- > assert not f()
- E assert not 42
- E + where 42 = <function TestFailing.test_not.<locals>.f at 0xdeadbeef>()
-
- failure_demo.py:39: AssertionError
- _______ TestSpecialisedExplanations.test_eq_text ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_eq_text(self):
- > assert 'spam' == 'eggs'
- E assert 'spam' == 'eggs'
- E - spam
- E + eggs
-
- failure_demo.py:43: AssertionError
- _______ TestSpecialisedExplanations.test_eq_similar_text ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_eq_similar_text(self):
- > assert 'foo 1 bar' == 'foo 2 bar'
- E assert 'foo 1 bar' == 'foo 2 bar'
- E - foo 1 bar
- E ? ^
- E + foo 2 bar
- E ? ^
-
- failure_demo.py:46: AssertionError
- _______ TestSpecialisedExplanations.test_eq_multiline_text ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_eq_multiline_text(self):
- > assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
- E assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
- E foo
- E - spam
- E + eggs
- E bar
-
- failure_demo.py:49: AssertionError
- _______ TestSpecialisedExplanations.test_eq_long_text ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_eq_long_text(self):
- a = '1'*100 + 'a' + '2'*100
- b = '1'*100 + 'b' + '2'*100
- > assert a == b
- E assert '111111111111...2222222222222' == '1111111111111...2222222222222'
- E Skipping 90 identical leading characters in diff, use -v to show
- E Skipping 91 identical trailing characters in diff, use -v to show
- E - 1111111111a222222222
- E ? ^
- E + 1111111111b222222222
- E ? ^
-
- failure_demo.py:54: AssertionError
- _______ TestSpecialisedExplanations.test_eq_long_text_multiline ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_eq_long_text_multiline(self):
- a = '1\n'*100 + 'a' + '2\n'*100
- b = '1\n'*100 + 'b' + '2\n'*100
- > assert a == b
- E assert '1\n1\n1\n1\n...n2\n2\n2\n2\n' == '1\n1\n1\n1\n1...n2\n2\n2\n2\n'
- E Skipping 190 identical leading characters in diff, use -v to show
- E Skipping 191 identical trailing characters in diff, use -v to show
- E 1
- E 1
- E 1
- E 1
- E 1
- E - a2
- E + b2
- E 2
- E 2
- E 2
- E 2
-
- failure_demo.py:59: AssertionError
- _______ TestSpecialisedExplanations.test_eq_list ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_eq_list(self):
- > assert [0, 1, 2] == [0, 1, 3]
- E assert [0, 1, 2] == [0, 1, 3]
- E At index 2 diff: 2 != 3
- E Use -v to get the full diff
-
- failure_demo.py:62: AssertionError
- _______ TestSpecialisedExplanations.test_eq_list_long ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_eq_list_long(self):
- a = [0]*100 + [1] + [3]*100
- b = [0]*100 + [2] + [3]*100
- > assert a == b
- E assert [0, 0, 0, 0, 0, 0, ...] == [0, 0, 0, 0, 0, 0, ...]
- E At index 100 diff: 1 != 2
- E Use -v to get the full diff
-
- failure_demo.py:67: AssertionError
- _______ TestSpecialisedExplanations.test_eq_dict ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_eq_dict(self):
- > assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
- E assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
- E Omitting 1 identical items, use -v to show
- E Differing items:
- E {'b': 1} != {'b': 2}
- E Left contains more items:
- E {'c': 0}
- E Right contains more items:
- E {'d': 0}
- E Use -v to get the full diff
-
- failure_demo.py:70: AssertionError
- _______ TestSpecialisedExplanations.test_eq_set ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_eq_set(self):
- > assert set([0, 10, 11, 12]) == set([0, 20, 21])
- E assert set([0, 10, 11, 12]) == set([0, 20, 21])
- E Extra items in the left set:
- E 10
- E 11
- E 12
- E Extra items in the right set:
- E 20
- E 21
- E Use -v to get the full diff
-
- failure_demo.py:73: AssertionError
- _______ TestSpecialisedExplanations.test_eq_longer_list ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_eq_longer_list(self):
- > assert [1,2] == [1,2,3]
- E assert [1, 2] == [1, 2, 3]
- E Right contains more items, first extra item: 3
- E Use -v to get the full diff
-
- failure_demo.py:76: AssertionError
- _______ TestSpecialisedExplanations.test_in_list ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_in_list(self):
- > assert 1 in [0, 2, 3, 4, 5]
- E assert 1 in [0, 2, 3, 4, 5]
-
- failure_demo.py:79: AssertionError
- _______ TestSpecialisedExplanations.test_not_in_text_multiline ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_not_in_text_multiline(self):
- text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail'
- > assert 'foo' not in text
- E assert 'foo' not in 'some multiline\ntext\nw...ncludes foo\nand a\ntail'
- E 'foo' is contained here:
- E some multiline
- E text
- E which
- E includes foo
- E ? +++
- E and a
- E tail
-
- failure_demo.py:83: AssertionError
- _______ TestSpecialisedExplanations.test_not_in_text_single ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_not_in_text_single(self):
- text = 'single foo line'
- > assert 'foo' not in text
- E assert 'foo' not in 'single foo line'
- E 'foo' is contained here:
- E single foo line
- E ? +++
-
- failure_demo.py:87: AssertionError
- _______ TestSpecialisedExplanations.test_not_in_text_single_long ________
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_not_in_text_single_long(self):
- text = 'head ' * 50 + 'foo ' + 'tail ' * 20
- > assert 'foo' not in text
- E assert 'foo' not in 'head head head head hea...ail tail tail tail tail '
- E 'foo' is contained here:
- E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
- E ? +++
-
- failure_demo.py:91: AssertionError
- ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
-
- self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
-
- def test_not_in_text_single_long_term(self):
- text = 'head ' * 50 + 'f'*70 + 'tail ' * 20
- > assert 'f'*70 not in text
- E assert 'fffffffffff...ffffffffffff' not in 'head head he...l tail tail '
- E 'ffffffffffffffffff...fffffffffffffffffff' is contained here:
- E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
- E ? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
- failure_demo.py:95: AssertionError
- _______ test_attribute ________
-
- def test_attribute():
- class Foo(object):
- b = 1
- i = Foo()
- > assert i.b == 2
- E assert 1 == 2
- E + where 1 = <failure_demo.test_attribute.<locals>.Foo object at 0xdeadbeef>.b
-
- failure_demo.py:102: AssertionError
- _______ test_attribute_instance ________
-
- def test_attribute_instance():
- class Foo(object):
- b = 1
- > assert Foo().b == 2
- E assert 1 == 2
- E + where 1 = <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef>.b
- E + where <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef> = <class 'failure_demo.test_attribute_instance.<locals>.Foo'>()
-
- failure_demo.py:108: AssertionError
- _______ test_attribute_failure ________
-
- def test_attribute_failure():
- class Foo(object):
- def _get_b(self):
- raise Exception('Failed to get attrib')
- b = property(_get_b)
- i = Foo()
- > assert i.b == 2
-
- failure_demo.py:117:
- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
- self = <failure_demo.test_attribute_failure.<locals>.Foo object at 0xdeadbeef>
-
- def _get_b(self):
- > raise Exception('Failed to get attrib')
- E Exception: Failed to get attrib
-
- failure_demo.py:114: Exception
- _______ test_attribute_multiple ________
-
- def test_attribute_multiple():
- class Foo(object):
- b = 1
- class Bar(object):
- b = 2
- > assert Foo().b == Bar().b
- E assert 1 == 2
- E + where 1 = <failure_demo.test_attribute_multiple.<locals>.Foo object at 0xdeadbeef>.b
- E + where <failure_demo.test_attribute_multiple.<locals>.Foo object at 0xdeadbeef> = <class 'failure_demo.test_attribute_multiple.<locals>.Foo'>()
- E + and 2 = <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef>.b
- E + where <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef> = <class 'failure_demo.test_attribute_multiple.<locals>.Bar'>()
-
- failure_demo.py:125: AssertionError
- _______ TestRaises.test_raises ________
-
- self = <failure_demo.TestRaises object at 0xdeadbeef>
-
- def test_raises(self):
- s = 'qwe'
- > raises(TypeError, "int(s)")
-
- failure_demo.py:134:
- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
- > int(s)
- E ValueError: invalid literal for int() with base 10: 'qwe'
-
- <0-codegen $PYTHON_PREFIX/lib/python3.4/site-packages/_pytest/python.py:1302>:1: ValueError
- _______ TestRaises.test_raises_doesnt ________
-
- self = <failure_demo.TestRaises object at 0xdeadbeef>
-
- def test_raises_doesnt(self):
- > raises(IOError, "int('3')")
- E Failed: DID NOT RAISE <class 'OSError'>
-
- failure_demo.py:137: Failed
- _______ TestRaises.test_raise ________
-
- self = <failure_demo.TestRaises object at 0xdeadbeef>
-
- def test_raise(self):
- > raise ValueError("demo error")
- E ValueError: demo error
-
- failure_demo.py:140: ValueError
- _______ TestRaises.test_tupleerror ________
-
- self = <failure_demo.TestRaises object at 0xdeadbeef>
-
- def test_tupleerror(self):
- > a,b = [1]
- E ValueError: need more than 1 value to unpack
-
- failure_demo.py:143: ValueError
- ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______
-
- self = <failure_demo.TestRaises object at 0xdeadbeef>
-
- def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
- l = [1,2,3]
- print ("l is %r" % l)
- > a,b = l.pop()
- E TypeError: 'int' object is not iterable
-
- failure_demo.py:148: TypeError
- --------------------------- Captured stdout call ---------------------------
- l is [1, 2, 3]
- _______ TestRaises.test_some_error ________
-
- self = <failure_demo.TestRaises object at 0xdeadbeef>
-
- def test_some_error(self):
- > if namenotexi:
- E NameError: name 'namenotexi' is not defined
-
- failure_demo.py:151: NameError
- _______ test_dynamic_compile_shows_nicely ________
-
- def test_dynamic_compile_shows_nicely():
- src = 'def foo():\n assert 1 == 0\n'
- name = 'abc-123'
- module = py.std.imp.new_module(name)
- code = _pytest._code.compile(src, name, 'exec')
- py.builtin.exec_(code, module.__dict__)
- py.std.sys.modules[name] = module
- > module.foo()
-
- failure_demo.py:166:
- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
- def foo():
- > assert 1 == 0
- E assert 1 == 0
-
- <2-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:163>:2: AssertionError
- _______ TestMoreErrors.test_complex_error ________
-
- self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
-
- def test_complex_error(self):
- def f():
- return 44
- def g():
- return 43
- > somefunc(f(), g())
-
- failure_demo.py:176:
- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
- failure_demo.py:9: in somefunc
- otherfunc(x,y)
- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
- a = 44, b = 43
-
- def otherfunc(a,b):
- > assert a==b
- E assert 44 == 43
-
- failure_demo.py:6: AssertionError
- _______ TestMoreErrors.test_z1_unpack_error ________
-
- self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
-
- def test_z1_unpack_error(self):
- l = []
- > a,b = l
- E ValueError: need more than 0 values to unpack
-
- failure_demo.py:180: ValueError
- _______ TestMoreErrors.test_z2_type_error ________
-
- self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
-
- def test_z2_type_error(self):
- l = 3
- > a,b = l
- E TypeError: 'int' object is not iterable
-
- failure_demo.py:184: TypeError
- _______ TestMoreErrors.test_startswith ________
-
- self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
-
- def test_startswith(self):
- s = "123"
- g = "456"
- > assert s.startswith(g)
- E assert <built-in method startswith of str object at 0xdeadbeef>('456')
- E + where <built-in method startswith of str object at 0xdeadbeef> = '123'.startswith
-
- failure_demo.py:189: AssertionError
- _______ TestMoreErrors.test_startswith_nested ________
-
- self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
-
- def test_startswith_nested(self):
- def f():
- return "123"
- def g():
- return "456"
- > assert f().startswith(g())
- E assert <built-in method startswith of str object at 0xdeadbeef>('456')
- E + where <built-in method startswith of str object at 0xdeadbeef> = '123'.startswith
- E + where '123' = <function TestMoreErrors.test_startswith_nested.<locals>.f at 0xdeadbeef>()
- E + and '456' = <function TestMoreErrors.test_startswith_nested.<locals>.g at 0xdeadbeef>()
-
- failure_demo.py:196: AssertionError
- _______ TestMoreErrors.test_global_func ________
-
- self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
-
- def test_global_func(self):
- > assert isinstance(globf(42), float)
- E assert isinstance(43, float)
- E + where 43 = globf(42)
-
- failure_demo.py:199: AssertionError
- _______ TestMoreErrors.test_instance ________
-
- self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
-
- def test_instance(self):
- self.x = 6*7
- > assert self.x != 42
- E assert 42 != 42
- E + where 42 = <failure_demo.TestMoreErrors object at 0xdeadbeef>.x
-
- failure_demo.py:203: AssertionError
- _______ TestMoreErrors.test_compare ________
-
- self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
-
- def test_compare(self):
- > assert globf(10) < 5
- E assert 11 < 5
- E + where 11 = globf(10)
-
- failure_demo.py:206: AssertionError
- _______ TestMoreErrors.test_try_finally ________
-
- self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
-
- def test_try_finally(self):
- x = 1
- try:
- > assert x == 0
- E assert 1 == 0
-
- failure_demo.py:211: AssertionError
- _______ TestCustomAssertMsg.test_single_line ________
-
- self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
-
- def test_single_line(self):
- class A:
- a = 1
- b = 2
- > assert A.a == b, "A.a appears not to be b"
- E AssertionError: A.a appears not to be b
- E assert 1 == 2
- E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_single_line.<locals>.A'>.a
-
- failure_demo.py:222: AssertionError
- _______ TestCustomAssertMsg.test_multiline ________
-
- self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
-
- def test_multiline(self):
- class A:
- a = 1
- b = 2
- > assert A.a == b, "A.a appears not to be b\n" \
- "or does not appear to be b\none of those"
- E AssertionError: A.a appears not to be b
- E or does not appear to be b
- E one of those
- E assert 1 == 2
- E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_multiline.<locals>.A'>.a
-
- failure_demo.py:228: AssertionError
- _______ TestCustomAssertMsg.test_custom_repr ________
-
- self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
-
- def test_custom_repr(self):
- class JSON:
- a = 1
- def __repr__(self):
- return "This is JSON\n{\n 'foo': 'bar'\n}"
- a = JSON()
- b = 2
- > assert a.a == b, a
- E AssertionError: This is JSON
- E {
- E 'foo': 'bar'
- E }
- E assert 1 == 2
- E + where 1 = This is JSON\n{\n 'foo': 'bar'\n}.a
-
- failure_demo.py:238: AssertionError
- ======= 42 failed in 0.12 seconds ========
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/simple.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/simple.rst
deleted file mode 100644
index be12d2afe41..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/simple.rst
+++ /dev/null
@@ -1,751 +0,0 @@
-
-.. highlightlang:: python
-
-Basic patterns and examples
-==========================================================
-
-Pass different values to a test function, depending on command line options
-----------------------------------------------------------------------------
-
-.. regendoc:wipe
-
-Suppose we want to write a test that depends on a command line option.
-Here is a basic pattern to achieve this::
-
- # content of test_sample.py
- def test_answer(cmdopt):
- if cmdopt == "type1":
- print ("first")
- elif cmdopt == "type2":
- print ("second")
- assert 0 # to see what was printed
-
-
-For this to work we need to add a command line option and
-provide the ``cmdopt`` through a :ref:`fixture function <fixture function>`::
-
- # content of conftest.py
- import pytest
-
- def pytest_addoption(parser):
- parser.addoption("--cmdopt", action="store", default="type1",
- help="my option: type1 or type2")
-
- @pytest.fixture
- def cmdopt(request):
- return request.config.getoption("--cmdopt")
-
-Let's run this without supplying our new option::
-
- $ py.test -q test_sample.py
- F
- ======= FAILURES ========
- _______ test_answer ________
-
- cmdopt = 'type1'
-
- def test_answer(cmdopt):
- if cmdopt == "type1":
- print ("first")
- elif cmdopt == "type2":
- print ("second")
- > assert 0 # to see what was printed
- E assert 0
-
- test_sample.py:6: AssertionError
- --------------------------- Captured stdout call ---------------------------
- first
- 1 failed in 0.12 seconds
-
-And now with supplying a command line option::
-
- $ py.test -q --cmdopt=type2
- F
- ======= FAILURES ========
- _______ test_answer ________
-
- cmdopt = 'type2'
-
- def test_answer(cmdopt):
- if cmdopt == "type1":
- print ("first")
- elif cmdopt == "type2":
- print ("second")
- > assert 0 # to see what was printed
- E assert 0
-
- test_sample.py:6: AssertionError
- --------------------------- Captured stdout call ---------------------------
- second
- 1 failed in 0.12 seconds
-
-You can see that the command line option arrived in our test. This
-completes the basic pattern. However, one often rather wants to process
-command line options outside of the test and rather pass in different or
-more complex objects.
-
-Dynamically adding command line options
---------------------------------------------------------------
-
-.. regendoc:wipe
-
-Through :confval:`addopts` you can statically add command line
-options for your project. You can also dynamically modify
-the command line arguments before they get processed::
-
- # content of conftest.py
- import sys
- def pytest_cmdline_preparse(args):
- if 'xdist' in sys.modules: # pytest-xdist plugin
- import multiprocessing
- num = max(multiprocessing.cpu_count() / 2, 1)
- args[:] = ["-n", str(num)] + args
-
-If you have the :ref:`xdist plugin <xdist>` installed
-you will now always perform test runs using a number
-of subprocesses close to your CPU. Running in an empty
-directory with the above conftest.py::
-
- $ py.test
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 0 items
-
- ======= no tests ran in 0.12 seconds ========
-
-.. _`excontrolskip`:
-
-Control skipping of tests according to command line option
---------------------------------------------------------------
-
-.. regendoc:wipe
-
-Here is a ``conftest.py`` file adding a ``--runslow`` command
-line option to control skipping of ``slow`` marked tests::
-
- # content of conftest.py
-
- import pytest
- def pytest_addoption(parser):
- parser.addoption("--runslow", action="store_true",
- help="run slow tests")
-
-We can now write a test module like this::
-
- # content of test_module.py
-
- import pytest
-
-
- slow = pytest.mark.skipif(
- not pytest.config.getoption("--runslow"),
- reason="need --runslow option to run"
- )
-
-
- def test_func_fast():
- pass
-
-
- @slow
- def test_func_slow():
- pass
-
-and when running it will see a skipped "slow" test::
-
- $ py.test -rs # "-rs" means report details on the little 's'
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 2 items
-
- test_module.py .s
- ======= short test summary info ========
- SKIP [1] test_module.py:14: need --runslow option to run
-
- ======= 1 passed, 1 skipped in 0.12 seconds ========
-
-Or run it including the ``slow`` marked test::
-
- $ py.test --runslow
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 2 items
-
- test_module.py ..
-
- ======= 2 passed in 0.12 seconds ========
-
-Writing well integrated assertion helpers
---------------------------------------------------
-
-.. regendoc:wipe
-
-If you have a test helper function called from a test you can
-use the ``pytest.fail`` marker to fail a test with a certain message.
-The test support function will not show up in the traceback if you
-set the ``__tracebackhide__`` option somewhere in the helper function.
-Example::
-
- # content of test_checkconfig.py
- import pytest
- def checkconfig(x):
- __tracebackhide__ = True
- if not hasattr(x, "config"):
- pytest.fail("not configured: %s" %(x,))
-
- def test_something():
- checkconfig(42)
-
-The ``__tracebackhide__`` setting influences ``pytest`` showing
-of tracebacks: the ``checkconfig`` function will not be shown
-unless the ``--fulltrace`` command line option is specified.
-Let's run our little function::
-
- $ py.test -q test_checkconfig.py
- F
- ======= FAILURES ========
- _______ test_something ________
-
- def test_something():
- > checkconfig(42)
- E Failed: not configured: 42
-
- test_checkconfig.py:8: Failed
- 1 failed in 0.12 seconds
-
-Detect if running from within a pytest run
---------------------------------------------------------------
-
-.. regendoc:wipe
-
-Usually it is a bad idea to make application code
-behave differently if called from a test. But if you
-absolutely must find out if your application code is
-running from a test you can do something like this::
-
- # content of conftest.py
-
- def pytest_configure(config):
- import sys
- sys._called_from_test = True
-
- def pytest_unconfigure(config):
- del sys._called_from_test
-
-and then check for the ``sys._called_from_test`` flag::
-
- if hasattr(sys, '_called_from_test'):
- # called from within a test run
- else:
- # called "normally"
-
-accordingly in your application. It's also a good idea
-to use your own application module rather than ``sys``
-for handling flag.
-
-Adding info to test report header
---------------------------------------------------------------
-
-.. regendoc:wipe
-
-It's easy to present extra information in a ``pytest`` run::
-
- # content of conftest.py
-
- def pytest_report_header(config):
- return "project deps: mylib-1.1"
-
-which will add the string to the test header accordingly::
-
- $ py.test
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- project deps: mylib-1.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 0 items
-
- ======= no tests ran in 0.12 seconds ========
-
-.. regendoc:wipe
-
-You can also return a list of strings which will be considered as several
-lines of information. You can of course also make the amount of reporting
-information on e.g. the value of ``config.option.verbose`` so that
-you present more information appropriately::
-
- # content of conftest.py
-
- def pytest_report_header(config):
- if config.option.verbose > 0:
- return ["info1: did you know that ...", "did you?"]
-
-which will add info only when run with "--v"::
-
- $ py.test -v
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- info1: did you know that ...
- did you?
- rootdir: $REGENDOC_TMPDIR, inifile:
- collecting ... collected 0 items
-
- ======= no tests ran in 0.12 seconds ========
-
-and nothing when run plainly::
-
- $ py.test
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 0 items
-
- ======= no tests ran in 0.12 seconds ========
-
-profiling test duration
---------------------------
-
-.. regendoc:wipe
-
-.. versionadded: 2.2
-
-If you have a slow running large test suite you might want to find
-out which tests are the slowest. Let's make an artificial test suite::
-
- # content of test_some_are_slow.py
-
- import time
-
- def test_funcfast():
- pass
-
- def test_funcslow1():
- time.sleep(0.1)
-
- def test_funcslow2():
- time.sleep(0.2)
-
-Now we can profile which test functions execute the slowest::
-
- $ py.test --durations=3
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 3 items
-
- test_some_are_slow.py ...
-
- ======= slowest 3 test durations ========
- 0.20s call test_some_are_slow.py::test_funcslow2
- 0.10s call test_some_are_slow.py::test_funcslow1
- 0.00s setup test_some_are_slow.py::test_funcfast
- ======= 3 passed in 0.12 seconds ========
-
-incremental testing - test steps
----------------------------------------------------
-
-.. regendoc:wipe
-
-Sometimes you may have a testing situation which consists of a series
-of test steps. If one step fails it makes no sense to execute further
-steps as they are all expected to fail anyway and their tracebacks
-add no insight. Here is a simple ``conftest.py`` file which introduces
-an ``incremental`` marker which is to be used on classes::
-
- # content of conftest.py
-
- import pytest
-
- def pytest_runtest_makereport(item, call):
- if "incremental" in item.keywords:
- if call.excinfo is not None:
- parent = item.parent
- parent._previousfailed = item
-
- def pytest_runtest_setup(item):
- if "incremental" in item.keywords:
- previousfailed = getattr(item.parent, "_previousfailed", None)
- if previousfailed is not None:
- pytest.xfail("previous test failed (%s)" %previousfailed.name)
-
-These two hook implementations work together to abort incremental-marked
-tests in a class. Here is a test module example::
-
- # content of test_step.py
-
- import pytest
-
- @pytest.mark.incremental
- class TestUserHandling:
- def test_login(self):
- pass
- def test_modification(self):
- assert 0
- def test_deletion(self):
- pass
-
- def test_normal():
- pass
-
-If we run this::
-
- $ py.test -rx
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 4 items
-
- test_step.py .Fx.
- ======= short test summary info ========
- XFAIL test_step.py::TestUserHandling::()::test_deletion
- reason: previous test failed (test_modification)
-
- ======= FAILURES ========
- _______ TestUserHandling.test_modification ________
-
- self = <test_step.TestUserHandling object at 0xdeadbeef>
-
- def test_modification(self):
- > assert 0
- E assert 0
-
- test_step.py:9: AssertionError
- ======= 1 failed, 2 passed, 1 xfailed in 0.12 seconds ========
-
-We'll see that ``test_deletion`` was not executed because ``test_modification``
-failed. It is reported as an "expected failure".
-
-
-Package/Directory-level fixtures (setups)
--------------------------------------------------------
-
-If you have nested test directories, you can have per-directory fixture scopes
-by placing fixture functions in a ``conftest.py`` file in that directory
-You can use all types of fixtures including :ref:`autouse fixtures
-<autouse fixtures>` which are the equivalent of xUnit's setup/teardown
-concept. It's however recommended to have explicit fixture references in your
-tests or test classes rather than relying on implicitly executing
-setup/teardown functions, especially if they are far away from the actual tests.
-
-Here is a an example for making a ``db`` fixture available in a directory::
-
- # content of a/conftest.py
- import pytest
-
- class DB:
- pass
-
- @pytest.fixture(scope="session")
- def db():
- return DB()
-
-and then a test module in that directory::
-
- # content of a/test_db.py
- def test_a1(db):
- assert 0, db # to show value
-
-another test module::
-
- # content of a/test_db2.py
- def test_a2(db):
- assert 0, db # to show value
-
-and then a module in a sister directory which will not see
-the ``db`` fixture::
-
- # content of b/test_error.py
- def test_root(db): # no db here, will error out
- pass
-
-We can run this::
-
- $ py.test
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 7 items
-
- test_step.py .Fx.
- a/test_db.py F
- a/test_db2.py F
- b/test_error.py E
-
- ======= ERRORS ========
- _______ ERROR at setup of test_root ________
- file $REGENDOC_TMPDIR/b/test_error.py, line 1
- def test_root(db): # no db here, will error out
- fixture 'db' not found
- available fixtures: record_xml_property, recwarn, cache, capsys, pytestconfig, tmpdir_factory, capfd, monkeypatch, tmpdir
- use 'py.test --fixtures [testpath]' for help on them.
-
- $REGENDOC_TMPDIR/b/test_error.py:1
- ======= FAILURES ========
- _______ TestUserHandling.test_modification ________
-
- self = <test_step.TestUserHandling object at 0xdeadbeef>
-
- def test_modification(self):
- > assert 0
- E assert 0
-
- test_step.py:9: AssertionError
- _______ test_a1 ________
-
- db = <conftest.DB object at 0xdeadbeef>
-
- def test_a1(db):
- > assert 0, db # to show value
- E AssertionError: <conftest.DB object at 0xdeadbeef>
- E assert 0
-
- a/test_db.py:2: AssertionError
- _______ test_a2 ________
-
- db = <conftest.DB object at 0xdeadbeef>
-
- def test_a2(db):
- > assert 0, db # to show value
- E AssertionError: <conftest.DB object at 0xdeadbeef>
- E assert 0
-
- a/test_db2.py:2: AssertionError
- ======= 3 failed, 2 passed, 1 xfailed, 1 error in 0.12 seconds ========
-
-The two test modules in the ``a`` directory see the same ``db`` fixture instance
-while the one test in the sister-directory ``b`` doesn't see it. We could of course
-also define a ``db`` fixture in that sister directory's ``conftest.py`` file.
-Note that each fixture is only instantiated if there is a test actually needing
-it (unless you use "autouse" fixture which are always executed ahead of the first test
-executing).
-
-
-post-process test reports / failures
----------------------------------------
-
-If you want to postprocess test reports and need access to the executing
-environment you can implement a hook that gets called when the test
-"report" object is about to be created. Here we write out all failing
-test calls and also access a fixture (if it was used by the test) in
-case you want to query/look at it during your post processing. In our
-case we just write some informations out to a ``failures`` file::
-
- # content of conftest.py
-
- import pytest
- import os.path
-
- @pytest.hookimpl(tryfirst=True, hookwrapper=True)
- def pytest_runtest_makereport(item, call):
- # execute all other hooks to obtain the report object
- outcome = yield
- rep = outcome.get_result()
-
- # we only look at actual failing test calls, not setup/teardown
- if rep.when == "call" and rep.failed:
- mode = "a" if os.path.exists("failures") else "w"
- with open("failures", mode) as f:
- # let's also access a fixture for the fun of it
- if "tmpdir" in item.fixturenames:
- extra = " (%s)" % item.funcargs["tmpdir"]
- else:
- extra = ""
-
- f.write(rep.nodeid + extra + "\n")
-
-
-if you then have failing tests::
-
- # content of test_module.py
- def test_fail1(tmpdir):
- assert 0
- def test_fail2():
- assert 0
-
-and run them::
-
- $ py.test test_module.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 2 items
-
- test_module.py FF
-
- ======= FAILURES ========
- _______ test_fail1 ________
-
- tmpdir = local('PYTEST_TMPDIR/test_fail10')
-
- def test_fail1(tmpdir):
- > assert 0
- E assert 0
-
- test_module.py:2: AssertionError
- _______ test_fail2 ________
-
- def test_fail2():
- > assert 0
- E assert 0
-
- test_module.py:4: AssertionError
- ======= 2 failed in 0.12 seconds ========
-
-you will have a "failures" file which contains the failing test ids::
-
- $ cat failures
- test_module.py::test_fail1 (PYTEST_TMPDIR/test_fail10)
- test_module.py::test_fail2
-
-Making test result information available in fixtures
------------------------------------------------------------
-
-.. regendoc:wipe
-
-If you want to make test result reports available in fixture finalizers
-here is a little example implemented via a local plugin::
-
- # content of conftest.py
-
- import pytest
-
- @pytest.hookimpl(tryfirst=True, hookwrapper=True)
- def pytest_runtest_makereport(item, call):
- # execute all other hooks to obtain the report object
- outcome = yield
- rep = outcome.get_result()
-
- # set an report attribute for each phase of a call, which can
- # be "setup", "call", "teardown"
-
- setattr(item, "rep_" + rep.when, rep)
-
-
- @pytest.fixture
- def something(request):
- def fin():
- # request.node is an "item" because we use the default
- # "function" scope
- if request.node.rep_setup.failed:
- print ("setting up a test failed!", request.node.nodeid)
- elif request.node.rep_setup.passed:
- if request.node.rep_call.failed:
- print ("executing test failed", request.node.nodeid)
- request.addfinalizer(fin)
-
-
-if you then have failing tests::
-
- # content of test_module.py
-
- import pytest
-
- @pytest.fixture
- def other():
- assert 0
-
- def test_setup_fails(something, other):
- pass
-
- def test_call_fails(something):
- assert 0
-
- def test_fail2():
- assert 0
-
-and run it::
-
- $ py.test -s test_module.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 3 items
-
- test_module.py Esetting up a test failed! test_module.py::test_setup_fails
- Fexecuting test failed test_module.py::test_call_fails
- F
-
- ======= ERRORS ========
- _______ ERROR at setup of test_setup_fails ________
-
- @pytest.fixture
- def other():
- > assert 0
- E assert 0
-
- test_module.py:6: AssertionError
- ======= FAILURES ========
- _______ test_call_fails ________
-
- something = None
-
- def test_call_fails(something):
- > assert 0
- E assert 0
-
- test_module.py:12: AssertionError
- _______ test_fail2 ________
-
- def test_fail2():
- > assert 0
- E assert 0
-
- test_module.py:15: AssertionError
- ======= 2 failed, 1 error in 0.12 seconds ========
-
-You'll see that the fixture finalizers could use the precise reporting
-information.
-
-Integrating pytest runner and cx_freeze
------------------------------------------------------------
-
-If you freeze your application using a tool like
-`cx_freeze <http://cx-freeze.readthedocs.org>`_ in order to distribute it
-to your end-users, it is a good idea to also package your test runner and run
-your tests using the frozen application.
-
-This way packaging errors such as dependencies not being
-included into the executable can be detected early while also allowing you to
-send test files to users so they can run them in their machines, which can be
-invaluable to obtain more information about a hard to reproduce bug.
-
-Unfortunately ``cx_freeze`` can't discover them
-automatically because of ``pytest``'s use of dynamic module loading, so you
-must declare them explicitly by using ``pytest.freeze_includes()``::
-
- # contents of setup.py
- from cx_Freeze import setup, Executable
- import pytest
-
- setup(
- name="app_main",
- executables=[Executable("app_main.py")],
- options={"build_exe":
- {
- 'includes': pytest.freeze_includes()}
- },
- # ... other options
- )
-
-If you don't want to ship a different executable just in order to run your tests,
-you can make your program check for a certain flag and pass control
-over to ``pytest`` instead. For example::
-
- # contents of app_main.py
- import sys
-
- if len(sys.argv) > 1 and sys.argv[1] == '--pytest':
- import pytest
- sys.exit(pytest.main(sys.argv[2:]))
- else:
- # normal application execution: at this point argv can be parsed
- # by your argument-parsing library of choice as usual
- ...
-
-This makes it convenient to execute your tests from within your frozen
-application, using standard ``py.test`` command-line options::
-
- ./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/special.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/special.rst
deleted file mode 100644
index 58e66d44e34..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/special.rst
+++ /dev/null
@@ -1,72 +0,0 @@
-A session-fixture which can look at all collected tests
-----------------------------------------------------------------
-
-A session-scoped fixture effectively has access to all
-collected test items. Here is an example of a fixture
-function which walks all collected tests and looks
-if their test class defines a ``callme`` method and
-calls it::
-
- # content of conftest.py
-
- import pytest
-
- @pytest.fixture(scope="session", autouse=True)
- def callattr_ahead_of_alltests(request):
- print ("callattr_ahead_of_alltests called")
- seen = set([None])
- session = request.node
- for item in session.items:
- cls = item.getparent(pytest.Class)
- if cls not in seen:
- if hasattr(cls.obj, "callme"):
- cls.obj.callme()
- seen.add(cls)
-
-test classes may now define a ``callme`` method which
-will be called ahead of running any tests::
-
- # content of test_module.py
-
- class TestHello:
- @classmethod
- def callme(cls):
- print ("callme called!")
-
- def test_method1(self):
- print ("test_method1 called")
-
- def test_method2(self):
- print ("test_method1 called")
-
- class TestOther:
- @classmethod
- def callme(cls):
- print ("callme other called")
- def test_other(self):
- print ("test other")
-
- # works with unittest as well ...
- import unittest
-
- class SomeTest(unittest.TestCase):
- @classmethod
- def callme(self):
- print ("SomeTest callme called")
-
- def test_unit1(self):
- print ("test_unit1 method called")
-
-If you run this without output capturing::
-
- $ py.test -q -s test_module.py
- callattr_ahead_of_alltests called
- callme called!
- callme other called
- SomeTest callme called
- test_method1 called
- .test_method1 called
- .test other
- .test_unit1 method called
- .
- 4 passed in 0.12 seconds
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/faq.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/faq.rst
deleted file mode 100644
index fd7ca35e943..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/faq.rst
+++ /dev/null
@@ -1,165 +0,0 @@
-Some Issues and Questions
-==================================
-
-.. note::
-
- This FAQ is here only mostly for historic reasons. Checkout
- `pytest Q&A at Stackoverflow <http://stackoverflow.com/search?q=pytest>`_
- for many questions and answers related to pytest and/or use
- :ref:`contact channels` to get help.
-
-On naming, nosetests, licensing and magic
-------------------------------------------------
-
-How does pytest relate to nose and unittest?
-+++++++++++++++++++++++++++++++++++++++++++++++++
-
-``pytest`` and nose_ share basic philosophy when it comes
-to running and writing Python tests. In fact, you can run many tests
-written for nose with ``pytest``. nose_ was originally created
-as a clone of ``pytest`` when ``pytest`` was in the ``0.8`` release
-cycle. Note that starting with pytest-2.0 support for running unittest
-test suites is majorly improved.
-
-how does pytest relate to twisted's trial?
-++++++++++++++++++++++++++++++++++++++++++++++
-
-Since some time ``pytest`` has builtin support for supporting tests
-written using trial. It does not itself start a reactor, however,
-and does not handle Deferreds returned from a test in pytest style.
-If you are using trial's unittest.TestCase chances are that you can
-just run your tests even if you return Deferreds. In addition,
-there also is a dedicated `pytest-twisted
-<http://pypi.python.org/pypi/pytest-twisted>`_ plugin which allows you to
-return deferreds from pytest-style tests, allowing the use of
-:ref:`fixtures` and other features.
-
-how does pytest work with Django?
-++++++++++++++++++++++++++++++++++++++++++++++
-
-In 2012, some work is going into the `pytest-django plugin <http://pypi.python.org/pypi/pytest-django>`_. It substitutes the usage of Django's
-``manage.py test`` and allows the use of all pytest features_ most of which
-are not available from Django directly.
-
-.. _features: features.html
-
-
-What's this "magic" with pytest? (historic notes)
-++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-Around 2007 (version ``0.8``) some people thought that ``pytest``
-was using too much "magic". It had been part of the `pylib`_ which
-contains a lot of unrelated python library code. Around 2010 there
-was a major cleanup refactoring, which removed unused or deprecated code
-and resulted in the new ``pytest`` PyPI package which strictly contains
-only test-related code. This release also brought a complete pluginification
-such that the core is around 300 lines of code and everything else is
-implemented in plugins. Thus ``pytest`` today is a small, universally runnable
-and customizable testing framework for Python. Note, however, that
-``pytest`` uses metaprogramming techniques and reading its source is
-thus likely not something for Python beginners.
-
-A second "magic" issue was the assert statement debugging feature.
-Nowadays, ``pytest`` explicitly rewrites assert statements in test modules
-in order to provide more useful :ref:`assert feedback <assertfeedback>`.
-This completely avoids previous issues of confusing assertion-reporting.
-It also means, that you can use Python's ``-O`` optimization without losing
-assertions in test modules.
-
-``pytest`` contains a second, mostly obsolete, assert debugging technique
-invoked via ``--assert=reinterpret``: When an ``assert`` statement fails, ``pytest`` re-interprets
-the expression part to show intermediate values. This technique suffers
-from a caveat that the rewriting does not: If your expression has side
-effects (better to avoid them anyway!) the intermediate values may not
-be the same, confusing the reinterpreter and obfuscating the initial
-error (this is also explained at the command line if it happens).
-
-You can also turn off all assertion interaction using the
-``--assert=plain`` option.
-
-.. _`py namespaces`: index.html
-.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/trunk/py/__init__.py
-
-
-Why a ``py.test`` instead of a ``pytest`` command?
-++++++++++++++++++++++++++++++++++++++++++++++++++
-
-Some of the reasons are historic, others are practical. ``pytest``
-used to be part of the ``py`` package which provided several developer
-utilities, all starting with ``py.<TAB>``, thus providing nice
-TAB-completion. If
-you install ``pip install pycmd`` you get these tools from a separate
-package. These days the command line tool could be called ``pytest``
-but since many people have gotten used to the old name and there
-is another tool named "pytest" we just decided to stick with
-``py.test`` for now.
-
-pytest fixtures, parametrized tests
--------------------------------------------------------
-
-.. _funcargs: funcargs.html
-
-Is using pytest fixtures versus xUnit setup a style question?
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-For simple applications and for people experienced with nose_ or
-unittest-style test setup using `xUnit style setup`_ probably
-feels natural. For larger test suites, parametrized testing
-or setup of complex test resources using fixtures_ may feel more natural.
-Moreover, fixtures are ideal for writing advanced test support
-code (like e.g. the monkeypatch_, the tmpdir_ or capture_ fixtures)
-because the support code can register setup/teardown functions
-in a managed class/module/function scope.
-
-.. _monkeypatch: monkeypatch.html
-.. _tmpdir: tmpdir.html
-.. _capture: capture.html
-.. _fixtures: fixture.html
-
-.. _`why pytest_pyfuncarg__ methods?`:
-
-.. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration
-
-Can I yield multiple values from a fixture function?
-++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-There are two conceptual reasons why yielding from a factory function
-is not possible:
-
-* If multiple factories yielded values there would
- be no natural place to determine the combination
- policy - in real-world examples some combinations
- often should not run.
-
-* Calling factories for obtaining test function arguments
- is part of setting up and running a test. At that
- point it is not possible to add new test calls to
- the test collection anymore.
-
-However, with pytest-2.3 you can use the :ref:`@pytest.fixture` decorator
-and specify ``params`` so that all tests depending on the factory-created
-resource will run multiple times with different parameters.
-
-You can also use the ``pytest_generate_tests`` hook to
-implement the `parametrization scheme of your choice`_. See also
-:ref:`paramexamples` for more examples.
-
-.. _`parametrization scheme of your choice`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
-
-pytest interaction with other packages
----------------------------------------------------
-
-Issues with pytest, multiprocess and setuptools?
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-On Windows the multiprocess package will instantiate sub processes
-by pickling and thus implicitly re-import a lot of local modules.
-Unfortunately, setuptools-0.6.11 does not ``if __name__=='__main__'``
-protect its generated command line script. This leads to infinite
-recursion when running a test that instantiates Processes.
-
-As of mid-2013, there shouldn't be a problem anymore when you
-use the standard setuptools (note that distribute has been merged
-back into setuptools which is now shipped directly with virtualenv).
-
-.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/feedback.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/feedback.rst
deleted file mode 100644
index 9c63b7640e0..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/feedback.rst
+++ /dev/null
@@ -1,8 +0,0 @@
-
-What users say:
-
- `py.test is pretty much the best thing ever`_ (Alex Gaynor)
-
-
-.. _`py.test is pretty much the best thing ever`_ (Alex Gaynor)
- http://twitter.com/#!/alex_gaynor/status/22389410366
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/fixture.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/fixture.rst
deleted file mode 100644
index f48607ae2dd..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/fixture.rst
+++ /dev/null
@@ -1,987 +0,0 @@
-.. _fixture:
-.. _fixtures:
-.. _`fixture functions`:
-
-pytest fixtures: explicit, modular, scalable
-========================================================
-
-.. currentmodule:: _pytest.python
-
-.. versionadded:: 2.0/2.3/2.4
-
-.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
-.. _`purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software
-.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection#Definition
-
-The `purpose of test fixtures`_ is to provide a fixed baseline
-upon which tests can reliably and repeatedly execute. pytest fixtures
-offer dramatic improvements over the classic xUnit style of setup/teardown
-functions:
-
-* fixtures have explicit names and are activated by declaring their use
- from test functions, modules, classes or whole projects.
-
-* fixtures are implemented in a modular manner, as each fixture name
- triggers a *fixture function* which can itself use other fixtures.
-
-* fixture management scales from simple unit to complex
- functional testing, allowing to parametrize fixtures and tests according
- to configuration and component options, or to re-use fixtures
- across class, module or whole test session scopes.
-
-In addition, pytest continues to support :ref:`xunitsetup`. You can mix
-both styles, moving incrementally from classic to new style, as you
-prefer. You can also start out from existing :ref:`unittest.TestCase
-style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
-
-.. note::
-
- pytest-2.4 introduced an additional experimental
- :ref:`yield fixture mechanism <yieldfixture>` for easier context manager
- integration and more linear writing of teardown code.
-
-.. _`funcargs`:
-.. _`funcarg mechanism`:
-.. _`fixture function`:
-.. _`@pytest.fixture`:
-.. _`pytest.fixture`:
-
-Fixtures as Function arguments
------------------------------------------
-
-Test functions can receive fixture objects by naming them as an input
-argument. For each argument name, a fixture function with that name provides
-the fixture object. Fixture functions are registered by marking them with
-:py:func:`@pytest.fixture <_pytest.python.fixture>`. Let's look at a simple
-self-contained test module containing a fixture and a test function
-using it::
-
- # content of ./test_smtpsimple.py
- import pytest
-
- @pytest.fixture
- def smtp():
- import smtplib
- return smtplib.SMTP("smtp.gmail.com")
-
- def test_ehlo(smtp):
- response, msg = smtp.ehlo()
- assert response == 250
- assert 0 # for demo purposes
-
-Here, the ``test_ehlo`` needs the ``smtp`` fixture value. pytest
-will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
-marked ``smtp`` fixture function. Running the test looks like this::
-
- $ py.test test_smtpsimple.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 1 items
-
- test_smtpsimple.py F
-
- ======= FAILURES ========
- _______ test_ehlo ________
-
- smtp = <smtplib.SMTP object at 0xdeadbeef>
-
- def test_ehlo(smtp):
- response, msg = smtp.ehlo()
- assert response == 250
- > assert 0 # for demo purposes
- E assert 0
-
- test_smtpsimple.py:11: AssertionError
- ======= 1 failed in 0.12 seconds ========
-
-In the failure traceback we see that the test function was called with a
-``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture
-function. The test function fails on our deliberate ``assert 0``. Here is
-the exact protocol used by ``pytest`` to call the test function this way:
-
-1. pytest :ref:`finds <test discovery>` the ``test_ehlo`` because
- of the ``test_`` prefix. The test function needs a function argument
- named ``smtp``. A matching fixture function is discovered by
- looking for a fixture-marked function named ``smtp``.
-
-2. ``smtp()`` is called to create an instance.
-
-3. ``test_ehlo(<SMTP instance>)`` is called and fails in the last
- line of the test function.
-
-Note that if you misspell a function argument or want
-to use one that isn't available, you'll see an error
-with a list of available function arguments.
-
-.. Note::
-
- You can always issue::
-
- py.test --fixtures test_simplefactory.py
-
- to see available fixtures.
-
- In versions prior to 2.3 there was no ``@pytest.fixture`` marker
- and you had to use a magic ``pytest_funcarg__NAME`` prefix
- for the fixture factory. This remains and will remain supported
- but is not anymore advertised as the primary means of declaring fixture
- functions.
-
-"Funcargs" a prime example of dependency injection
----------------------------------------------------
-
-When injecting fixtures to test functions, pytest-2.0 introduced the
-term "funcargs" or "funcarg mechanism" which continues to be present
-also in docs today. It now refers to the specific case of injecting
-fixture values as arguments to test functions. With pytest-2.3 there are
-more possibilities to use fixtures but "funcargs" remain as the main way
-as they allow to directly state the dependencies of a test function.
-
-As the following examples show in more detail, funcargs allow test
-functions to easily receive and work against specific pre-initialized
-application objects without having to care about import/setup/cleanup
-details. It's a prime example of `dependency injection`_ where fixture
-functions take the role of the *injector* and test functions are the
-*consumers* of fixture objects.
-
-.. _smtpshared:
-
-Sharing a fixture across tests in a module (or class/session)
------------------------------------------------------------------
-
-.. regendoc:wipe
-
-Fixtures requiring network access depend on connectivity and are
-usually time-expensive to create. Extending the previous example, we
-can add a ``scope='module'`` parameter to the
-:py:func:`@pytest.fixture <_pytest.python.fixture>` invocation
-to cause the decorated ``smtp`` fixture function to only be invoked once
-per test module. Multiple test functions in a test module will thus
-each receive the same ``smtp`` fixture instance. The next example puts
-the fixture function into a separate ``conftest.py`` file so
-that tests from multiple test modules in the directory can
-access the fixture function::
-
- # content of conftest.py
- import pytest
- import smtplib
-
- @pytest.fixture(scope="module")
- def smtp():
- return smtplib.SMTP("smtp.gmail.com")
-
-The name of the fixture again is ``smtp`` and you can access its result by
-listing the name ``smtp`` as an input parameter in any test or fixture
-function (in or below the directory where ``conftest.py`` is located)::
-
- # content of test_module.py
-
- def test_ehlo(smtp):
- response, msg = smtp.ehlo()
- assert response == 250
- assert b"smtp.gmail.com" in msg
- assert 0 # for demo purposes
-
- def test_noop(smtp):
- response, msg = smtp.noop()
- assert response == 250
- assert 0 # for demo purposes
-
-We deliberately insert failing ``assert 0`` statements in order to
-inspect what is going on and can now run the tests::
-
- $ py.test test_module.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 2 items
-
- test_module.py FF
-
- ======= FAILURES ========
- _______ test_ehlo ________
-
- smtp = <smtplib.SMTP object at 0xdeadbeef>
-
- def test_ehlo(smtp):
- response, msg = smtp.ehlo()
- assert response == 250
- assert b"smtp.gmail.com" in msg
- > assert 0 # for demo purposes
- E assert 0
-
- test_module.py:6: AssertionError
- _______ test_noop ________
-
- smtp = <smtplib.SMTP object at 0xdeadbeef>
-
- def test_noop(smtp):
- response, msg = smtp.noop()
- assert response == 250
- > assert 0 # for demo purposes
- E assert 0
-
- test_module.py:11: AssertionError
- ======= 2 failed in 0.12 seconds ========
-
-You see the two ``assert 0`` failing and more importantly you can also see
-that the same (module-scoped) ``smtp`` object was passed into the two
-test functions because pytest shows the incoming argument values in the
-traceback. As a result, the two test functions using ``smtp`` run as
-quick as a single one because they reuse the same instance.
-
-If you decide that you rather want to have a session-scoped ``smtp``
-instance, you can simply declare it:
-
-.. code-block:: python
-
- @pytest.fixture(scope="session")
- def smtp(...):
- # the returned fixture value will be shared for
- # all tests needing it
-
-.. _`finalization`:
-
-Fixture finalization / executing teardown code
--------------------------------------------------------------
-
-pytest supports execution of fixture specific finalization code
-when the fixture goes out of scope. By accepting a ``request`` object
-into your fixture function you can call its ``request.addfinalizer`` one
-or multiple times::
-
- # content of conftest.py
-
- import smtplib
- import pytest
-
- @pytest.fixture(scope="module")
- def smtp(request):
- smtp = smtplib.SMTP("smtp.gmail.com")
- def fin():
- print ("teardown smtp")
- smtp.close()
- request.addfinalizer(fin)
- return smtp # provide the fixture value
-
-The ``fin`` function will execute when the last test using
-the fixture in the module has finished execution.
-
-Let's execute it::
-
- $ py.test -s -q --tb=no
- FFteardown smtp
-
- 2 failed in 0.12 seconds
-
-We see that the ``smtp`` instance is finalized after the two
-tests finished execution. Note that if we decorated our fixture
-function with ``scope='function'`` then fixture setup and cleanup would
-occur around each single test. In either case the test
-module itself does not need to change or know about these details
-of fixture setup.
-
-
-.. _`request-context`:
-
-Fixtures can introspect the requesting test context
--------------------------------------------------------------
-
-Fixture function can accept the :py:class:`request <FixtureRequest>` object
-to introspect the "requesting" test function, class or module context.
-Further extending the previous ``smtp`` fixture example, let's
-read an optional server URL from the test module which uses our fixture::
-
- # content of conftest.py
- import pytest
- import smtplib
-
- @pytest.fixture(scope="module")
- def smtp(request):
- server = getattr(request.module, "smtpserver", "smtp.gmail.com")
- smtp = smtplib.SMTP(server)
-
- def fin():
- print ("finalizing %s (%s)" % (smtp, server))
- smtp.close()
- request.addfinalizer(fin)
- return smtp
-
-We use the ``request.module`` attribute to optionally obtain an
-``smtpserver`` attribute from the test module. If we just execute
-again, nothing much has changed::
-
- $ py.test -s -q --tb=no
- FFfinalizing <smtplib.SMTP object at 0xdeadbeef> (smtp.gmail.com)
-
- 2 failed in 0.12 seconds
-
-Let's quickly create another test module that actually sets the
-server URL in its module namespace::
-
- # content of test_anothersmtp.py
-
- smtpserver = "mail.python.org" # will be read by smtp fixture
-
- def test_showhelo(smtp):
- assert 0, smtp.helo()
-
-Running it::
-
- $ py.test -qq --tb=short test_anothersmtp.py
- F
- ======= FAILURES ========
- _______ test_showhelo ________
- test_anothersmtp.py:5: in test_showhelo
- assert 0, smtp.helo()
- E AssertionError: (250, b'mail.python.org')
- E assert 0
-
-voila! The ``smtp`` fixture function picked up our mail server name
-from the module namespace.
-
-.. _`fixture-parametrize`:
-
-Parametrizing a fixture
------------------------------------------------------------------
-
-Fixture functions can be parametrized in which case they will be called
-multiple times, each time executing the set of dependent tests, i. e. the
-tests that depend on this fixture. Test functions do usually not need
-to be aware of their re-running. Fixture parametrization helps to
-write exhaustive functional tests for components which themselves can be
-configured in multiple ways.
-
-Extending the previous example, we can flag the fixture to create two
-``smtp`` fixture instances which will cause all tests using the fixture
-to run twice. The fixture function gets access to each parameter
-through the special :py:class:`request <FixtureRequest>` object::
-
- # content of conftest.py
- import pytest
- import smtplib
-
- @pytest.fixture(scope="module",
- params=["smtp.gmail.com", "mail.python.org"])
- def smtp(request):
- smtp = smtplib.SMTP(request.param)
- def fin():
- print ("finalizing %s" % smtp)
- smtp.close()
- request.addfinalizer(fin)
- return smtp
-
-The main change is the declaration of ``params`` with
-:py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values
-for each of which the fixture function will execute and can access
-a value via ``request.param``. No test function code needs to change.
-So let's just do another run::
-
- $ py.test -q test_module.py
- FFFF
- ======= FAILURES ========
- _______ test_ehlo[smtp.gmail.com] ________
-
- smtp = <smtplib.SMTP object at 0xdeadbeef>
-
- def test_ehlo(smtp):
- response, msg = smtp.ehlo()
- assert response == 250
- assert b"smtp.gmail.com" in msg
- > assert 0 # for demo purposes
- E assert 0
-
- test_module.py:6: AssertionError
- _______ test_noop[smtp.gmail.com] ________
-
- smtp = <smtplib.SMTP object at 0xdeadbeef>
-
- def test_noop(smtp):
- response, msg = smtp.noop()
- assert response == 250
- > assert 0 # for demo purposes
- E assert 0
-
- test_module.py:11: AssertionError
- _______ test_ehlo[mail.python.org] ________
-
- smtp = <smtplib.SMTP object at 0xdeadbeef>
-
- def test_ehlo(smtp):
- response, msg = smtp.ehlo()
- assert response == 250
- > assert b"smtp.gmail.com" in msg
- E assert b'smtp.gmail.com' in b'mail.python.org\nSIZE 51200000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8'
-
- test_module.py:5: AssertionError
- -------------------------- Captured stdout setup ---------------------------
- finalizing <smtplib.SMTP object at 0xdeadbeef>
- _______ test_noop[mail.python.org] ________
-
- smtp = <smtplib.SMTP object at 0xdeadbeef>
-
- def test_noop(smtp):
- response, msg = smtp.noop()
- assert response == 250
- > assert 0 # for demo purposes
- E assert 0
-
- test_module.py:11: AssertionError
- 4 failed in 0.12 seconds
-
-We see that our two test functions each ran twice, against the different
-``smtp`` instances. Note also, that with the ``mail.python.org``
-connection the second test fails in ``test_ehlo`` because a
-different server string is expected than what arrived.
-
-pytest will build a string that is the test ID for each fixture value
-in a parametrized fixture, e.g. ``test_ehlo[smtp.gmail.com]`` and
-``test_ehlo[mail.python.org]`` in the above examples. These IDs can
-be used with ``-k`` to select specific cases to run, and they will
-also identify the specific case when one is failing. Running pytest
-with ``--collect-only`` will show the generated IDs.
-
-Numbers, strings, booleans and None will have their usual string
-representation used in the test ID. For other objects, pytest will
-make a string based on the argument name. It is possible to customise
-the string used in a test ID for a certain fixture value by using the
-``ids`` keyword argument::
-
- # content of test_ids.py
- import pytest
-
- @pytest.fixture(params=[0, 1], ids=["spam", "ham"])
- def a(request):
- return request.param
-
- def test_a(a):
- pass
-
- def idfn(fixture_value):
- if fixture_value == 0:
- return "eggs"
- else:
- return None
-
- @pytest.fixture(params=[0, 1], ids=idfn)
- def b(request):
- return request.param
-
- def test_b(b):
- pass
-
-The above shows how ``ids`` can be either a list of strings to use or
-a function which will be called with the fixture value and then
-has to return a string to use. In the latter case if the function
-return ``None`` then pytest's auto-generated ID will be used.
-
-Running the above tests results in the following test IDs being used::
-
- $ py.test --collect-only
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 10 items
- <Module 'test_anothersmtp.py'>
- <Function 'test_showhelo[smtp.gmail.com]'>
- <Function 'test_showhelo[mail.python.org]'>
- <Module 'test_ids.py'>
- <Function 'test_a[spam]'>
- <Function 'test_a[ham]'>
- <Function 'test_b[eggs]'>
- <Function 'test_b[1]'>
- <Module 'test_module.py'>
- <Function 'test_ehlo[smtp.gmail.com]'>
- <Function 'test_noop[smtp.gmail.com]'>
- <Function 'test_ehlo[mail.python.org]'>
- <Function 'test_noop[mail.python.org]'>
-
- ======= no tests ran in 0.12 seconds ========
-
-.. _`interdependent fixtures`:
-
-Modularity: using fixtures from a fixture function
-----------------------------------------------------------
-
-You can not only use fixtures in test functions but fixture functions
-can use other fixtures themselves. This contributes to a modular design
-of your fixtures and allows re-use of framework-specific fixtures across
-many projects. As a simple example, we can extend the previous example
-and instantiate an object ``app`` where we stick the already defined
-``smtp`` resource into it::
-
- # content of test_appsetup.py
-
- import pytest
-
- class App:
- def __init__(self, smtp):
- self.smtp = smtp
-
- @pytest.fixture(scope="module")
- def app(smtp):
- return App(smtp)
-
- def test_smtp_exists(app):
- assert app.smtp
-
-Here we declare an ``app`` fixture which receives the previously defined
-``smtp`` fixture and instantiates an ``App`` object with it. Let's run it::
-
- $ py.test -v test_appsetup.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- rootdir: $REGENDOC_TMPDIR, inifile:
- collecting ... collected 2 items
-
- test_appsetup.py::test_smtp_exists[smtp.gmail.com] PASSED
- test_appsetup.py::test_smtp_exists[mail.python.org] PASSED
-
- ======= 2 passed in 0.12 seconds ========
-
-Due to the parametrization of ``smtp`` the test will run twice with two
-different ``App`` instances and respective smtp servers. There is no
-need for the ``app`` fixture to be aware of the ``smtp`` parametrization
-as pytest will fully analyse the fixture dependency graph.
-
-Note, that the ``app`` fixture has a scope of ``module`` and uses a
-module-scoped ``smtp`` fixture. The example would still work if ``smtp``
-was cached on a ``session`` scope: it is fine for fixtures to use
-"broader" scoped fixtures but not the other way round:
-A session-scoped fixture could not use a module-scoped one in a
-meaningful way.
-
-
-.. _`automatic per-resource grouping`:
-
-Automatic grouping of tests by fixture instances
-----------------------------------------------------------
-
-.. regendoc: wipe
-
-pytest minimizes the number of active fixtures during test runs.
-If you have a parametrized fixture, then all the tests using it will
-first execute with one instance and then finalizers are called
-before the next fixture instance is created. Among other things,
-this eases testing of applications which create and use global state.
-
-The following example uses two parametrized funcargs, one of which is
-scoped on a per-module basis, and all the functions perform ``print`` calls
-to show the setup/teardown flow::
-
- # content of test_module.py
- import pytest
-
- @pytest.fixture(scope="module", params=["mod1", "mod2"])
- def modarg(request):
- param = request.param
- print ("create", param)
- def fin():
- print ("fin %s" % param)
- return param
-
- @pytest.fixture(scope="function", params=[1,2])
- def otherarg(request):
- return request.param
-
- def test_0(otherarg):
- print (" test0", otherarg)
- def test_1(modarg):
- print (" test1", modarg)
- def test_2(otherarg, modarg):
- print (" test2", otherarg, modarg)
-
-Let's run the tests in verbose mode and with looking at the print-output::
-
- $ py.test -v -s test_module.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
- cachedir: .cache
- rootdir: $REGENDOC_TMPDIR, inifile:
- collecting ... collected 8 items
-
- test_module.py::test_0[1] test0 1
- PASSED
- test_module.py::test_0[2] test0 2
- PASSED
- test_module.py::test_1[mod1] create mod1
- test1 mod1
- PASSED
- test_module.py::test_2[1-mod1] test2 1 mod1
- PASSED
- test_module.py::test_2[2-mod1] test2 2 mod1
- PASSED
- test_module.py::test_1[mod2] create mod2
- test1 mod2
- PASSED
- test_module.py::test_2[1-mod2] test2 1 mod2
- PASSED
- test_module.py::test_2[2-mod2] test2 2 mod2
- PASSED
-
- ======= 8 passed in 0.12 seconds ========
-
-You can see that the parametrized module-scoped ``modarg`` resource caused
-an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
-before the ``mod2`` resource was setup.
-
-
-.. _`usefixtures`:
-
-Using fixtures from classes, modules or projects
-----------------------------------------------------------------------
-
-.. regendoc:wipe
-
-Sometimes test functions do not directly need access to a fixture object.
-For example, tests may require to operate with an empty directory as the
-current working directory but otherwise do not care for the concrete
-directory. Here is how you can can use the standard `tempfile
-<http://docs.python.org/library/tempfile.html>`_ and pytest fixtures to
-achieve it. We separate the creation of the fixture into a conftest.py
-file::
-
- # content of conftest.py
-
- import pytest
- import tempfile
- import os
-
- @pytest.fixture()
- def cleandir():
- newpath = tempfile.mkdtemp()
- os.chdir(newpath)
-
-and declare its use in a test module via a ``usefixtures`` marker::
-
- # content of test_setenv.py
- import os
- import pytest
-
- @pytest.mark.usefixtures("cleandir")
- class TestDirectoryInit:
- def test_cwd_starts_empty(self):
- assert os.listdir(os.getcwd()) == []
- with open("myfile", "w") as f:
- f.write("hello")
-
- def test_cwd_again_starts_empty(self):
- assert os.listdir(os.getcwd()) == []
-
-Due to the ``usefixtures`` marker, the ``cleandir`` fixture
-will be required for the execution of each test method, just as if
-you specified a "cleandir" function argument to each of them. Let's run it
-to verify our fixture is activated and the tests pass::
-
- $ py.test -q
- ..
- 2 passed in 0.12 seconds
-
-You can specify multiple fixtures like this:
-
-.. code-block:: python
-
- @pytest.mark.usefixtures("cleandir", "anotherfixture")
-
-and you may specify fixture usage at the test module level, using
-a generic feature of the mark mechanism:
-
-.. code-block:: python
-
- pytestmark = pytest.mark.usefixtures("cleandir")
-
-Note that the assigned variable *must* be called ``pytestmark``, assigning e.g.
-``foomark`` will not activate the fixtures.
-
-Lastly you can put fixtures required by all tests in your project
-into an ini-file:
-
-.. code-block:: ini
-
- # content of pytest.ini
- [pytest]
- usefixtures = cleandir
-
-
-.. _`autouse`:
-.. _`autouse fixtures`:
-
-Autouse fixtures (xUnit setup on steroids)
-----------------------------------------------------------------------
-
-.. regendoc:wipe
-
-Occasionally, you may want to have fixtures get invoked automatically
-without a `usefixtures`_ or `funcargs`_ reference. As a practical
-example, suppose we have a database fixture which has a
-begin/rollback/commit architecture and we want to automatically surround
-each test method by a transaction and a rollback. Here is a dummy
-self-contained implementation of this idea::
-
- # content of test_db_transact.py
-
- import pytest
-
- class DB:
- def __init__(self):
- self.intransaction = []
- def begin(self, name):
- self.intransaction.append(name)
- def rollback(self):
- self.intransaction.pop()
-
- @pytest.fixture(scope="module")
- def db():
- return DB()
-
- class TestClass:
- @pytest.fixture(autouse=True)
- def transact(self, request, db):
- db.begin(request.function.__name__)
- request.addfinalizer(db.rollback)
-
- def test_method1(self, db):
- assert db.intransaction == ["test_method1"]
-
- def test_method2(self, db):
- assert db.intransaction == ["test_method2"]
-
-The class-level ``transact`` fixture is marked with *autouse=true*
-which implies that all test methods in the class will use this fixture
-without a need to state it in the test function signature or with a
-class-level ``usefixtures`` decorator.
-
-If we run it, we get two passing tests::
-
- $ py.test -q
- ..
- 2 passed in 0.12 seconds
-
-Here is how autouse fixtures work in other scopes:
-
-- if an autouse fixture is defined in a test module, all its test
- functions automatically use it.
-
-- if an autouse fixture is defined in a conftest.py file then all tests in
- all test modules below its directory will invoke the fixture.
-
-- lastly, and **please use that with care**: if you define an autouse
- fixture in a plugin, it will be invoked for all tests in all projects
- where the plugin is installed. This can be useful if a fixture only
- anyway works in the presence of certain settings e. g. in the ini-file. Such
- a global fixture should always quickly determine if it should do
- any work and avoid otherwise expensive imports or computation.
-
-Note that the above ``transact`` fixture may very well be a fixture that
-you want to make available in your project without having it generally
-active. The canonical way to do that is to put the transact definition
-into a conftest.py file **without** using ``autouse``::
-
- # content of conftest.py
- @pytest.fixture()
- def transact(self, request, db):
- db.begin()
- request.addfinalizer(db.rollback)
-
-and then e.g. have a TestClass using it by declaring the need::
-
- @pytest.mark.usefixtures("transact")
- class TestClass:
- def test_method1(self):
- ...
-
-All test methods in this TestClass will use the transaction fixture while
-other test classes or functions in the module will not use it unless
-they also add a ``transact`` reference.
-
-Shifting (visibility of) fixture functions
-----------------------------------------------------
-
-If during implementing your tests you realize that you
-want to use a fixture function from multiple test files you can move it
-to a :ref:`conftest.py <conftest.py>` file or even separately installable
-:ref:`plugins <plugins>` without changing test code. The discovery of
-fixtures functions starts at test classes, then test modules, then
-``conftest.py`` files and finally builtin and third party plugins.
-
-Overriding fixtures on various levels
--------------------------------------
-
-In relatively large test suite, you most likely need to ``override`` a ``global`` or ``root`` fixture with a ``locally``
-defined one, keeping the test code readable and maintainable.
-
-Override a fixture on a folder (conftest) level
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Given the tests file structure is:
-
-::
-
- tests/
- __init__.py
-
- conftest.py
- # content of tests/conftest.py
- import pytest
-
- @pytest.fixture
- def username():
- return 'username'
-
- test_something.py
- # content of tests/test_something.py
- def test_username(username):
- assert username == 'username'
-
- subfolder/
- __init__.py
-
- conftest.py
- # content of tests/subfolder/conftest.py
- import pytest
-
- @pytest.fixture
- def username(username):
- return 'overridden-' + username
-
- test_something.py
- # content of tests/subfolder/test_something.py
- def test_username(username):
- assert username == 'overridden-username'
-
-As you can see, a fixture with the same name can be overridden for certain test folder level.
-Note that the ``base`` or ``super`` fixture can be accessed from the ``overriding``
-fixture easily - used in the example above.
-
-Override a fixture on a test module level
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Given the tests file structure is:
-
-::
-
- tests/
- __init__.py
-
- conftest.py
- # content of tests/conftest.py
- @pytest.fixture
- def username():
- return 'username'
-
- test_something.py
- # content of tests/test_something.py
- import pytest
-
- @pytest.fixture
- def username(username):
- return 'overridden-' + username
-
- def test_username(username):
- assert username == 'overridden-username'
-
- test_something_else.py
- # content of tests/test_something_else.py
- import pytest
-
- @pytest.fixture
- def username(username):
- return 'overridden-else-' + username
-
- def test_username(username):
- assert username == 'overridden-else-username'
-
-In the example above, a fixture with the same name can be overridden for certain test module.
-
-
-Override a fixture with direct test parametrization
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Given the tests file structure is:
-
-::
-
- tests/
- __init__.py
-
- conftest.py
- # content of tests/conftest.py
- import pytest
-
- @pytest.fixture
- def username():
- return 'username'
-
- @pytest.fixture
- def other_username(username):
- return 'other-' + username
-
- test_something.py
- # content of tests/test_something.py
- import pytest
-
- @pytest.mark.parametrize('username', ['directly-overridden-username'])
- def test_username(username):
- assert username == 'directly-overridden-username'
-
- @pytest.mark.parametrize('username', ['directly-overridden-username-other'])
- def test_username_other(other_username):
- assert username == 'other-directly-overridden-username-other'
-
-In the example above, a fixture value is overridden by the test parameter value. Note that the value of the fixture
-can be overridden this way even if the test doesn't use it directly (doesn't mention it in the function prototype).
-
-
-Override a parametrized fixture with non-parametrized one and vice versa
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Given the tests file structure is:
-
-::
-
- tests/
- __init__.py
-
- conftest.py
- # content of tests/conftest.py
- import pytest
-
- @pytest.fixture(params=['one', 'two', 'three'])
- def parametrized_username(request):
- return request.param
-
- @pytest.fixture
- def non_parametrized_username(request):
- return 'username'
-
- test_something.py
- # content of tests/test_something.py
- import pytest
-
- @pytest.fixture
- def parametrized_username():
- return 'overridden-username'
-
- @pytest.fixture(params=['one', 'two', 'three'])
- def non_parametrized_username(request):
- return request.param
-
- def test_username(parametrized_username):
- assert parametrized_username == 'overridden-username'
-
- def test_parametrized_username(non_parametrized_username):
- assert non_parametrized_username in ['one', 'two', 'three']
-
- test_something_else.py
- # content of tests/test_something_else.py
- def test_username(parametrized_username):
- assert parametrized_username in ['one', 'two', 'three']
-
- def test_username(non_parametrized_username):
- assert non_parametrized_username == 'username'
-
-In the example above, a parametrized fixture is overridden with a non-parametrized version, and
-a non-parametrized fixture is overridden with a parametrized version for certain test module.
-The same applies for the test folder level obviously.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/funcarg_compare.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/funcarg_compare.rst
deleted file mode 100644
index 832922e1839..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/funcarg_compare.rst
+++ /dev/null
@@ -1,217 +0,0 @@
-
-.. _`funcargcompare`:
-
-pytest-2.3: reasoning for fixture/funcarg evolution
-=============================================================
-
-**Target audience**: Reading this document requires basic knowledge of
-python testing, xUnit setup methods and the (previous) basic pytest
-funcarg mechanism, see http://pytest.org/2.2.4/funcargs.html
-If you are new to pytest, then you can simply ignore this
-section and read the other sections.
-
-.. currentmodule:: _pytest
-
-Shortcomings of the previous ``pytest_funcarg__`` mechanism
---------------------------------------------------------------
-
-The pre pytest-2.3 funcarg mechanism calls a factory each time a
-funcarg for a test function is required. If a factory wants to
-re-use a resource across different scopes, it often used
-the ``request.cached_setup()`` helper to manage caching of
-resources. Here is a basic example how we could implement
-a per-session Database object::
-
- # content of conftest.py
- class Database:
- def __init__(self):
- print ("database instance created")
- def destroy(self):
- print ("database instance destroyed")
-
- def pytest_funcarg__db(request):
- return request.cached_setup(setup=DataBase,
- teardown=lambda db: db.destroy,
- scope="session")
-
-There are several limitations and difficulties with this approach:
-
-1. Scoping funcarg resource creation is not straight forward, instead one must
- understand the intricate cached_setup() method mechanics.
-
-2. parametrizing the "db" resource is not straight forward:
- you need to apply a "parametrize" decorator or implement a
- :py:func:`~hookspec.pytest_generate_tests` hook
- calling :py:func:`~python.Metafunc.parametrize` which
- performs parametrization at the places where the resource
- is used. Moreover, you need to modify the factory to use an
- ``extrakey`` parameter containing ``request.param`` to the
- :py:func:`~python.Request.cached_setup` call.
-
-3. Multiple parametrized session-scoped resources will be active
- at the same time, making it hard for them to affect global state
- of the application under test.
-
-4. there is no way how you can make use of funcarg factories
- in xUnit setup methods.
-
-5. A non-parametrized fixture function cannot use a parametrized
- funcarg resource if it isn't stated in the test function signature.
-
-All of these limitations are addressed with pytest-2.3 and its
-improved :ref:`fixture mechanism <fixture>`.
-
-
-Direct scoping of fixture/funcarg factories
---------------------------------------------------------
-
-Instead of calling cached_setup() with a cache scope, you can use the
-:ref:`@pytest.fixture <pytest.fixture>` decorator and directly state
-the scope::
-
- @pytest.fixture(scope="session")
- def db(request):
- # factory will only be invoked once per session -
- db = DataBase()
- request.addfinalizer(db.destroy) # destroy when session is finished
- return db
-
-This factory implementation does not need to call ``cached_setup()`` anymore
-because it will only be invoked once per session. Moreover, the
-``request.addfinalizer()`` registers a finalizer according to the specified
-resource scope on which the factory function is operating.
-
-
-Direct parametrization of funcarg resource factories
-----------------------------------------------------------
-
-Previously, funcarg factories could not directly cause parametrization.
-You needed to specify a ``@parametrize`` decorator on your test function
-or implement a ``pytest_generate_tests`` hook to perform
-parametrization, i.e. calling a test multiple times with different value
-sets. pytest-2.3 introduces a decorator for use on the factory itself::
-
- @pytest.fixture(params=["mysql", "pg"])
- def db(request):
- ... # use request.param
-
-Here the factory will be invoked twice (with the respective "mysql"
-and "pg" values set as ``request.param`` attributes) and and all of
-the tests requiring "db" will run twice as well. The "mysql" and
-"pg" values will also be used for reporting the test-invocation variants.
-
-This new way of parametrizing funcarg factories should in many cases
-allow to re-use already written factories because effectively
-``request.param`` was already used when test functions/classes were
-parametrized via
-:py:func:`~_pytest.python.Metafunc.parametrize(indirect=True)` calls.
-
-Of course it's perfectly fine to combine parametrization and scoping::
-
- @pytest.fixture(scope="session", params=["mysql", "pg"])
- def db(request):
- if request.param == "mysql":
- db = MySQL()
- elif request.param == "pg":
- db = PG()
- request.addfinalizer(db.destroy) # destroy when session is finished
- return db
-
-This would execute all tests requiring the per-session "db" resource twice,
-receiving the values created by the two respective invocations to the
-factory function.
-
-
-No ``pytest_funcarg__`` prefix when using @fixture decorator
--------------------------------------------------------------------
-
-When using the ``@fixture`` decorator the name of the function
-denotes the name under which the resource can be accessed as a function
-argument::
-
- @pytest.fixture()
- def db(request):
- ...
-
-The name under which the funcarg resource can be requested is ``db``.
-
-You can still use the "old" non-decorator way of specifying funcarg factories
-aka::
-
- def pytest_funcarg__db(request):
- ...
-
-
-But it is then not possible to define scoping and parametrization.
-It is thus recommended to use the factory decorator.
-
-
-solving per-session setup / autouse fixtures
---------------------------------------------------------------
-
-pytest for a long time offered a pytest_configure and a pytest_sessionstart
-hook which are often used to setup global resources. This suffers from
-several problems:
-
-1. in distributed testing the master process would setup test resources
- that are never needed because it only co-ordinates the test run
- activities of the slave processes.
-
-2. if you only perform a collection (with "--collect-only")
- resource-setup will still be executed.
-
-3. If a pytest_sessionstart is contained in some subdirectories
- conftest.py file, it will not be called. This stems from the
- fact that this hook is actually used for reporting, in particular
- the test-header with platform/custom information.
-
-Moreover, it was not easy to define a scoped setup from plugins or
-conftest files other than to implement a ``pytest_runtest_setup()`` hook
-and caring for scoping/caching yourself. And it's virtually impossible
-to do this with parametrization as ``pytest_runtest_setup()`` is called
-during test execution and parametrization happens at collection time.
-
-It follows that pytest_configure/session/runtest_setup are often not
-appropriate for implementing common fixture needs. Therefore,
-pytest-2.3 introduces :ref:`autouse fixtures` which fully
-integrate with the generic :ref:`fixture mechanism <fixture>`
-and obsolete many prior uses of pytest hooks.
-
-funcargs/fixture discovery now happens at collection time
----------------------------------------------------------------------
-
-pytest-2.3 takes care to discover fixture/funcarg factories
-at collection time. This is more efficient especially for large test suites.
-Moreover, a call to "py.test --collect-only" should be able to in the future
-show a lot of setup-information and thus presents a nice method to get an
-overview of fixture management in your project.
-
-.. _`compatibility notes`:
-
-.. _`funcargscompat`:
-
-Conclusion and compatibility notes
----------------------------------------------------------
-
-**funcargs** were originally introduced to pytest-2.0. In pytest-2.3
-the mechanism was extended and refined and is now described as
-fixtures:
-
-* previously funcarg factories were specified with a special
- ``pytest_funcarg__NAME`` prefix instead of using the
- ``@pytest.fixture`` decorator.
-
-* Factories received a ``request`` object which managed caching through
- ``request.cached_setup()`` calls and allowed using other funcargs via
- ``request.getfuncargvalue()`` calls. These intricate APIs made it hard
- to do proper parametrization and implement resource caching. The
- new :py:func:`pytest.fixture` decorator allows to declare the scope
- and let pytest figure things out for you.
-
-* if you used parametrization and funcarg factories which made use of
- ``request.cached_setup()`` it is recommended to invest a few minutes
- and simplify your fixture function code to use the :ref:`@pytest.fixture`
- decorator instead. This will also allow to take advantage of
- the automatic per-resource grouping of tests.
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/genapi.py b/tests/wpt/web-platform-tests/tools/pytest/doc/en/genapi.py
deleted file mode 100644
index f8cdda6cfbf..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/genapi.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import textwrap
-import inspect
-
-class Writer:
- def __init__(self, clsname):
- self.clsname = clsname
-
- def __enter__(self):
- self.file = open("%s.api" % self.clsname, "w")
- return self
-
- def __exit__(self, *args):
- self.file.close()
- print "wrote", self.file.name
-
- def line(self, line):
- self.file.write(line+"\n")
-
- def docmethod(self, method):
- doc = " ".join(method.__doc__.split())
- indent = " "
- w = textwrap.TextWrapper(initial_indent=indent,
- subsequent_indent=indent)
-
- spec = inspect.getargspec(method)
- del spec.args[0]
- self.line(".. py:method:: " + method.__name__ +
- inspect.formatargspec(*spec))
- self.line("")
- self.line(w.fill(doc))
- self.line("")
-
-def pytest_funcarg__a(request):
- with Writer("request") as writer:
- writer.docmethod(request.getfuncargvalue)
- writer.docmethod(request.cached_setup)
- writer.docmethod(request.addfinalizer)
- writer.docmethod(request.applymarker)
-
-def test_hello(a):
- pass
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/getting-started.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/getting-started.rst
deleted file mode 100644
index 4a5b75aea0c..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/getting-started.rst
+++ /dev/null
@@ -1,237 +0,0 @@
-Installation and Getting Started
-===================================
-
-**Pythons**: Python 2.6,2.7,3.3,3.4,3.5, Jython, PyPy-2.3
-
-**Platforms**: Unix/Posix and Windows
-
-**PyPI package name**: `pytest <http://pypi.python.org/pypi/pytest>`_
-
-**dependencies**: `py <http://pypi.python.org/pypi/py>`_,
-`colorama (Windows) <http://pypi.python.org/pypi/colorama>`_,
-`argparse (py26) <http://pypi.python.org/pypi/argparse>`_.
-
-**documentation as PDF**: `download latest <http://pytest.org/latest/pytest.pdf>`_
-
-.. _`getstarted`:
-.. _installation:
-
-Installation
-----------------------------------------
-
-Installation options::
-
- pip install -U pytest # or
- easy_install -U pytest
-
-To check your installation has installed the correct version::
-
- $ py.test --version
- This is pytest version 2.9.1, imported from $PYTHON_PREFIX/lib/python3.4/site-packages/pytest.py
-
-If you get an error checkout :ref:`installation issues`.
-
-.. _`simpletest`:
-
-Our first test run
-----------------------------------------------------------
-
-Let's create a first test file with a simple test function::
-
- # content of test_sample.py
- def func(x):
- return x + 1
-
- def test_answer():
- assert func(3) == 5
-
-That's it. You can execute the test function now::
-
- $ py.test
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 1 items
-
- test_sample.py F
-
- ======= FAILURES ========
- _______ test_answer ________
-
- def test_answer():
- > assert func(3) == 5
- E assert 4 == 5
- E + where 4 = func(3)
-
- test_sample.py:5: AssertionError
- ======= 1 failed in 0.12 seconds ========
-
-We got a failure report because our little ``func(3)`` call did not return ``5``.
-
-.. note::
-
- You can simply use the ``assert`` statement for asserting test
- expectations. pytest's :ref:`assert introspection` will intelligently
- report intermediate values of the assert expression freeing
- you from the need to learn the many names of `JUnit legacy methods`_.
-
-.. _`JUnit legacy methods`: http://docs.python.org/library/unittest.html#test-cases
-
-.. _`assert statement`: http://docs.python.org/reference/simple_stmts.html#the-assert-statement
-
-Running multiple tests
-----------------------------------------------------------
-
-``pytest`` will run all files in the current directory and its subdirectories of the form test_*.py or \*_test.py. More generally, it follows :ref:`standard test discovery rules <test discovery>`.
-
-
-Asserting that a certain exception is raised
---------------------------------------------------------------
-
-If you want to assert that some code raises an exception you can
-use the ``raises`` helper::
-
- # content of test_sysexit.py
- import pytest
- def f():
- raise SystemExit(1)
-
- def test_mytest():
- with pytest.raises(SystemExit):
- f()
-
-Running it with, this time in "quiet" reporting mode::
-
- $ py.test -q test_sysexit.py
- .
- 1 passed in 0.12 seconds
-
-Grouping multiple tests in a class
---------------------------------------------------------------
-
-Once you start to have more than a few tests it often makes sense
-to group tests logically, in classes and modules. Let's write a class
-containing two tests::
-
- # content of test_class.py
- class TestClass:
- def test_one(self):
- x = "this"
- assert 'h' in x
-
- def test_two(self):
- x = "hello"
- assert hasattr(x, 'check')
-
-The two tests are found because of the standard :ref:`test discovery`.
-There is no need to subclass anything. We can simply
-run the module by passing its filename::
-
- $ py.test -q test_class.py
- .F
- ======= FAILURES ========
- _______ TestClass.test_two ________
-
- self = <test_class.TestClass object at 0xdeadbeef>
-
- def test_two(self):
- x = "hello"
- > assert hasattr(x, 'check')
- E assert hasattr('hello', 'check')
-
- test_class.py:8: AssertionError
- 1 failed, 1 passed in 0.12 seconds
-
-The first test passed, the second failed. Again we can easily see
-the intermediate values used in the assertion, helping us to
-understand the reason for the failure.
-
-Going functional: requesting a unique temporary directory
---------------------------------------------------------------
-
-For functional tests one often needs to create some files
-and pass them to application objects. pytest provides
-:ref:`builtinfixtures` which allow to request arbitrary
-resources, for example a unique temporary directory::
-
- # content of test_tmpdir.py
- def test_needsfiles(tmpdir):
- print (tmpdir)
- assert 0
-
-We list the name ``tmpdir`` in the test function signature and
-``pytest`` will lookup and call a fixture factory to create the resource
-before performing the test function call. Let's just run it::
-
- $ py.test -q test_tmpdir.py
- F
- ======= FAILURES ========
- _______ test_needsfiles ________
-
- tmpdir = local('PYTEST_TMPDIR/test_needsfiles0')
-
- def test_needsfiles(tmpdir):
- print (tmpdir)
- > assert 0
- E assert 0
-
- test_tmpdir.py:3: AssertionError
- --------------------------- Captured stdout call ---------------------------
- PYTEST_TMPDIR/test_needsfiles0
- 1 failed in 0.12 seconds
-
-Before the test runs, a unique-per-test-invocation temporary directory
-was created. More info at :ref:`tmpdir handling`.
-
-You can find out what kind of builtin :ref:`fixtures` exist by typing::
-
- py.test --fixtures # shows builtin and custom fixtures
-
-Where to go next
--------------------------------------
-
-Here are a few suggestions where to go next:
-
-* :ref:`cmdline` for command line invocation examples
-* :ref:`good practices <goodpractices>` for virtualenv, test layout, genscript support
-* :ref:`fixtures` for providing a functional baseline to your tests
-* :ref:`apiref` for documentation and examples on using ``pytest``
-* :ref:`plugins` managing and writing plugins
-
-.. _`installation issues`:
-
-Known Installation issues
-------------------------------
-
-easy_install or pip not found?
-++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-.. _`install pip`: http://www.pip-installer.org/en/latest/index.html
-
-`Install pip`_ for a state of the art python package installer.
-
-Install `setuptools`_ to get ``easy_install`` which allows to install
-``.egg`` binary format packages in addition to source-based ones.
-
-py.test not found on Windows despite installation?
-++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-.. _`Python for Windows`: http://www.imladris.com/Scripts/PythonForWindows.html
-
-- **Windows**: If "easy_install" or "py.test" are not found
- you need to add the Python script path to your ``PATH``, see here:
- `Python for Windows`_. You may alternatively use an `ActivePython install`_
- which does this for you automatically.
-
-.. _`ActivePython install`: http://www.activestate.com/activepython/downloads
-
-.. _`Jython does not create command line launchers`: http://bugs.jython.org/issue1491
-
-- **Jython2.5.1 on Windows XP**: `Jython does not create command line launchers`_
- so ``py.test`` will not work correctly. You may install py.test on
- CPython and type ``py.test --genscript=mytest`` and then use
- ``jython mytest`` to run your tests with Jython using ``pytest``.
-
- :ref:`examples` for more complex examples
-
-.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/goodpractices.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/goodpractices.rst
deleted file mode 100644
index 2d8050bd9ca..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/goodpractices.rst
+++ /dev/null
@@ -1,278 +0,0 @@
-.. highlightlang:: python
-.. _`goodpractices`:
-
-Good Integration Practices
-=================================================
-
-
-.. _`test discovery`:
-.. _`Python test discovery`:
-
-Conventions for Python test discovery
--------------------------------------------------
-
-``pytest`` implements the following standard test discovery:
-
-* If no arguments are specified then collection starts from :confval:`testpaths`
- (if configured) or the current directory. Alternatively, command line arguments
- can be used in any combination of directories, file names or node ids.
-* recurse into directories, unless they match :confval:`norecursedirs`
-* ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
-* ``Test`` prefixed test classes (without an ``__init__`` method)
-* ``test_`` prefixed test functions or methods are test items
-
-For examples of how to customize your test discovery :doc:`example/pythoncollection`.
-
-Within Python modules, ``pytest`` also discovers tests using the standard
-:ref:`unittest.TestCase <unittest.TestCase>` subclassing technique.
-
-
-Choosing a test layout / import rules
-------------------------------------------
-
-``pytest`` supports two common test layouts:
-
-* putting tests into an extra directory outside your actual application
- code, useful if you have many functional tests or for other reasons
- want to keep tests separate from actual application code (often a good
- idea)::
-
- setup.py # your setuptools Python package metadata
- mypkg/
- __init__.py
- appmodule.py
- tests/
- test_app.py
- ...
-
-
-* inlining test directories into your application package, useful if you
- have direct relation between (unit-)test and application modules and
- want to distribute your tests along with your application::
-
- setup.py # your setuptools Python package metadata
- mypkg/
- __init__.py
- appmodule.py
- ...
- test/
- test_app.py
- ...
-
-Important notes relating to both schemes:
-
-- **make sure that "mypkg" is importable**, for example by typing once::
-
- pip install -e . # install package using setup.py in editable mode
-
-- **avoid "__init__.py" files in your test directories**.
- This way your tests can run easily against an installed version
- of ``mypkg``, independently from the installed package if it contains
- the tests or not.
-
-- With inlined tests you might put ``__init__.py`` into test
- directories and make them installable as part of your application.
- Using the ``py.test --pyargs mypkg`` invocation pytest will
- discover where mypkg is installed and collect tests from there.
- With the "external" test you can still distribute tests but they
- will not be installed or become importable.
-
-Typically you can run tests by pointing to test directories or modules::
-
- py.test tests/test_app.py # for external test dirs
- py.test mypkg/test/test_app.py # for inlined test dirs
- py.test mypkg # run tests in all below test directories
- py.test # run all tests below current dir
- ...
-
-Because of the above ``editable install`` mode you can change your
-source code (both tests and the app) and rerun tests at will.
-Once you are done with your work, you can `use tox`_ to make sure
-that the package is really correct and tests pass in all
-required configurations.
-
-.. note::
-
- You can use Python3 namespace packages (PEP420) for your application
- but pytest will still perform `test package name`_ discovery based on the
- presence of ``__init__.py`` files. If you use one of the
- two recommended file system layouts above but leave away the ``__init__.py``
- files from your directories it should just work on Python3.3 and above. From
- "inlined tests", however, you will need to use absolute imports for
- getting at your application code.
-
-.. _`test package name`:
-
-.. note::
-
- If ``pytest`` finds a "a/b/test_module.py" test file while
- recursing into the filesystem it determines the import name
- as follows:
-
- * determine ``basedir``: this is the first "upward" (towards the root)
- directory not containing an ``__init__.py``. If e.g. both ``a``
- and ``b`` contain an ``__init__.py`` file then the parent directory
- of ``a`` will become the ``basedir``.
-
- * perform ``sys.path.insert(0, basedir)`` to make the test module
- importable under the fully qualified import name.
-
- * ``import a.b.test_module`` where the path is determined
- by converting path separators ``/`` into "." characters. This means
- you must follow the convention of having directory and file
- names map directly to the import names.
-
- The reason for this somewhat evolved importing technique is
- that in larger projects multiple test modules might import
- from each other and thus deriving a canonical import name helps
- to avoid surprises such as a test modules getting imported twice.
-
-
-.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
-.. _`buildout`: http://www.buildout.org/
-.. _pip: http://pypi.python.org/pypi/pip
-
-.. _`use tox`:
-
-Tox
-------
-
-For development, we recommend to use virtualenv_ environments and pip_
-for installing your application and any dependencies
-as well as the ``pytest`` package itself. This ensures your code and
-dependencies are isolated from the system Python installation.
-
-If you frequently release code and want to make sure that your actual
-package passes all tests you may want to look into `tox`_, the
-virtualenv test automation tool and its `pytest support
-<http://testrun.org/tox/latest/example/pytest.html>`_.
-Tox helps you to setup virtualenv environments with pre-defined
-dependencies and then executing a pre-configured test command with
-options. It will run tests against the installed package and not
-against your source code checkout, helping to detect packaging
-glitches.
-
-Continuous integration services such as Jenkins_ can make use of the
-``--junitxml=PATH`` option to create a JUnitXML file and generate reports.
-
-
-Integrating with setuptools / ``python setup.py test`` / ``pytest-runner``
---------------------------------------------------------------------------
-
-You can integrate test runs into your setuptools based project
-with the `pytest-runner <https://pypi.python.org/pypi/pytest-runner>`_ plugin.
-
-Add this to ``setup.py`` file:
-
-.. code-block:: python
-
- from setuptools import setup
-
- setup(
- #...,
- setup_requires=['pytest-runner', ...],
- tests_require=['pytest', ...],
- #...,
- )
-
-
-And create an alias into ``setup.cfg`` file:
-
-
-.. code-block:: ini
-
- [aliases]
- test=pytest
-
-If you now type::
-
- python setup.py test
-
-this will execute your tests using ``pytest-runner``. As this is a
-standalone version of ``pytest`` no prior installation whatsoever is
-required for calling the test command. You can also pass additional
-arguments to py.test such as your test directory or other
-options using ``--addopts``.
-
-
-Manual Integration
-^^^^^^^^^^^^^^^^^^
-
-If for some reason you don't want/can't use ``pytest-runner``, you can write
-your own setuptools Test command for invoking pytest.
-
-.. code-block:: python
-
- import sys
-
- from setuptools.command.test import test as TestCommand
-
-
- class PyTest(TestCommand):
- user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]
-
- def initialize_options(self):
- TestCommand.initialize_options(self)
- self.pytest_args = []
-
- def run_tests(self):
- #import here, cause outside the eggs aren't loaded
- import pytest
- errno = pytest.main(self.pytest_args)
- sys.exit(errno)
-
-
- setup(
- #...,
- tests_require=['pytest'],
- cmdclass = {'test': PyTest},
- )
-
-Now if you run::
-
- python setup.py test
-
-this will download ``pytest`` if needed and then run your tests
-as you would expect it to. You can pass a single string of arguments
-using the ``--pytest-args`` or ``-a`` command-line option. For example::
-
- python setup.py test -a "--durations=5"
-
-is equivalent to running ``py.test --durations=5``.
-
-
-.. _standalone:
-.. _`genscript method`:
-
-(deprecated) Create a pytest standalone script
------------------------------------------------
-
-.. deprecated:: 2.8
-
-.. note::
-
- ``genscript`` has been deprecated because:
-
- * It cannot support plugins, rendering its usefulness extremely limited;
- * Tooling has become much better since ``genscript`` was introduced;
- * It is possible to build a zipped ``pytest`` application without the
- shortcomings above.
-
- There's no planned version in which this command will be removed
- at the moment of this writing, but its use is discouraged for new
- applications.
-
-If you are a maintainer or application developer and want people
-who don't deal with python much to easily run tests you may generate
-a standalone ``pytest`` script::
-
- py.test --genscript=runtests.py
-
-This generates a ``runtests.py`` script which is a fully functional basic
-``pytest`` script, running unchanged under Python2 and Python3.
-You can tell people to download the script and then e.g. run it like this::
-
- python runtests.py
-
-
-.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/index.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/index.rst
deleted file mode 100644
index 04b4512da30..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/index.rst
+++ /dev/null
@@ -1,61 +0,0 @@
-
-.. _features:
-
-pytest: helps you write better programs
-=============================================
-
-**a mature full-featured Python testing tool**
-
- - runs on Posix/Windows, Python 2.6-3.5, PyPy and (possibly still) Jython-2.5.1
- - free and open source software, distributed under the terms of the :ref:`MIT license <license>`
- - **well tested** with more than a thousand tests against itself
- - **strict backward compatibility policy** for safe pytest upgrades
- - :ref:`comprehensive online <toc>` and `PDF documentation <pytest.pdf>`_
- - many :ref:`third party plugins <extplugins>` and :ref:`builtin helpers <pytest helpers>`,
- - used in :ref:`many small and large projects and organisations <projects>`
- - comes with many :ref:`tested examples <examples>`
-
-**provides easy no-boilerplate testing**
-
- - makes it :ref:`easy to get started <getstarted>`,
- has many :ref:`usage options <usage>`
- - :ref:`assert with the assert statement`
- - helpful :ref:`traceback and failing assertion reporting <tbreportdemo>`
- - :ref:`print debugging <printdebugging>` and :ref:`the
- capturing of standard output during test execution <captures>`
-
-**scales from simple unit to complex functional testing**
-
- - :ref:`modular parametrizeable fixtures <fixture>` (new in 2.3,
- continuously improved)
- - :ref:`parametrized test functions <parametrized test functions>`
- - :ref:`mark`
- - :ref:`skipping` (improved in 2.4)
- - :ref:`distribute tests to multiple CPUs <xdistcpu>` through :ref:`xdist plugin <xdist>`
- - :ref:`continuously re-run failing tests <looponfailing>`
- - :doc:`cache`
- - flexible :ref:`Python test discovery`
-
-**integrates with other testing methods and tools**:
-
- - multi-paradigm: pytest can run ``nose``, ``unittest`` and
- ``doctest`` style test suites, including running testcases made for
- Django and trial
- - supports :ref:`good integration practices <goodpractices>`
- - supports extended :ref:`xUnit style setup <xunitsetup>`
- - supports domain-specific :ref:`non-python tests`
- - supports generating `test coverage reports
- <https://pypi.python.org/pypi/pytest-cov>`_
- - supports :pep:`8` compliant coding styles in tests
-
-**extensive plugin and customization system**:
-
- - all collection, reporting, running aspects are delegated to hook functions
- - customizations can be per-directory, per-project or per PyPI released plugin
- - it is easy to add command line options or customize existing behaviour
- - :ref:`easy to write your own plugins <writing-plugins>`
-
-
-.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/license.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/license.rst
deleted file mode 100644
index 3fc1dad52fd..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/license.rst
+++ /dev/null
@@ -1,32 +0,0 @@
-.. _license:
-
-License
--------
-
-Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
-
-::
-
- The MIT License (MIT)
-
- Copyright (c) 2004-2016 Holger Krekel and others
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- of the Software, and to permit persons to whom the Software is furnished to do
- so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-
-.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/links.inc b/tests/wpt/web-platform-tests/tools/pytest/doc/en/links.inc
deleted file mode 100644
index 3d7863751e2..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/links.inc
+++ /dev/null
@@ -1,21 +0,0 @@
-
-.. _`skipping plugin`: plugin/skipping.html
-.. _`funcargs mechanism`: funcargs.html
-.. _`doctest.py`: http://docs.python.org/library/doctest.html
-.. _`xUnit style setup`: xunit_setup.html
-.. _`pytest_nose`: plugin/nose.html
-.. _`reStructured Text`: http://docutils.sourceforge.net
-.. _`Python debugger`: http://docs.python.org/lib/module-pdb.html
-.. _nose: https://nose.readthedocs.org/en/latest/
-.. _pytest: http://pypi.python.org/pypi/pytest
-.. _mercurial: http://mercurial.selenic.com/wiki/
-.. _`setuptools`: http://pypi.python.org/pypi/setuptools
-.. _`easy_install`:
-.. _`distribute docs`:
-.. _`distribute`: http://pypi.python.org/pypi/distribute
-.. _`pip`: http://pypi.python.org/pypi/pip
-.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
-.. _hudson: http://hudson-ci.org/
-.. _jenkins: http://jenkins-ci.org/
-.. _tox: http://testrun.org/tox
-.. _pylib: http://py.readthedocs.org/en/latest/
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/mark.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/mark.rst
deleted file mode 100644
index ab9546d317c..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/mark.rst
+++ /dev/null
@@ -1,40 +0,0 @@
-
-.. _mark:
-
-Marking test functions with attributes
-=================================================================
-
-.. currentmodule:: _pytest.mark
-
-By using the ``pytest.mark`` helper you can easily set
-metadata on your test functions. There are
-some builtin markers, for example:
-
-* :ref:`skipif <skipif>` - skip a test function if a certain condition is met
-* :ref:`xfail <xfail>` - produce an "expected failure" outcome if a certain
- condition is met
-* :ref:`parametrize <parametrizemark>` to perform multiple calls
- to the same test function.
-
-It's easy to create custom markers or to apply markers
-to whole test classes or modules. See :ref:`mark examples` for examples
-which also serve as documentation.
-
-.. note::
-
- Marks can only be applied to tests, having no effect on
- :ref:`fixtures <fixtures>`.
-
-
-API reference for mark related objects
-------------------------------------------------
-
-.. autoclass:: MarkGenerator
- :members:
-
-.. autoclass:: MarkDecorator
- :members:
-
-.. autoclass:: MarkInfo
- :members:
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/monkeypatch.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/monkeypatch.rst
deleted file mode 100644
index 4155a3a345b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/monkeypatch.rst
+++ /dev/null
@@ -1,82 +0,0 @@
-
-Monkeypatching/mocking modules and environments
-================================================================
-
-.. currentmodule:: _pytest.monkeypatch
-
-Sometimes tests need to invoke functionality which depends
-on global settings or which invokes code which cannot be easily
-tested such as network access. The ``monkeypatch`` function argument
-helps you to safely set/delete an attribute, dictionary item or
-environment variable or to modify ``sys.path`` for importing.
-See the `monkeypatch blog post`_ for some introduction material
-and a discussion of its motivation.
-
-.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
-
-Simple example: monkeypatching functions
----------------------------------------------------
-
-If you want to pretend that ``os.expanduser`` returns a certain
-directory, you can use the :py:meth:`monkeypatch.setattr` method to
-patch this function before calling into a function which uses it::
-
- # content of test_module.py
- import os.path
- def getssh(): # pseudo application code
- return os.path.join(os.path.expanduser("~admin"), '.ssh')
-
- def test_mytest(monkeypatch):
- def mockreturn(path):
- return '/abc'
- monkeypatch.setattr(os.path, 'expanduser', mockreturn)
- x = getssh()
- assert x == '/abc/.ssh'
-
-Here our test function monkeypatches ``os.path.expanduser`` and
-then calls into an function that calls it. After the test function
-finishes the ``os.path.expanduser`` modification will be undone.
-
-example: preventing "requests" from remote operations
-------------------------------------------------------
-
-If you want to prevent the "requests" library from performing http
-requests in all your tests, you can do::
-
- # content of conftest.py
- import pytest
- @pytest.fixture(autouse=True)
- def no_requests(monkeypatch):
- monkeypatch.delattr("requests.sessions.Session.request")
-
-This autouse fixture will be executed for each test function and it
-will delete the method ``request.session.Session.request``
-so that any attempts within tests to create http requests will fail.
-
-example: setting an attribute on some class
-------------------------------------------------------
-
-If you need to patch out ``os.getcwd()`` to return an artificial
-value::
-
- def test_some_interaction(monkeypatch):
- monkeypatch.setattr("os.getcwd", lambda: "/")
-
-which is equivalent to the long form::
-
- def test_some_interaction(monkeypatch):
- import os
- monkeypatch.setattr(os, "getcwd", lambda: "/")
-
-
-
-Method reference of the monkeypatch function argument
------------------------------------------------------
-
-.. autoclass:: monkeypatch
- :members: setattr, replace, delattr, setitem, delitem, setenv, delenv, syspath_prepend, chdir, undo
-
-``monkeypatch.setattr/delattr/delitem/delenv()`` all
-by default raise an Exception if the target does not exist.
-Pass ``raising=False`` if you want to skip this check.
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/nose.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/nose.rst
deleted file mode 100644
index 3b92e04cffd..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/nose.rst
+++ /dev/null
@@ -1,55 +0,0 @@
-Running tests written for nose
-=======================================
-
-.. include:: links.inc
-
-``pytest`` has basic support for running tests written for nose_.
-
-.. _nosestyle:
-
-Usage
--------------
-
-After :ref:`installation` type::
-
- python setup.py develop # make sure tests can import our package
- py.test # instead of 'nosetests'
-
-and you should be able to run your nose style tests and
-make use of pytest's capabilities.
-
-Supported nose Idioms
-----------------------
-
-* setup and teardown at module/class/method level
-* SkipTest exceptions and markers
-* setup/teardown decorators
-* yield-based tests and their setup
-* ``__test__`` attribute on modules/classes/functions
-* general usage of nose utilities
-
-Unsupported idioms / known issues
-----------------------------------
-
-- unittest-style ``setUp, tearDown, setUpClass, tearDownClass``
- are recognized only on ``unittest.TestCase`` classes but not
- on plain classes. ``nose`` supports these methods also on plain
- classes but pytest deliberately does not. As nose and pytest already
- both support ``setup_class, teardown_class, setup_method, teardown_method``
- it doesn't seem useful to duplicate the unittest-API like nose does.
- If you however rather think pytest should support the unittest-spelling on
- plain classes please post `to this issue
- <https://github.com/pytest-dev/pytest/issues/377/>`_.
-
-- nose imports test modules with the same import path (e.g.
- ``tests.test_mod``) but different file system paths
- (e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
- by extending sys.path/import semantics. pytest does not do that
- but there is discussion in `issue268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support. Note that
- `nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.org/en/latest/differences.html#test-discovery-and-loading>`_.
-
-- nose-style doctests are not collected and executed correctly,
- also doctest fixtures don't work.
-
-- no nose-configuration is recognized
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/overview.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/overview.rst
deleted file mode 100644
index eb261977514..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/overview.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-==================================================
-Getting started basics
-==================================================
-
-.. toctree::
- :maxdepth: 2
-
- getting-started
- usage
- goodpractices
- projects
- faq
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/parametrize.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/parametrize.rst
deleted file mode 100644
index 919ac93d25a..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/parametrize.rst
+++ /dev/null
@@ -1,219 +0,0 @@
-
-.. _`test generators`:
-.. _`parametrizing-tests`:
-.. _`parametrized test functions`:
-.. _`parametrize`:
-
-.. _`parametrize-basics`:
-
-Parametrizing fixtures and test functions
-==========================================================================
-
-pytest supports test parametrization in several well-integrated ways:
-
-- :py:func:`pytest.fixture` allows to define :ref:`parametrization
- at the level of fixture functions <fixture-parametrize>`.
-
-* `@pytest.mark.parametrize`_ allows to define parametrization at the
- function or class level, provides multiple argument/fixture sets
- for a particular test function or class.
-
-* `pytest_generate_tests`_ enables implementing your own custom
- dynamic parametrization scheme or extensions.
-
-.. _parametrizemark:
-.. _`@pytest.mark.parametrize`:
-
-
-``@pytest.mark.parametrize``: parametrizing test functions
----------------------------------------------------------------------
-
-.. regendoc: wipe
-
-.. versionadded:: 2.2
-.. versionchanged:: 2.4
- Several improvements.
-
-The builtin ``pytest.mark.parametrize`` decorator enables
-parametrization of arguments for a test function. Here is a typical example
-of a test function that implements checking that a certain input leads
-to an expected output::
-
- # content of test_expectation.py
- import pytest
- @pytest.mark.parametrize("test_input,expected", [
- ("3+5", 8),
- ("2+4", 6),
- ("6*9", 42),
- ])
- def test_eval(test_input, expected):
- assert eval(test_input) == expected
-
-Here, the ``@parametrize`` decorator defines three different ``(test_input,expected)``
-tuples so that the ``test_eval`` function will run three times using
-them in turn::
-
- $ py.test
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 3 items
-
- test_expectation.py ..F
-
- ======= FAILURES ========
- _______ test_eval[6*9-42] ________
-
- test_input = '6*9', expected = 42
-
- @pytest.mark.parametrize("test_input,expected", [
- ("3+5", 8),
- ("2+4", 6),
- ("6*9", 42),
- ])
- def test_eval(test_input, expected):
- > assert eval(test_input) == expected
- E assert 54 == 42
- E + where 54 = eval('6*9')
-
- test_expectation.py:8: AssertionError
- ======= 1 failed, 2 passed in 0.12 seconds ========
-
-As designed in this example, only one pair of input/output values fails
-the simple test function. And as usual with test function arguments,
-you can see the ``input`` and ``output`` values in the traceback.
-
-Note that you could also use the parametrize marker on a class or a module
-(see :ref:`mark`) which would invoke several functions with the argument sets.
-
-It is also possible to mark individual test instances within parametrize,
-for example with the builtin ``mark.xfail``::
-
- # content of test_expectation.py
- import pytest
- @pytest.mark.parametrize("test_input,expected", [
- ("3+5", 8),
- ("2+4", 6),
- pytest.mark.xfail(("6*9", 42)),
- ])
- def test_eval(test_input, expected):
- assert eval(test_input) == expected
-
-Let's run this::
-
- $ py.test
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 3 items
-
- test_expectation.py ..x
-
- ======= 2 passed, 1 xfailed in 0.12 seconds ========
-
-The one parameter set which caused a failure previously now
-shows up as an "xfailed (expected to fail)" test.
-
-To get all combinations of multiple parametrized arguments you can stack
-``parametrize`` decorators::
-
- import pytest
- @pytest.mark.parametrize("x", [0, 1])
- @pytest.mark.parametrize("y", [2, 3])
- def test_foo(x, y):
- pass
-
-This will run the test with the arguments set to x=0/y=2, x=0/y=3, x=1/y=2 and
-x=1/y=3.
-
-.. note::
-
- In versions prior to 2.4 one needed to specify the argument
- names as a tuple. This remains valid but the simpler ``"name1,name2,..."``
- comma-separated-string syntax is now advertised first because
- it's easier to write and produces less line noise.
-
-.. _`pytest_generate_tests`:
-
-Basic ``pytest_generate_tests`` example
----------------------------------------------
-
-Sometimes you may want to implement your own parametrization scheme
-or implement some dynamism for determining the parameters or scope
-of a fixture. For this, you can use the ``pytest_generate_tests`` hook
-which is called when collecting a test function. Through the passed in
-``metafunc`` object you can inspect the requesting test context and, most
-importantly, you can call ``metafunc.parametrize()`` to cause
-parametrization.
-
-For example, let's say we want to run a test taking string inputs which
-we want to set via a new ``pytest`` command line option. Let's first write
-a simple test accepting a ``stringinput`` fixture function argument::
-
- # content of test_strings.py
-
- def test_valid_string(stringinput):
- assert stringinput.isalpha()
-
-Now we add a ``conftest.py`` file containing the addition of a
-command line option and the parametrization of our test function::
-
- # content of conftest.py
-
- def pytest_addoption(parser):
- parser.addoption("--stringinput", action="append", default=[],
- help="list of stringinputs to pass to test functions")
-
- def pytest_generate_tests(metafunc):
- if 'stringinput' in metafunc.fixturenames:
- metafunc.parametrize("stringinput",
- metafunc.config.option.stringinput)
-
-If we now pass two stringinput values, our test will run twice::
-
- $ py.test -q --stringinput="hello" --stringinput="world" test_strings.py
- ..
- 2 passed in 0.12 seconds
-
-Let's also run with a stringinput that will lead to a failing test::
-
- $ py.test -q --stringinput="!" test_strings.py
- F
- ======= FAILURES ========
- _______ test_valid_string[!] ________
-
- stringinput = '!'
-
- def test_valid_string(stringinput):
- > assert stringinput.isalpha()
- E assert <built-in method isalpha of str object at 0xdeadbeef>()
- E + where <built-in method isalpha of str object at 0xdeadbeef> = '!'.isalpha
-
- test_strings.py:3: AssertionError
- 1 failed in 0.12 seconds
-
-As expected our test function fails.
-
-If you don't specify a stringinput it will be skipped because
-``metafunc.parametrize()`` will be called with an empty parameter
-list::
-
- $ py.test -q -rs test_strings.py
- s
- ======= short test summary info ========
- SKIP [1] $PYTHON_PREFIX/lib/python3.4/site-packages/_pytest/python.py:1419: got empty parameter set, function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:1
- 1 skipped in 0.12 seconds
-
-For further examples, you might want to look at :ref:`more
-parametrization examples <paramexamples>`.
-
-.. _`metafunc object`:
-
-The **metafunc** object
--------------------------------------------
-
-.. currentmodule:: _pytest.python
-.. autoclass:: Metafunc
-
- .. automethod:: Metafunc.parametrize
- .. automethod:: Metafunc.addcall(funcargs=None,id=_notexists,param=_notexists)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/plugins.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/plugins.rst
deleted file mode 100644
index e9b3f460cc4..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/plugins.rst
+++ /dev/null
@@ -1,159 +0,0 @@
-.. _`external plugins`:
-.. _`extplugins`:
-.. _`using plugins`:
-
-Installing and Using plugins
-============================
-
-This section talks about installing and using third party plugins.
-For writing your own plugins, please refer to :ref:`writing-plugins`.
-
-Installing a third party plugin can be easily done with ``pip``::
-
- pip install pytest-NAME
- pip uninstall pytest-NAME
-
-If a plugin is installed, ``pytest`` automatically finds and integrates it,
-there is no need to activate it.
-
-Here is a little annotated list for some popular plugins:
-
-.. _`django`: https://www.djangoproject.com/
-
-* `pytest-django <http://pypi.python.org/pypi/pytest-django>`_: write tests
- for `django`_ apps, using pytest integration.
-
-* `pytest-twisted <http://pypi.python.org/pypi/pytest-twisted>`_: write tests
- for `twisted <http://twistedmatrix.com>`_ apps, starting a reactor and
- processing deferreds from test functions.
-
-* `pytest-catchlog <http://pypi.python.org/pypi/pytest-catchlog>`_:
- to capture and assert about messages from the logging module
-
-* `pytest-cov <http://pypi.python.org/pypi/pytest-cov>`_:
- coverage reporting, compatible with distributed testing
-
-* `pytest-xdist <http://pypi.python.org/pypi/pytest-xdist>`_:
- to distribute tests to CPUs and remote hosts, to run in boxed
- mode which allows to survive segmentation faults, to run in
- looponfailing mode, automatically re-running failing tests
- on file changes, see also :ref:`xdist`
-
-* `pytest-instafail <http://pypi.python.org/pypi/pytest-instafail>`_:
- to report failures while the test run is happening.
-
-* `pytest-bdd <http://pypi.python.org/pypi/pytest-bdd>`_ and
- `pytest-konira <http://pypi.python.org/pypi/pytest-konira>`_
- to write tests using behaviour-driven testing.
-
-* `pytest-timeout <http://pypi.python.org/pypi/pytest-timeout>`_:
- to timeout tests based on function marks or global definitions.
-
-* `pytest-pep8 <http://pypi.python.org/pypi/pytest-pep8>`_:
- a ``--pep8`` option to enable PEP8 compliance checking.
-
-* `pytest-flakes <https://pypi.python.org/pypi/pytest-flakes>`_:
- check source code with pyflakes.
-
-* `oejskit <http://pypi.python.org/pypi/oejskit>`_:
- a plugin to run javascript unittests in live browsers.
-
-To see a complete list of all plugins with their latest testing
-status against different py.test and Python versions, please visit
-`plugincompat <http://plugincompat.herokuapp.com/>`_.
-
-You may also discover more plugins through a `pytest- pypi.python.org search`_.
-
-.. _`available installable plugins`:
-.. _`pytest- pypi.python.org search`: http://pypi.python.org/pypi?%3Aaction=search&term=pytest-&submit=search
-
-
-Requiring/Loading plugins in a test module or conftest file
------------------------------------------------------------
-
-You can require plugins in a test module or a conftest file like this::
-
- pytest_plugins = "myapp.testsupport.myplugin",
-
-When the test module or conftest plugin is loaded the specified plugins
-will be loaded as well.
-
- pytest_plugins = "myapp.testsupport.myplugin"
-
-which will import the specified module as a ``pytest`` plugin.
-
-.. _`findpluginname`:
-
-Finding out which plugins are active
-------------------------------------
-
-If you want to find out which plugins are active in your
-environment you can type::
-
- py.test --traceconfig
-
-and will get an extended test header which shows activated plugins
-and their names. It will also print local plugins aka
-:ref:`conftest.py <conftest>` files when they are loaded.
-
-.. _`cmdunregister`:
-
-Deactivating / unregistering a plugin by name
----------------------------------------------
-
-You can prevent plugins from loading or unregister them::
-
- py.test -p no:NAME
-
-This means that any subsequent try to activate/load the named
-plugin will not work.
-
-If you want to unconditionally disable a plugin for a project, you can add
-this option to your ``pytest.ini`` file:
-
-.. code-block:: ini
-
- [pytest]
- addopts = -p no:NAME
-
-Alternatively to disable it only in certain environments (for example in a
-CI server), you can set ``PYTEST_ADDOPTS`` environment variable to
-``-p no:name``.
-
-See :ref:`findpluginname` for how to obtain the name of a plugin.
-
-.. _`builtin plugins`:
-
-Pytest default plugin reference
--------------------------------
-
-
-You can find the source code for the following plugins
-in the `pytest repository <https://github.com/pytest-dev/pytest>`_.
-
-.. autosummary::
-
- _pytest.assertion
- _pytest.cacheprovider
- _pytest.capture
- _pytest.config
- _pytest.doctest
- _pytest.genscript
- _pytest.helpconfig
- _pytest.junitxml
- _pytest.mark
- _pytest.monkeypatch
- _pytest.nose
- _pytest.pastebin
- _pytest.pdb
- _pytest.pytester
- _pytest.python
- _pytest.recwarn
- _pytest.resultlog
- _pytest.runner
- _pytest.main
- _pytest.skipping
- _pytest.terminal
- _pytest.tmpdir
- _pytest.unittest
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/projects.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/projects.rst
deleted file mode 100644
index 76d004916ce..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/projects.rst
+++ /dev/null
@@ -1,85 +0,0 @@
-.. _projects:
-
-.. image:: img/gaynor3.png
- :width: 400px
- :align: right
-
-.. image:: img/theuni.png
- :width: 400px
- :align: right
-
-.. image:: img/cramer2.png
- :width: 400px
- :align: right
-
-.. image:: img/keleshev.png
- :width: 400px
- :align: right
-
-
-Project examples
-==========================
-
-Here are some examples of projects using ``pytest`` (please send notes via :ref:`contact`):
-
-* `PyPy <http://pypy.org>`_, Python with a JIT compiler, running over
- `21000 tests <http://buildbot.pypy.org/summary?branch=%3Ctrunk%3E>`_
-* the `MoinMoin <http://moinmo.in>`_ Wiki Engine
-* `sentry <https://getsentry.com/welcome/>`_, realtime app-maintenance and exception tracking
-* `Astropy <http://www.astropy.org/>`_ and `affiliated packages <http://www.astropy.org/affiliated/index.html>`_
-* `tox <http://testrun.org/tox>`_, virtualenv/Hudson integration tool
-* `PIDA <http://pida.co.uk>`_ framework for integrated development
-* `PyPM <http://code.activestate.com/pypm/>`_ ActiveState's package manager
-* `Fom <http://packages.python.org/Fom/>`_ a fluid object mapper for FluidDB
-* `applib <https://github.com/ActiveState/applib>`_ cross-platform utilities
-* `six <http://pypi.python.org/pypi/six/>`_ Python 2 and 3 compatibility utilities
-* `pediapress <http://code.pediapress.com/wiki/wiki>`_ MediaWiki articles
-* `mwlib <http://pypi.python.org/pypi/mwlib>`_ mediawiki parser and utility library
-* `The Translate Toolkit <http://translate.sourceforge.net/wiki/toolkit/index>`_ for localization and conversion
-* `execnet <http://codespeak.net/execnet>`_ rapid multi-Python deployment
-* `pylib <http://py.rtfd.org>`_ cross-platform path, IO, dynamic code library
-* `Pacha <http://pacha.cafepais.com/>`_ configuration management in five minutes
-* `bbfreeze <http://pypi.python.org/pypi/bbfreeze>`_ create standalone executables from Python scripts
-* `pdb++ <http://bitbucket.org/antocuni/pdb>`_ a fancier version of PDB
-* `py-s3fuse <http://code.google.com/p/py-s3fuse/>`_ Amazon S3 FUSE based filesystem
-* `waskr <http://code.google.com/p/waskr/>`_ WSGI Stats Middleware
-* `guachi <http://code.google.com/p/guachi/>`_ global persistent configs for Python modules
-* `Circuits <http://pypi.python.org/pypi/circuits>`_ lightweight Event Driven Framework
-* `pygtk-helpers <http://bitbucket.org/aafshar/pygtkhelpers-main/>`_ easy interaction with PyGTK
-* `QuantumCore <http://quantumcore.org/>`_ statusmessage and repoze openid plugin
-* `pydataportability <http://pydataportability.net/>`_ libraries for managing the open web
-* `XIST <http://www.livinglogic.de/Python/xist/>`_ extensible HTML/XML generator
-* `tiddlyweb <http://pypi.python.org/pypi/tiddlyweb>`_ optionally headless, extensible RESTful datastore
-* `fancycompleter <http://bitbucket.org/antocuni/fancycompleter/src>`_ for colorful tab-completion
-* `Paludis <http://paludis.exherbo.org/>`_ tools for Gentoo Paludis package manager
-* `Gerald <http://halfcooked.com/code/gerald/>`_ schema comparison tool
-* `abjad <http://code.google.com/p/abjad/>`_ Python API for Formalized Score control
-* `bu <http://packages.python.org/bu/>`_ a microscopic build system
-* `katcp <https://bitbucket.org/hodgestar/katcp>`_ Telescope communication protocol over Twisted
-* `kss plugin timer <http://pypi.python.org/pypi/kss.plugin.timer>`_
-* `pyudev <http://pyudev.readthedocs.org/en/latest/tests/plugins.html>`_ a pure Python binding to the Linux library libudev
-* `pytest-localserver <https://bitbucket.org/basti/pytest-localserver/>`_ a plugin for pytest that provides a httpserver and smtpserver
-* `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus/>`_ a plugin that extends monkeypatch
-
-These projects help integrate ``pytest`` into other Python frameworks:
-
-* `pytest-django <http://pypi.python.org/pypi/pytest-django/>`_ for Django
-* `zope.pytest <http://packages.python.org/zope.pytest/>`_ for Zope and Grok
-* `pytest_gae <http://pypi.python.org/pypi/pytest_gae/0.2.1>`_ for Google App Engine
-* There is `some work <https://github.com/Kotti/Kotti/blob/master/kotti/testing.py>`_ underway for Kotti, a CMS built in Pyramid/Pylons
-
-
-Some organisations using pytest
------------------------------------
-
-* `Square Kilometre Array, Cape Town <http://ska.ac.za/>`_
-* `Some Mozilla QA people <http://www.theautomatedtester.co.uk/blog/2011/pytest_and_xdist_plugin.html>`_ use pytest to distribute their Selenium tests
-* `Tandberg <http://www.tandberg.com/>`_
-* `Shootq <http://web.shootq.com/>`_
-* `Stups department of Heinrich Heine University Duesseldorf <http://www.stups.uni-duesseldorf.de/projects.php>`_
-* `cellzome <http://www.cellzome.com/>`_
-* `Open End, Gothenborg <http://www.openend.se>`_
-* `Laboratory of Bioinformatics, Warsaw <http://genesilico.pl/>`_
-* `merlinux, Germany <http://merlinux.eu>`_
-* `ESSS, Brazil <http://www.esss.com.br>`_
-* many more ... (please be so kind to send a note via :ref:`contact`)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/recwarn.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/recwarn.rst
deleted file mode 100644
index 3c42bfaaf94..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/recwarn.rst
+++ /dev/null
@@ -1,130 +0,0 @@
-
-Asserting Warnings
-=====================================================
-
-.. _warns:
-
-Asserting warnings with the warns function
------------------------------------------------
-
-.. versionadded:: 2.8
-
-You can check that code raises a particular warning using ``pytest.warns``,
-which works in a similar manner to :ref:`raises <assertraises>`::
-
- import warnings
- import pytest
-
- def test_warning():
- with pytest.warns(UserWarning):
- warnings.warn("my warning", UserWarning)
-
-The test will fail if the warning in question is not raised.
-
-You can also call ``pytest.warns`` on a function or code string::
-
- pytest.warns(expected_warning, func, *args, **kwargs)
- pytest.warns(expected_warning, "func(*args, **kwargs)")
-
-The function also returns a list of all raised warnings (as
-``warnings.WarningMessage`` objects), which you can query for
-additional information::
-
- with pytest.warns(RuntimeWarning) as record:
- warnings.warn("another warning", RuntimeWarning)
-
- # check that only one warning was raised
- assert len(record) == 1
- # check that the message matches
- assert record[0].message.args[0] == "another warning"
-
-Alternatively, you can examine raised warnings in detail using the
-:ref:`recwarn <recwarn>` fixture (see below).
-
-.. note::
- ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
- differently; see :ref:`ensuring_function_triggers`.
-
-.. _recwarn:
-
-Recording warnings
-------------------------
-
-You can record raised warnings either using ``pytest.warns`` or with
-the ``recwarn`` fixture.
-
-To record with ``pytest.warns`` without asserting anything about the warnings,
-pass ``None`` as the expected warning type::
-
- with pytest.warns(None) as record:
- warnings.warn("user", UserWarning)
- warnings.warn("runtime", RuntimeWarning)
-
- assert len(record) == 2
- assert str(record[0].message) == "user"
- assert str(record[1].message) == "runtime"
-
-The ``recwarn`` fixture will record warnings for the whole function::
-
- import warnings
-
- def test_hello(recwarn):
- warnings.warn("hello", UserWarning)
- assert len(recwarn) == 1
- w = recwarn.pop(UserWarning)
- assert issubclass(w.category, UserWarning)
- assert str(w.message) == "hello"
- assert w.filename
- assert w.lineno
-
-Both ``recwarn`` and ``pytest.warns`` return the same interface for recorded
-warnings: a WarningsRecorder instance. To view the recorded warnings, you can
-iterate over this instance, call ``len`` on it to get the number of recorded
-warnings, or index into it to get a particular recorded warning. It also
-provides these methods:
-
-.. autoclass:: _pytest.recwarn.WarningsRecorder()
- :members:
-
-Each recorded warning has the attributes ``message``, ``category``,
-``filename``, ``lineno``, ``file``, and ``line``. The ``category`` is the
-class of the warning. The ``message`` is the warning itself; calling
-``str(message)`` will return the actual message of the warning.
-
-.. note::
- ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
- differently; see :ref:`ensuring_function_triggers`.
-
-.. _ensuring_function_triggers:
-
-Ensuring a function triggers a deprecation warning
--------------------------------------------------------
-
-You can also call a global helper for checking
-that a certain function call triggers a ``DeprecationWarning`` or
-``PendingDeprecationWarning``::
-
- import pytest
-
- def test_global():
- pytest.deprecated_call(myfunction, 17)
-
-By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be
-caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide
-them. If you wish to record them in your own code, use the
-command ``warnings.simplefilter('always')``::
-
- import warnings
- import pytest
-
- def test_deprecation(recwarn):
- warnings.simplefilter('always')
- warnings.warn("deprecated", DeprecationWarning)
- assert len(recwarn) == 1
- assert recwarn.pop(DeprecationWarning)
-
-You can also use it as a contextmanager::
-
- def test_global():
- with pytest.deprecated_call():
- myobject.deprecated_method()
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/setup.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/setup.rst
deleted file mode 100644
index fe235346544..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/setup.rst
+++ /dev/null
@@ -1,10 +0,0 @@
-
-setup: is now an "autouse fixture"
-========================================================
-
-During development prior to the pytest-2.3 release the name
-``pytest.setup`` was used but before the release it was renamed
-and moved to become part of the general fixture mechanism,
-namely :ref:`autouse fixtures`
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/skipping.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/skipping.rst
deleted file mode 100644
index 4282afb77ec..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/skipping.rst
+++ /dev/null
@@ -1,373 +0,0 @@
-.. _`skip and xfail`:
-
-.. _skipping:
-
-Skip and xfail: dealing with tests that can not succeed
-=====================================================================
-
-If you have test functions that cannot be run on certain platforms
-or that you expect to fail you can mark them accordingly or you
-may call helper functions during execution of setup or test functions.
-
-A *skip* means that you expect your test to pass unless the environment
-(e.g. wrong Python interpreter, missing dependency) prevents it to run.
-And *xfail* means that your test can run but you expect it to fail
-because there is an implementation problem.
-
-``pytest`` counts and lists *skip* and *xfail* tests separately. Detailed
-information about skipped/xfailed tests is not shown by default to avoid
-cluttering the output. You can use the ``-r`` option to see details
-corresponding to the "short" letters shown in the test progress::
-
- py.test -rxs # show extra info on skips and xfails
-
-(See :ref:`how to change command line options defaults`)
-
-.. _skipif:
-.. _`condition booleans`:
-
-Marking a test function to be skipped
--------------------------------------------
-
-.. versionadded:: 2.9
-
-The simplest way to skip a test function is to mark it with the ``skip`` decorator
-which may be passed an optional ``reason``:
-
-.. code-block:: python
-
- @pytest.mark.skip(reason="no way of currently testing this")
- def test_the_unknown():
- ...
-
-``skipif``
-~~~~~~~~~~
-
-.. versionadded:: 2.0, 2.4
-
-If you wish to skip something conditionally then you can use ``skipif`` instead.
-Here is an example of marking a test function to be skipped
-when run on a Python3.3 interpreter::
-
- import sys
- @pytest.mark.skipif(sys.version_info < (3,3),
- reason="requires python3.3")
- def test_function():
- ...
-
-During test function setup the condition ("sys.version_info >= (3,3)") is
-checked. If it evaluates to True, the test function will be skipped
-with the specified reason. Note that pytest enforces specifying a reason
-in order to report meaningful "skip reasons" (e.g. when using ``-rs``).
-If the condition is a string, it will be evaluated as python expression.
-
-You can share skipif markers between modules. Consider this test module::
-
- # content of test_mymodule.py
-
- import mymodule
- minversion = pytest.mark.skipif(mymodule.__versioninfo__ < (1,1),
- reason="at least mymodule-1.1 required")
- @minversion
- def test_function():
- ...
-
-You can import it from another test module::
-
- # test_myothermodule.py
- from test_mymodule import minversion
-
- @minversion
- def test_anotherfunction():
- ...
-
-For larger test suites it's usually a good idea to have one file
-where you define the markers which you then consistently apply
-throughout your test suite.
-
-Alternatively, the pre pytest-2.4 way to specify :ref:`condition strings
-<string conditions>` instead of booleans will remain fully supported in future
-versions of pytest. It couldn't be easily used for importing markers
-between test modules so it's no longer advertised as the primary method.
-
-
-Skip all test functions of a class or module
----------------------------------------------
-
-You can use the ``skipif`` decorator (and any other marker) on classes::
-
- @pytest.mark.skipif(sys.platform == 'win32',
- reason="does not run on windows")
- class TestPosixCalls:
-
- def test_function(self):
- "will not be setup or run under 'win32' platform"
-
-If the condition is true, this marker will produce a skip result for
-each of the test methods.
-
-If you want to skip all test functions of a module, you must use
-the ``pytestmark`` name on the global level:
-
-.. code-block:: python
-
- # test_module.py
- pytestmark = pytest.mark.skipif(...)
-
-If multiple "skipif" decorators are applied to a test function, it
-will be skipped if any of the skip conditions is true.
-
-.. _`whole class- or module level`: mark.html#scoped-marking
-
-.. _xfail:
-
-Mark a test function as expected to fail
--------------------------------------------------------
-
-You can use the ``xfail`` marker to indicate that you
-expect a test to fail::
-
- @pytest.mark.xfail
- def test_function():
- ...
-
-This test will be run but no traceback will be reported
-when it fails. Instead terminal reporting will list it in the
-"expected to fail" (``XFAIL``) or "unexpectedly passing" (``XPASS``) sections.
-
-``strict`` parameter
-~~~~~~~~~~~~~~~~~~~~
-
-.. versionadded:: 2.9
-
-Both ``XFAIL`` and ``XPASS`` don't fail the test suite, unless the ``strict`` keyword-only
-parameter is passed as ``True``:
-
-.. code-block:: python
-
- @pytest.mark.xfail(strict=True)
- def test_function():
- ...
-
-
-This will make ``XPASS`` ("unexpectedly passing") results from this test to fail the test suite.
-
-You can change the default value of the ``strict`` parameter using the
-``xfail_strict`` ini option:
-
-.. code-block:: ini
-
- [pytest]
- xfail_strict=true
-
-
-``reason`` parameter
-~~~~~~~~~~~~~~~~~~~~
-
-As with skipif_ you can also mark your expectation of a failure
-on a particular platform::
-
- @pytest.mark.xfail(sys.version_info >= (3,3),
- reason="python3.3 api changes")
- def test_function():
- ...
-
-
-``raises`` parameter
-~~~~~~~~~~~~~~~~~~~~
-
-If you want to be more specific as to why the test is failing, you can specify
-a single exception, or a list of exceptions, in the ``raises`` argument.
-
-.. code-block:: python
-
- @pytest.mark.xfail(raises=RuntimeError)
- def test_function():
- ...
-
-Then the test will be reported as a regular failure if it fails with an
-exception not mentioned in ``raises``.
-
-``run`` parameter
-~~~~~~~~~~~~~~~~~
-
-If a test should be marked as xfail and reported as such but should not be
-even executed, use the ``run`` parameter as ``False``:
-
-.. code-block:: python
-
- @pytest.mark.xfail(run=False)
- def test_function():
- ...
-
-This is specially useful for marking crashing tests for later inspection.
-
-
-Ignoring xfail marks
-~~~~~~~~~~~~~~~~~~~~
-
-By specifying on the commandline::
-
- pytest --runxfail
-
-you can force the running and reporting of an ``xfail`` marked test
-as if it weren't marked at all.
-
-Examples
-~~~~~~~~
-
-Here is a simple test file with the several usages:
-
-.. literalinclude:: example/xfail_demo.py
-
-Running it with the report-on-xfail option gives this output::
-
- example $ py.test -rx xfail_demo.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR/example, inifile:
- collected 7 items
-
- xfail_demo.py xxxxxxx
- ======= short test summary info ========
- XFAIL xfail_demo.py::test_hello
- XFAIL xfail_demo.py::test_hello2
- reason: [NOTRUN]
- XFAIL xfail_demo.py::test_hello3
- condition: hasattr(os, 'sep')
- XFAIL xfail_demo.py::test_hello4
- bug 110
- XFAIL xfail_demo.py::test_hello5
- condition: pytest.__version__[0] != "17"
- XFAIL xfail_demo.py::test_hello6
- reason: reason
- XFAIL xfail_demo.py::test_hello7
-
- ======= 7 xfailed in 0.12 seconds ========
-
-xfail signature summary
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Here's the signature of the ``xfail`` marker, using Python 3 keyword-only
-arguments syntax:
-
-.. code-block:: python
-
- def xfail(condition=None, *, reason=None, raises=None, run=True, strict=False):
-
-
-
-.. _`skip/xfail with parametrize`:
-
-Skip/xfail with parametrize
----------------------------
-
-It is possible to apply markers like skip and xfail to individual
-test instances when using parametrize::
-
- import pytest
-
- @pytest.mark.parametrize(("n", "expected"), [
- (1, 2),
- pytest.mark.xfail((1, 0)),
- pytest.mark.xfail(reason="some bug")((1, 3)),
- (2, 3),
- (3, 4),
- (4, 5),
- pytest.mark.skipif("sys.version_info >= (3,0)")((10, 11)),
- ])
- def test_increment(n, expected):
- assert n + 1 == expected
-
-
-Imperative xfail from within a test or setup function
-------------------------------------------------------
-
-If you cannot declare xfail- of skipif conditions at import
-time you can also imperatively produce an according outcome
-imperatively, in test or setup code::
-
- def test_function():
- if not valid_config():
- pytest.xfail("failing configuration (but should work)")
- # or
- pytest.skip("unsupported configuration")
-
-
-Skipping on a missing import dependency
---------------------------------------------------
-
-You can use the following import helper at module level
-or within a test or test setup function::
-
- docutils = pytest.importorskip("docutils")
-
-If ``docutils`` cannot be imported here, this will lead to a
-skip outcome of the test. You can also skip based on the
-version number of a library::
-
- docutils = pytest.importorskip("docutils", minversion="0.3")
-
-The version will be read from the specified
-module's ``__version__`` attribute.
-
-
-.. _string conditions:
-
-specifying conditions as strings versus booleans
-----------------------------------------------------------
-
-Prior to pytest-2.4 the only way to specify skipif/xfail conditions was
-to use strings::
-
- import sys
- @pytest.mark.skipif("sys.version_info >= (3,3)")
- def test_function():
- ...
-
-During test function setup the skipif condition is evaluated by calling
-``eval('sys.version_info >= (3,0)', namespace)``. The namespace contains
-all the module globals, and ``os`` and ``sys`` as a minimum.
-
-Since pytest-2.4 `condition booleans`_ are considered preferable
-because markers can then be freely imported between test modules.
-With strings you need to import not only the marker but all variables
-everything used by the marker, which violates encapsulation.
-
-The reason for specifying the condition as a string was that ``pytest`` can
-report a summary of skip conditions based purely on the condition string.
-With conditions as booleans you are required to specify a ``reason`` string.
-
-Note that string conditions will remain fully supported and you are free
-to use them if you have no need for cross-importing markers.
-
-The evaluation of a condition string in ``pytest.mark.skipif(conditionstring)``
-or ``pytest.mark.xfail(conditionstring)`` takes place in a namespace
-dictionary which is constructed as follows:
-
-* the namespace is initialized by putting the ``sys`` and ``os`` modules
- and the pytest ``config`` object into it.
-
-* updated with the module globals of the test function for which the
- expression is applied.
-
-The pytest ``config`` object allows you to skip based on a test
-configuration value which you might have added::
-
- @pytest.mark.skipif("not config.getvalue('db')")
- def test_function(...):
- ...
-
-The equivalent with "boolean conditions" is::
-
- @pytest.mark.skipif(not pytest.config.getvalue("db"),
- reason="--db was not specified")
- def test_function(...):
- pass
-
-.. note::
-
- You cannot use ``pytest.config.getvalue()`` in code
- imported before py.test's argument parsing takes place. For example,
- ``conftest.py`` files are imported before command line parsing and thus
- ``config.getvalue()`` will not execute correctly.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/status.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/status.rst
deleted file mode 100644
index 3c7bf70eaa4..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/status.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-pytest development status
-================================
-
-https://travis-ci.org/pytest-dev/pytest
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/talks.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/talks.rst
deleted file mode 100644
index 7a5221845ce..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/talks.rst
+++ /dev/null
@@ -1,116 +0,0 @@
-
-Talks and Tutorials
-==========================
-
-.. sidebar:: Next Open Trainings
-
- `professional testing with pytest and tox <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, 27-29th June 2016, Freiburg, Germany
-
-.. _`funcargs`: funcargs.html
-
-Talks and blog postings
----------------------------------------------
-
-.. _`tutorial1 repository`: http://bitbucket.org/pytest-dev/pytest-tutorial1/
-.. _`pycon 2010 tutorial PDF`: http://bitbucket.org/pytest-dev/pytest-tutorial1/raw/tip/pytest-basic.pdf
-
-- `pytest - Rapid Simple Testing, Florian Bruhin, Swiss Python Summit 2016
- <https://www.youtube.com/watch?v=rCBHkQ_LVIs>`_.
-
-- `Improve your testing with Pytest and Mock, Gabe Hollombe, PyCon SG 2015
- <https://www.youtube.com/watch?v=RcN26hznmk4>`_.
-
-- `Introduction to pytest, Andreas Pelme, EuroPython 2014
- <https://www.youtube.com/watch?v=LdVJj65ikRY>`_.
-
-- `Advanced Uses of py.test Fixtures, Floris Bruynooghe, EuroPython
- 2014 <https://www.youtube.com/watch?v=IBC_dxr-4ps>`_.
-
-- `Why i use py.test and maybe you should too, Andy Todd, Pycon AU 2013
- <https://www.youtube.com/watch?v=P-AhpukDIik>`_
-
-- `3-part blog series about pytest from @pydanny alias Daniel Greenfeld (January
- 2014) <http://pydanny.com/pytest-no-boilerplate-testing.html>`_
-
-- `pytest: helps you write better Django apps, Andreas Pelme, DjangoCon
- Europe 2014 <https://www.youtube.com/watch?v=aaArYVh6XSM>`_.
-
-- :ref:`fixtures`
-
-- `Testing Django Applications with pytest, Andreas Pelme, EuroPython
- 2013 <https://www.youtube.com/watch?v=aUf8Fkb7TaY>`_.
-
-- `Testes pythonics com py.test, Vinicius Belchior Assef Neto, Plone
- Conf 2013, Brazil <https://www.youtube.com/watch?v=QUKoq2K7bis>`_.
-
-- `Introduction to py.test fixtures, FOSDEM 2013, Floris Bruynooghe
- <https://www.youtube.com/watch?v=bJhRW4eZMco>`_.
-
-- `pytest feature and release highlights, Holger Krekel (GERMAN, October 2013)
- <http://pyvideo.org/video/2429/pytest-feature-and-new-release-highlights>`_
-
-- `pytest introduction from Brian Okken (January 2013)
- <http://pythontesting.net/framework/pytest-introduction/>`_
-
-- `monkey patching done right`_ (blog post, consult `monkeypatch
- plugin`_ for up-to-date API)
-
-Test parametrization:
-
-- `generating parametrized tests with funcargs`_ (uses deprecated ``addcall()`` API.
-- `test generators and cached setup`_
-- `parametrizing tests, generalized`_ (blog post)
-- `putting test-hooks into local or global plugins`_ (blog post)
-
-Assertion introspection:
-
-- `(07/2011) Behind the scenes of pytest's new assertion rewriting
- <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_
-
-Distributed testing:
-
-- `simultaneously test your code on all platforms`_ (blog entry)
-
-Plugin specific examples:
-
-- `skipping slow tests by default in pytest`_ (blog entry)
-
-- `many examples in the docs for plugins`_
-
-.. _`skipping slow tests by default in pytest`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
-.. _`many examples in the docs for plugins`: plugin/index.html
-.. _`monkeypatch plugin`: plugin/monkeypatch.html
-.. _`application setup in test functions with funcargs`: funcargs.html#appsetup
-.. _`simultaneously test your code on all platforms`: http://tetamap.wordpress.com/2009/03/23/new-simultanously-test-your-code-on-all-platforms/
-.. _`monkey patching done right`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
-.. _`putting test-hooks into local or global plugins`: http://tetamap.wordpress.com/2009/05/14/putting-test-hooks-into-local-and-global-plugins/
-.. _`parametrizing tests, generalized`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
-.. _`generating parametrized tests with funcargs`: funcargs.html#test-generators
-.. _`test generators and cached setup`: http://bruynooghe.blogspot.com/2010/06/pytest-test-generators-and-cached-setup.html
-
-Older conference talks and tutorials
-----------------------------------------
-
-- `pycon australia 2012 pytest talk from Brianna Laugher
- <http://2012.pycon-au.org/schedule/52/view_talk?day=sunday>`_ (`video <http://www.youtube.com/watch?v=DTNejE9EraI>`_, `slides <http://www.slideshare.net/pfctdayelise/funcargs-other-fun-with-pytest>`_, `code <https://gist.github.com/3386951>`_)
-- `pycon 2012 US talk video from Holger Krekel <http://www.youtube.com/watch?v=9LVqBQcFmyw>`_
-- `pycon 2010 tutorial PDF`_ and `tutorial1 repository`_
-
-- `ep2009-rapidtesting.pdf`_ tutorial slides (July 2009):
-
- - testing terminology
- - basic pytest usage, file system layout
- - test function arguments (funcargs_) and test fixtures
- - existing plugins
- - distributed testing
-
-- `ep2009-pytest.pdf`_ 60 minute pytest talk, highlighting unique features and a roadmap (July 2009)
-
-- `pycon2009-pytest-introduction.zip`_ slides and files, extended version of pytest basic introduction, discusses more options, also introduces old-style xUnit setup, looponfailing and other features.
-
-- `pycon2009-pytest-advanced.pdf`_ contain a slightly older version of funcargs and distributed testing, compared to the EuroPython 2009 slides.
-
-.. _`ep2009-rapidtesting.pdf`: http://codespeak.net/download/py/ep2009-rapidtesting.pdf
-.. _`ep2009-pytest.pdf`: http://codespeak.net/download/py/ep2009-pytest.pdf
-.. _`pycon2009-pytest-introduction.zip`: http://codespeak.net/download/py/pycon2009-pytest-introduction.zip
-.. _`pycon2009-pytest-advanced.pdf`: http://codespeak.net/download/py/pycon2009-pytest-advanced.pdf
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/attic.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/attic.rst
deleted file mode 100644
index 6408c722584..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/attic.rst
+++ /dev/null
@@ -1,117 +0,0 @@
-===============================================
-ATTIC documentation
-===============================================
-
-XXX REVIEW and remove the below XXX
-
-Customizing the testing process
-===============================
-
-writing conftest.py files
------------------------------------
-
-You may put conftest.py files containing project-specific
-configuration in your project's root directory, it's usually
-best to put it just into the same directory level as your
-topmost ``__init__.py``. In fact, ``pytest`` performs
-an "upwards" search starting from the directory that you specify
-to be tested and will lookup configuration values right-to-left.
-You may have options that reside e.g. in your home directory
-but note that project specific settings will be considered
-first. There is a flag that helps you debugging your
-conftest.py configurations::
-
- py.test --traceconfig
-
-
-customizing the collecting and running process
------------------------------------------------
-
-To introduce different test items you can create
-one or more ``conftest.py`` files in your project.
-When the collection process traverses directories
-and modules the default collectors will produce
-custom Collectors and Items if they are found
-in a local ``conftest.py`` file.
-
-
-Customizing the collection process in a module
-----------------------------------------------
-
-If you have a module where you want to take responsibility for
-collecting your own test Items and possibly even for executing
-a test then you can provide `generative tests`_ that yield
-callables and possibly arguments as a tuple. This is especially
-useful for calling application test machinery with different
-parameter sets but counting each of the calls as a separate
-tests.
-
-.. _`generative tests`: features.html#generative-tests
-
-The other extension possibility is about
-specifying a custom test ``Item`` class which
-is responsible for setting up and executing an underlying
-test. Or you can extend the collection process for a whole
-directory tree by putting Items in a ``conftest.py`` configuration file.
-The collection process dynamically consults the *chain of conftest.py*
-modules to determine collectors and items at ``Directory``, ``Module``,
-``Class``, ``Function`` or ``Generator`` level respectively.
-
-Customizing execution of Items and Functions
-----------------------------------------------------
-
-- ``pytest.Function`` test items control execution
- of a test function through its ``function.runtest()`` method.
- This method is responsible for performing setup and teardown
- ("Test Fixtures") for a test Function.
-
-- ``Function.execute(target, *args)`` methods are invoked by
- the default ``Function.run()`` to actually execute a python
- function with the given (usually empty set of) arguments.
-
-.. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev
-
-
-.. _`test generators`: funcargs.html#test-generators
-
-.. _`generative tests`:
-
-generative tests: yielding parametrized tests
-====================================================
-
-Deprecated since 1.0 in favour of `test generators`_.
-
-*Generative tests* are test methods that are *generator functions* which
-``yield`` callables and their arguments. This is useful for running a
-test function multiple times against different parameters. Example::
-
- def test_generative():
- for x in (42,17,49):
- yield check, x
-
- def check(arg):
- assert arg % 7 == 0 # second generated tests fails!
-
-Note that ``test_generative()`` will cause three tests
-to get run, notably ``check(42)``, ``check(17)`` and ``check(49)``
-of which the middle one will obviously fail.
-
-To make it easier to distinguish the generated tests it is possible to specify an explicit name for them, like for example::
-
- def test_generative():
- for x in (42,17,49):
- yield "case %d" % x, check, x
-
-
-disabling a test class
-----------------------
-
-If you want to disable a complete test class you
-can set the class-level attribute ``disabled``.
-For example, in order to avoid running some tests on Win32::
-
- class TestPosixOnly:
- disabled = sys.platform == 'win32'
-
- def test_xxx(self):
- ...
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/mission.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/mission.rst
deleted file mode 100644
index cda8d9a7208..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/mission.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-
-Mission
-====================================
-
-``pytest`` strives to make testing a fun and no-boilerplate effort.
-
-The tool is distributed as a `pytest` package. Its project independent
-``py.test`` command line tool helps you to:
-
-* rapidly collect and run tests
-* run unit- or doctests, functional or integration tests
-* distribute tests to multiple environments
-* use local or global plugins for custom test types and setup
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/cov.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/cov.rst
deleted file mode 100644
index 355093f2586..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/cov.rst
+++ /dev/null
@@ -1,230 +0,0 @@
-
-produce code coverage reports using the 'coverage' package, including support for distributed testing.
-======================================================================================================
-
-
-.. contents::
- :local:
-
-This plugin produces coverage reports. It supports centralised testing and distributed testing in
-both load and each modes. It also supports coverage of subprocesses.
-
-All features offered by the coverage package should be available, either through pytest-cov or
-through coverage's config file.
-
-
-Installation
-------------
-
-The `pytest-cov`_ package may be installed with pip or easy_install::
-
- pip install pytest-cov
- easy_install pytest-cov
-
-.. _`pytest-cov`: http://pypi.python.org/pypi/pytest-cov/
-
-
-Uninstallation
---------------
-
-Uninstalling packages is supported by pip::
-
- pip uninstall pytest-cov
-
-However easy_install does not provide an uninstall facility.
-
-.. IMPORTANT::
-
- Ensure that you manually delete the init_covmain.pth file in your
- site-packages directory.
-
- This file starts coverage collection of subprocesses if appropriate during
- site initialization at python startup.
-
-
-Usage
------
-
-Centralised Testing
-~~~~~~~~~~~~~~~~~~~
-
-Centralised testing will report on the combined coverage of the main process and all of it's
-subprocesses.
-
-Running centralised testing::
-
- py.test --cov myproj tests/
-
-Shows a terminal report::
-
- -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
- Name Stmts Miss Cover
- ----------------------------------------
- myproj/__init__ 2 0 100%
- myproj/myproj 257 13 94%
- myproj/feature4286 94 7 92%
- ----------------------------------------
- TOTAL 353 20 94%
-
-
-Distributed Testing: Load
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Distributed testing with dist mode set to load will report on the combined coverage of all slaves.
-The slaves may be spread out over any number of hosts and each slave may be located anywhere on the
-file system. Each slave will have it's subprocesses measured.
-
-Running distributed testing with dist mode set to load::
-
- py.test --cov myproj -n 2 tests/
-
-Shows a terminal report::
-
- -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
- Name Stmts Miss Cover
- ----------------------------------------
- myproj/__init__ 2 0 100%
- myproj/myproj 257 13 94%
- myproj/feature4286 94 7 92%
- ----------------------------------------
- TOTAL 353 20 94%
-
-
-Again but spread over different hosts and different directories::
-
- py.test --cov myproj --dist load
- --tx ssh=memedough@host1//chdir=testenv1
- --tx ssh=memedough@host2//chdir=/tmp/testenv2//python=/tmp/env1/bin/python
- --rsyncdir myproj --rsyncdir tests --rsync examples
- tests/
-
-Shows a terminal report::
-
- -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
- Name Stmts Miss Cover
- ----------------------------------------
- myproj/__init__ 2 0 100%
- myproj/myproj 257 13 94%
- myproj/feature4286 94 7 92%
- ----------------------------------------
- TOTAL 353 20 94%
-
-
-Distributed Testing: Each
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Distributed testing with dist mode set to each will report on the combined coverage of all slaves.
-Since each slave is running all tests this allows generating a combined coverage report for multiple
-environments.
-
-Running distributed testing with dist mode set to each::
-
- py.test --cov myproj --dist each
- --tx popen//chdir=/tmp/testenv3//python=/usr/local/python27/bin/python
- --tx ssh=memedough@host2//chdir=/tmp/testenv4//python=/tmp/env2/bin/python
- --rsyncdir myproj --rsyncdir tests --rsync examples
- tests/
-
-Shows a terminal report::
-
- ---------------------------------------- coverage ----------------------------------------
- platform linux2, python 2.6.5-final-0
- platform linux2, python 2.7.0-final-0
- Name Stmts Miss Cover
- ----------------------------------------
- myproj/__init__ 2 0 100%
- myproj/myproj 257 13 94%
- myproj/feature4286 94 7 92%
- ----------------------------------------
- TOTAL 353 20 94%
-
-
-Reporting
----------
-
-It is possible to generate any combination of the reports for a single test run.
-
-The available reports are terminal (with or without missing line numbers shown), HTML, XML and
-annotated source code.
-
-The terminal report without line numbers (default)::
-
- py.test --cov-report term --cov myproj tests/
-
- -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
- Name Stmts Miss Cover
- ----------------------------------------
- myproj/__init__ 2 0 100%
- myproj/myproj 257 13 94%
- myproj/feature4286 94 7 92%
- ----------------------------------------
- TOTAL 353 20 94%
-
-
-The terminal report with line numbers::
-
- py.test --cov-report term-missing --cov myproj tests/
-
- -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
- Name Stmts Miss Cover Missing
- --------------------------------------------------
- myproj/__init__ 2 0 100%
- myproj/myproj 257 13 94% 24-26, 99, 149, 233-236, 297-298, 369-370
- myproj/feature4286 94 7 92% 183-188, 197
- --------------------------------------------------
- TOTAL 353 20 94%
-
-
-The remaining three reports output to files without showing anything on the terminal (useful for
-when the output is going to a continuous integration server)::
-
- py.test --cov-report html --cov-report xml --cov-report annotate --cov myproj tests/
-
-
-Coverage Data File
-------------------
-
-The data file is erased at the beginning of testing to ensure clean data for each test run.
-
-The data file is left at the end of testing so that it is possible to use normal coverage tools to
-examine it.
-
-
-Limitations
------------
-
-For distributed testing the slaves must have the pytest-cov package installed. This is needed since
-the plugin must be registered through setuptools / distribute for pytest to start the plugin on the
-slave.
-
-For subprocess measurement environment variables must make it from the main process to the
-subprocess. The python used by the subprocess must have pytest-cov installed. The subprocess must
-do normal site initialization so that the environment variables can be detected and coverage
-started.
-
-
-Acknowledgments
-----------------
-
-Holger Krekel for pytest with its distributed testing support.
-
-Ned Batchelder for coverage and its ability to combine the coverage results of parallel runs.
-
-Whilst this plugin has been built fresh from the ground up to support distributed testing it has
-been influenced by the work done on pytest-coverage (Ross Lawley, James Mills, Holger Krekel) and
-nose-cover (Jason Pellerin) which are other coverage plugins for pytest and nose respectively.
-
-No doubt others have contributed to these tools as well.
-
-command line options
---------------------
-
-
-``--cov=path``
- measure coverage for filesystem path (multi-allowed)
-``--cov-report=type``
- type of report to generate: term, term-missing, annotate, html, xml (multi-allowed)
-``--cov-config=path``
- config file for coverage, default: .coveragerc
-
-.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/coverage.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/coverage.rst
deleted file mode 100644
index 965b4a4ee07..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/coverage.rst
+++ /dev/null
@@ -1,51 +0,0 @@
-
-Write and report coverage data with the 'coverage' package.
-===========================================================
-
-
-.. contents::
- :local:
-
-Note: Original code by Ross Lawley.
-
-Install
---------------
-
-Use pip to (un)install::
-
- pip install pytest-coverage
- pip uninstall pytest-coverage
-
-or alternatively use easy_install to install::
-
- easy_install pytest-coverage
-
-
-Usage
--------------
-
-To get full test coverage reports for a particular package type::
-
- py.test --cover-report=report
-
-command line options
---------------------
-
-
-``--cover=COVERPACKAGES``
- (multi allowed) only include info from specified package.
-``--cover-report=REPORT_TYPE``
- html: Directory for html output.
- report: Output a text report.
- annotate: Annotate your source code for which lines were executed and which were not.
- xml: Output an xml report compatible with the cobertura plugin for hudson.
-``--cover-directory=DIRECTORY``
- Directory for the reports (html / annotate results) defaults to ./coverage
-``--cover-xml-file=XML_FILE``
- File for the xml report defaults to ./coverage.xml
-``--cover-show-missing``
- Show missing files
-``--cover-ignore-errors=IGNORE_ERRORS``
- Ignore errors of finding source files for code.
-
-.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/figleaf.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/figleaf.rst
deleted file mode 100644
index 86e0da65b6b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/figleaf.rst
+++ /dev/null
@@ -1,44 +0,0 @@
-
-report test coverage using the 'figleaf' package.
-=================================================
-
-
-.. contents::
- :local:
-
-Install
----------------
-
-To install the plugin issue::
-
- easy_install pytest-figleaf # or
- pip install pytest-figleaf
-
-and if you are using pip you can also uninstall::
-
- pip uninstall pytest-figleaf
-
-
-Usage
----------------
-
-After installation you can simply type::
-
- py.test --figleaf [...]
-
-to enable figleaf coverage in your test run. A default ".figleaf" data file
-and "html" directory will be created. You can use command line options
-to control where data and html files are created.
-
-command line options
---------------------
-
-
-``--figleaf``
- trace python coverage with figleaf and write HTML for files below the current working dir
-``--fig-data=dir``
- set tracing file, default: ".figleaf".
-``--fig-html=dir``
- set html reporting dir, default "html".
-
-.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/genscript.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/genscript.rst
deleted file mode 100644
index ee80f233fa0..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/genscript.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-
-(deprecated) generate standalone test script to be distributed along with an application.
-============================================================================
-
-
-.. contents::
- :local:
-
-
-
-command line options
---------------------
-
-
-``--genscript=path``
- create standalone ``pytest`` script at given target path.
-
-Start improving this plugin in 30 seconds
-=========================================
-
-
-1. Download `pytest_genscript.py`_ plugin source code
-2. put it somewhere as ``pytest_genscript.py`` into your import path
-3. a subsequent ``pytest`` run will use your local version
-
-Checkout customize_, other plugins_ or `get in contact`_.
-
-.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/helpconfig.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/helpconfig.rst
deleted file mode 100644
index 9b5b8cddd9d..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/helpconfig.rst
+++ /dev/null
@@ -1,38 +0,0 @@
-
-provide version info, conftest/environment config names.
-========================================================
-
-
-.. contents::
- :local:
-
-
-
-command line options
---------------------
-
-
-``--version``
- display py lib version and import information.
-``-p name``
- early-load given plugin (multi-allowed).
-``--traceconfig``
- trace considerations of conftest.py files.
-``--nomagic``
- don't reinterpret asserts, no traceback cutting.
-``--debug``
- generate and show internal debugging information.
-``--help-config``
- show available conftest.py and ENV-variable names.
-
-Start improving this plugin in 30 seconds
-=========================================
-
-
-1. Download `pytest_helpconfig.py`_ plugin source code
-2. put it somewhere as ``pytest_helpconfig.py`` into your import path
-3. a subsequent ``pytest`` run will use your local version
-
-Checkout customize_, other plugins_ or `get in contact`_.
-
-.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/links.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/links.rst
deleted file mode 100644
index aa965e73049..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/links.rst
+++ /dev/null
@@ -1,47 +0,0 @@
-.. _`helpconfig`: helpconfig.html
-.. _`pytest_recwarn.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_recwarn.py
-.. _`unittest`: unittest.html
-.. _`pytest_monkeypatch.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_monkeypatch.py
-.. _`pytest_genscript.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_genscript.py
-.. _`pastebin`: pastebin.html
-.. _`skipping`: skipping.html
-.. _`genscript`: genscript.html
-.. _`plugins`: index.html
-.. _`mark`: mark.html
-.. _`tmpdir`: tmpdir.html
-.. _`pytest_doctest.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_doctest.py
-.. _`capture`: capture.html
-.. _`pytest_nose.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_nose.py
-.. _`pytest_restdoc.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_restdoc.py
-.. _`restdoc`: restdoc.html
-.. _`xdist`: xdist.html
-.. _`pytest_pastebin.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_pastebin.py
-.. _`pytest_tmpdir.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_tmpdir.py
-.. _`terminal`: terminal.html
-.. _`pytest_hooklog.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_hooklog.py
-.. _`capturelog`: capturelog.html
-.. _`junitxml`: junitxml.html
-.. _`pytest_skipping.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_skipping.py
-.. _`checkout the pytest development version`: ../../install.html#checkout
-.. _`pytest_helpconfig.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_helpconfig.py
-.. _`oejskit`: oejskit.html
-.. _`doctest`: doctest.html
-.. _`pytest_mark.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_mark.py
-.. _`get in contact`: ../../contact.html
-.. _`pytest_capture.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_capture.py
-.. _`figleaf`: figleaf.html
-.. _`customize`: ../customize.html
-.. _`hooklog`: hooklog.html
-.. _`pytest_terminal.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_terminal.py
-.. _`recwarn`: recwarn.html
-.. _`pytest_pdb.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_pdb.py
-.. _`monkeypatch`: monkeypatch.html
-.. _`coverage`: coverage.html
-.. _`resultlog`: resultlog.html
-.. _`cov`: cov.html
-.. _`pytest_junitxml.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_junitxml.py
-.. _`django`: django.html
-.. _`pytest_unittest.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_unittest.py
-.. _`nose`: nose.html
-.. _`pytest_resultlog.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_resultlog.py
-.. _`pdb`: pdb.html
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/nose.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/nose.rst
deleted file mode 100644
index f3aa7d705f5..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/nose.rst
+++ /dev/null
@@ -1,56 +0,0 @@
-
-nose-compatibility plugin: allow to run nose test suites natively.
-==================================================================
-
-
-.. contents::
- :local:
-
-This is an experimental plugin for allowing to run tests written
-in 'nosetests' style with ``pytest``.
-
-Usage
--------------
-
-type::
-
- py.test # instead of 'nosetests'
-
-and you should be able to run nose style tests and at the same
-time can make full use of pytest's capabilities.
-
-Supported nose Idioms
-----------------------
-
-* setup and teardown at module/class/method level
-* SkipTest exceptions and markers
-* setup/teardown decorators
-* yield-based tests and their setup
-* general usage of nose utilities
-
-Unsupported idioms / issues
-----------------------------------
-
-- nose-style doctests are not collected and executed correctly,
- also fixtures don't work.
-
-- no nose-configuration is recognized
-
-If you find other issues or have suggestions please run::
-
- py.test --pastebin=all
-
-and send the resulting URL to a ``pytest`` contact channel,
-at best to the mailing list.
-
-Start improving this plugin in 30 seconds
-=========================================
-
-
-1. Download `pytest_nose.py`_ plugin source code
-2. put it somewhere as ``pytest_nose.py`` into your import path
-3. a subsequent ``pytest`` run will use your local version
-
-Checkout customize_, other plugins_ or `get in contact`_.
-
-.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/terminal.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/terminal.rst
deleted file mode 100644
index 214c24dfccd..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/terminal.rst
+++ /dev/null
@@ -1,40 +0,0 @@
-
-Implements terminal reporting of the full testing process.
-==========================================================
-
-
-.. contents::
- :local:
-
-This is a good source for looking at the various reporting hooks.
-
-command line options
---------------------
-
-
-``-v, --verbose``
- increase verbosity.
-``-r chars``
- show extra test summary info as specified by chars (f)ailed, (s)skipped, (x)failed, (X)passed.
-``-l, --showlocals``
- show locals in tracebacks (disabled by default).
-``--report=opts``
- (deprecated, use -r)
-``--tb=style``
- traceback print mode (long/short/line/no).
-``--fulltrace``
- don't cut any tracebacks (default is to cut).
-``--fixtures``
- show available function arguments, sorted by plugin
-
-Start improving this plugin in 30 seconds
-=========================================
-
-
-1. Download `pytest_terminal.py`_ plugin source code
-2. put it somewhere as ``pytest_terminal.py`` into your import path
-3. a subsequent ``pytest`` run will use your local version
-
-Checkout customize_, other plugins_ or `get in contact`_.
-
-.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/xdist.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/xdist.rst
deleted file mode 100644
index 7ab6cdc8b83..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/xdist.rst
+++ /dev/null
@@ -1,172 +0,0 @@
-
-loop on failing tests, distribute test runs to CPUs and hosts.
-==============================================================
-
-
-.. contents::
- :local:
-
-The `pytest-xdist`_ plugin extends ``pytest`` with some unique
-test execution modes:
-
-* Looponfail: run your tests repeatedly in a subprocess. After each run
- ``pytest`` waits until a file in your project changes and then re-runs the
- previously failing tests. This is repeated until all tests pass after which
- again a full run is performed.
-
-* Load-balancing: if you have multiple CPUs or hosts you can use
- those for a combined test run. This allows to speed up
- development or to use special resources of remote machines.
-
-* Multi-Platform coverage: you can specify different Python interpreters
- or different platforms and run tests in parallel on all of them.
-
-Before running tests remotely, ``pytest`` efficiently synchronizes your
-program source code to the remote place. All test results
-are reported back and displayed to your local test session.
-You may specify different Python versions and interpreters.
-
-.. _`pytest-xdist`: http://pypi.python.org/pypi/pytest-xdist
-
-Usage examples
----------------------
-
-Speed up test runs by sending tests to multiple CPUs
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-To send tests to multiple CPUs, type::
-
- py.test -n NUM
-
-Especially for longer running tests or tests requiring
-a lot of IO this can lead to considerable speed ups.
-
-
-Running tests in a Python subprocess
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-To instantiate a python2.4 sub process and send tests to it, you may type::
-
- py.test -d --tx popen//python=python2.4
-
-This will start a subprocess which is run with the "python2.4"
-Python interpreter, found in your system binary lookup path.
-
-If you prefix the --tx option value like this::
-
- --tx 3*popen//python=python2.4
-
-then three subprocesses would be created and tests
-will be load-balanced across these three processes.
-
-
-Sending tests to remote SSH accounts
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-Suppose you have a package ``mypkg`` which contains some
-tests that you can successfully run locally. And you
-have a ssh-reachable machine ``myhost``. Then
-you can ad-hoc distribute your tests by typing::
-
- py.test -d --tx ssh=myhostpopen --rsyncdir mypkg mypkg
-
-This will synchronize your ``mypkg`` package directory
-to an remote ssh account and then locally collect tests
-and send them to remote places for execution.
-
-You can specify multiple ``--rsyncdir`` directories
-to be sent to the remote side.
-
-**NOTE:** For ``pytest`` to collect and send tests correctly
-you not only need to make sure all code and tests
-directories are rsynced, but that any test (sub) directory
-also has an ``__init__.py`` file because internally
-``pytest`` references tests using their fully qualified python
-module path. **You will otherwise get strange errors**
-during setup of the remote side.
-
-Sending tests to remote Socket Servers
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-Download the single-module `socketserver.py`_ Python program
-and run it like this::
-
- python socketserver.py
-
-It will tell you that it starts listening on the default
-port. You can now on your home machine specify this
-new socket host with something like this::
-
- py.test -d --tx socket=192.168.1.102:8888 --rsyncdir mypkg mypkg
-
-
-.. _`atonce`:
-
-Running tests on many platforms at once
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-The basic command to run tests on multiple platforms is::
-
- py.test --dist=each --tx=spec1 --tx=spec2
-
-If you specify a windows host, an OSX host and a Linux
-environment this command will send each tests to all
-platforms - and report back failures from all platforms
-at once. The specifications strings use the `xspec syntax`_.
-
-.. _`xspec syntax`: http://codespeak.net/execnet/trunk/basics.html#xspec
-
-.. _`socketserver.py`: http://codespeak.net/svn/py/dist/py/execnet/script/socketserver.py
-
-.. _`execnet`: http://codespeak.net/execnet
-
-Specifying test exec environments in a conftest.py
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-Instead of specifying command line options, you can
-put options values in a ``conftest.py`` file like this::
-
- option_tx = ['ssh=myhost//python=python2.7', 'popen//python=python2.7']
- option_dist = True
-
-Any commandline ``--tx`` specifications will add to the list of
-available execution environments.
-
-Specifying "rsync" dirs in a conftest.py
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-In your ``mypkg/conftest.py`` you may specify directories to synchronise
-or to exclude::
-
- rsyncdirs = ['.', '../plugins']
- rsyncignore = ['_cache']
-
-These directory specifications are relative to the directory
-where the ``conftest.py`` is found.
-
-command line options
---------------------
-
-
-``-f, --looponfail``
- run tests in subprocess, wait for modified files and re-run failing test set until all pass.
-``-n numprocesses``
- shortcut for '--dist=load --tx=NUM*popen'
-``--boxed``
- box each test run in a separate process (unix)
-``--dist=distmode``
- set mode for distributing tests to exec environments.
-
- each: send each test to each available environment.
-
- load: send each test to one available environment so it is run only once.
-
- (default) no: run tests inprocess, don't distribute.
-``--tx=xspec``
- add a test execution environment. some examples: --tx popen//python=python2.7 --tx socket=192.168.1.102:8888 --tx ssh=user@codespeak.net//chdir=testcache
-``-d``
- load-balance tests. shortcut for '--dist=load'
-``--rsyncdir=dir1``
- add directory for rsyncing to remote tx nodes.
-
-.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/tmpdir.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/tmpdir.rst
deleted file mode 100644
index f8935b8cef8..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/tmpdir.rst
+++ /dev/null
@@ -1,111 +0,0 @@
-
-.. _`tmpdir handling`:
-.. _tmpdir:
-
-Temporary directories and files
-================================================
-
-The 'tmpdir' fixture
---------------------
-
-You can use the ``tmpdir`` fixture which will
-provide a temporary directory unique to the test invocation,
-created in the `base temporary directory`_.
-
-``tmpdir`` is a `py.path.local`_ object which offers ``os.path`` methods
-and more. Here is an example test usage::
-
- # content of test_tmpdir.py
- import os
- def test_create_file(tmpdir):
- p = tmpdir.mkdir("sub").join("hello.txt")
- p.write("content")
- assert p.read() == "content"
- assert len(tmpdir.listdir()) == 1
- assert 0
-
-Running this would result in a passed test except for the last
-``assert 0`` line which we use to look at values::
-
- $ py.test test_tmpdir.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 1 items
-
- test_tmpdir.py F
-
- ======= FAILURES ========
- _______ test_create_file ________
-
- tmpdir = local('PYTEST_TMPDIR/test_create_file0')
-
- def test_create_file(tmpdir):
- p = tmpdir.mkdir("sub").join("hello.txt")
- p.write("content")
- assert p.read() == "content"
- assert len(tmpdir.listdir()) == 1
- > assert 0
- E assert 0
-
- test_tmpdir.py:7: AssertionError
- ======= 1 failed in 0.12 seconds ========
-
-The 'tmpdir_factory' fixture
-----------------------------
-
-.. versionadded:: 2.8
-
-The ``tmpdir_factory`` is a session-scoped fixture which can be used
-to create arbitrary temporary directories from any other fixture or test.
-
-For example, suppose your test suite needs a large image on disk, which is
-generated procedurally. Instead of computing the same image for each test
-that uses it into its own ``tmpdir``, you can generate it once per-session
-to save time:
-
-.. code-block:: python
-
- # contents of conftest.py
- import pytest
-
- @pytest.fixture(scope='session')
- def image_file(tmpdir_factory):
- img = compute_expensive_image()
- fn = tmpdir_factory.mktemp('data').join('img.png')
- img.save(str(fn))
- return fn
-
- # contents of test_image.py
- def test_histogram(image_file):
- img = load_image(image_file)
- # compute and test histogram
-
-``tmpdir_factory`` instances have the following methods:
-
-.. currentmodule:: _pytest.tmpdir
-
-.. automethod:: TempdirFactory.mktemp
-.. automethod:: TempdirFactory.getbasetemp
-
-.. _`base temporary directory`:
-
-The default base temporary directory
------------------------------------------------
-
-Temporary directories are by default created as sub-directories of
-the system temporary directory. The base name will be ``pytest-NUM`` where
-``NUM`` will be incremented with each test run. Moreover, entries older
-than 3 temporary directories will be removed.
-
-You can override the default temporary directory setting like this::
-
- py.test --basetemp=mydir
-
-When distributing tests on the local machine, ``pytest`` takes care to
-configure a basetemp directory for the sub processes such that all temporary
-data lands below a single per-test run basetemp directory.
-
-.. _`py.path.local`: http://py.rtfd.org/en/latest/path.html
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/unittest.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/unittest.rst
deleted file mode 100644
index ce99bd11867..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/unittest.rst
+++ /dev/null
@@ -1,190 +0,0 @@
-
-.. _`unittest.TestCase`:
-
-Support for unittest.TestCase / Integration of fixtures
-=====================================================================
-
-.. _`unittest.py style`: http://docs.python.org/library/unittest.html
-
-``pytest`` has support for running Python `unittest.py style`_ tests.
-It's meant for leveraging existing unittest-style projects
-to use pytest features. Concretely, pytest will automatically
-collect ``unittest.TestCase`` subclasses and their ``test`` methods in
-test files. It will invoke typical setup/teardown methods and
-generally try to make test suites written to run on unittest, to also
-run using ``pytest``. We assume here that you are familiar with writing
-``unittest.TestCase`` style tests and rather focus on
-integration aspects.
-
-Usage
--------------------------------------------------------------------
-
-After :ref:`installation` type::
-
- py.test
-
-and you should be able to run your unittest-style tests if they
-are contained in ``test_*`` modules. If that works for you then
-you can make use of most :ref:`pytest features <features>`, for example
-``--pdb`` debugging in failures, using :ref:`plain assert-statements <assert>`,
-:ref:`more informative tracebacks <tbreportdemo>`, stdout-capturing or
-distributing tests to multiple CPUs via the ``-nNUM`` option if you
-installed the ``pytest-xdist`` plugin. Please refer to
-the general ``pytest`` documentation for many more examples.
-
-Mixing pytest fixtures into unittest.TestCase style tests
------------------------------------------------------------
-
-Running your unittest with ``pytest`` allows you to use its
-:ref:`fixture mechanism <fixture>` with ``unittest.TestCase`` style
-tests. Assuming you have at least skimmed the pytest fixture features,
-let's jump-start into an example that integrates a pytest ``db_class``
-fixture, setting up a class-cached database object, and then reference
-it from a unittest-style test::
-
- # content of conftest.py
-
- # we define a fixture function below and it will be "used" by
- # referencing its name from tests
-
- import pytest
-
- @pytest.fixture(scope="class")
- def db_class(request):
- class DummyDB:
- pass
- # set a class attribute on the invoking test context
- request.cls.db = DummyDB()
-
-This defines a fixture function ``db_class`` which - if used - is
-called once for each test class and which sets the class-level
-``db`` attribute to a ``DummyDB`` instance. The fixture function
-achieves this by receiving a special ``request`` object which gives
-access to :ref:`the requesting test context <request-context>` such
-as the ``cls`` attribute, denoting the class from which the fixture
-is used. This architecture de-couples fixture writing from actual test
-code and allows re-use of the fixture by a minimal reference, the fixture
-name. So let's write an actual ``unittest.TestCase`` class using our
-fixture definition::
-
- # content of test_unittest_db.py
-
- import unittest
- import pytest
-
- @pytest.mark.usefixtures("db_class")
- class MyTest(unittest.TestCase):
- def test_method1(self):
- assert hasattr(self, "db")
- assert 0, self.db # fail for demo purposes
-
- def test_method2(self):
- assert 0, self.db # fail for demo purposes
-
-The ``@pytest.mark.usefixtures("db_class")`` class-decorator makes sure that
-the pytest fixture function ``db_class`` is called once per class.
-Due to the deliberately failing assert statements, we can take a look at
-the ``self.db`` values in the traceback::
-
- $ py.test test_unittest_db.py
- ======= test session starts ========
- platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
- rootdir: $REGENDOC_TMPDIR, inifile:
- collected 2 items
-
- test_unittest_db.py FF
-
- ======= FAILURES ========
- _______ MyTest.test_method1 ________
-
- self = <test_unittest_db.MyTest testMethod=test_method1>
-
- def test_method1(self):
- assert hasattr(self, "db")
- > assert 0, self.db # fail for demo purposes
- E AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef>
- E assert 0
-
- test_unittest_db.py:9: AssertionError
- _______ MyTest.test_method2 ________
-
- self = <test_unittest_db.MyTest testMethod=test_method2>
-
- def test_method2(self):
- > assert 0, self.db # fail for demo purposes
- E AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef>
- E assert 0
-
- test_unittest_db.py:12: AssertionError
- ======= 2 failed in 0.12 seconds ========
-
-This default pytest traceback shows that the two test methods
-share the same ``self.db`` instance which was our intention
-when writing the class-scoped fixture function above.
-
-
-autouse fixtures and accessing other fixtures
--------------------------------------------------------------------
-
-Although it's usually better to explicitly declare use of fixtures you need
-for a given test, you may sometimes want to have fixtures that are
-automatically used in a given context. After all, the traditional
-style of unittest-setup mandates the use of this implicit fixture writing
-and chances are, you are used to it or like it.
-
-You can flag fixture functions with ``@pytest.fixture(autouse=True)``
-and define the fixture function in the context where you want it used.
-Let's look at an ``initdir`` fixture which makes all test methods of a
-``TestCase`` class execute in a temporary directory with a
-pre-initialized ``samplefile.ini``. Our ``initdir`` fixture itself uses
-the pytest builtin :ref:`tmpdir <tmpdir>` fixture to delegate the
-creation of a per-test temporary directory::
-
- # content of test_unittest_cleandir.py
- import pytest
- import unittest
-
- class MyTest(unittest.TestCase):
- @pytest.fixture(autouse=True)
- def initdir(self, tmpdir):
- tmpdir.chdir() # change to pytest-provided temporary directory
- tmpdir.join("samplefile.ini").write("# testdata")
-
- def test_method(self):
- s = open("samplefile.ini").read()
- assert "testdata" in s
-
-Due to the ``autouse`` flag the ``initdir`` fixture function will be
-used for all methods of the class where it is defined. This is a
-shortcut for using a ``@pytest.mark.usefixtures("initdir")`` marker
-on the class like in the previous example.
-
-Running this test module ...::
-
- $ py.test -q test_unittest_cleandir.py
- .
- 1 passed in 0.12 seconds
-
-... gives us one passed test because the ``initdir`` fixture function
-was executed ahead of the ``test_method``.
-
-.. note::
-
- While pytest supports receiving fixtures via :ref:`test function arguments <funcargs>` for non-unittest test methods, ``unittest.TestCase`` methods cannot directly receive fixture
- function arguments as implementing that is likely to inflict
- on the ability to run general unittest.TestCase test suites.
- Maybe optional support would be possible, though. If unittest finally
- grows a plugin system that should help as well. In the meanwhile, the
- above ``usefixtures`` and ``autouse`` examples should help to mix in
- pytest fixtures into unittest suites. And of course you can also start
- to selectively leave away the ``unittest.TestCase`` subclassing, use
- plain asserts and get the unlimited pytest feature set.
-
-
-Converting from unittest to pytest
----------------------------------------
-
-If you want to convert your unittest testcases to pytest, there are
-some helpers like `unittest2pytest
-<https://pypi.python.org/pypi/unittest2pytest/>`__, which uses lib2to3
-and introspection for the transformation.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/usage.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/usage.rst
deleted file mode 100644
index 4b92fd1e150..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/usage.rst
+++ /dev/null
@@ -1,275 +0,0 @@
-
-.. _usage:
-
-Usage and Invocations
-==========================================
-
-
-.. _cmdline:
-
-Calling pytest through ``python -m pytest``
------------------------------------------------------
-
-.. versionadded:: 2.0
-
-You can invoke testing through the Python interpreter from the command line::
-
- python -m pytest [...]
-
-This is equivalent to invoking the command line script ``py.test [...]``
-directly.
-
-Getting help on version, option names, environment variables
---------------------------------------------------------------
-
-::
-
- py.test --version # shows where pytest was imported from
- py.test --fixtures # show available builtin function arguments
- py.test -h | --help # show help on command line and config file options
-
-
-Stopping after the first (or N) failures
----------------------------------------------------
-
-To stop the testing process after the first (N) failures::
-
- py.test -x # stop after first failure
- py.test --maxfail=2 # stop after two failures
-
-Specifying tests / selecting tests
----------------------------------------------------
-
-Several test run options::
-
- py.test test_mod.py # run tests in module
- py.test somepath # run all tests below somepath
- py.test -k stringexpr # only run tests with names that match the
- # "string expression", e.g. "MyClass and not method"
- # will select TestMyClass.test_something
- # but not TestMyClass.test_method_simple
- py.test test_mod.py::test_func # only run tests that match the "node ID",
- # e.g "test_mod.py::test_func" will select
- # only test_func in test_mod.py
- py.test test_mod.py::TestClass::test_method # run a single method in
- # a single class
-
-Import 'pkg' and use its filesystem location to find and run tests::
-
- py.test --pyargs pkg # run all tests found below directory of pypkg
-
-Modifying Python traceback printing
-----------------------------------------------
-
-Examples for modifying traceback printing::
-
- py.test --showlocals # show local variables in tracebacks
- py.test -l # show local variables (shortcut)
-
- py.test --tb=auto # (default) 'long' tracebacks for the first and last
- # entry, but 'short' style for the other entries
- py.test --tb=long # exhaustive, informative traceback formatting
- py.test --tb=short # shorter traceback format
- py.test --tb=line # only one line per failure
- py.test --tb=native # Python standard library formatting
- py.test --tb=no # no traceback at all
-
-Dropping to PDB_ (Python Debugger) on failures
------------------------------------------------
-
-.. _PDB: http://docs.python.org/library/pdb.html
-
-Python comes with a builtin Python debugger called PDB_. ``pytest``
-allows one to drop into the PDB_ prompt via a command line option::
-
- py.test --pdb
-
-This will invoke the Python debugger on every failure. Often you might
-only want to do this for the first failing test to understand a certain
-failure situation::
-
- py.test -x --pdb # drop to PDB on first failure, then end test session
- py.test --pdb --maxfail=3 # drop to PDB for first three failures
-
-Note that on any failure the exception information is stored on
-``sys.last_value``, ``sys.last_type`` and ``sys.last_traceback``. In
-interactive use, this allows one to drop into postmortem debugging with
-any debug tool. One can also manually access the exception information,
-for example::
-
- >>> import sys
- >>> sys.last_traceback.tb_lineno
- 42
- >>> sys.last_value
- AssertionError('assert result == "ok"',)
-
-Setting a breakpoint / aka ``set_trace()``
-----------------------------------------------------
-
-If you want to set a breakpoint and enter the ``pdb.set_trace()`` you
-can use a helper::
-
- import pytest
- def test_function():
- ...
- pytest.set_trace() # invoke PDB debugger and tracing
-
-.. versionadded: 2.0.0
-
-Prior to pytest version 2.0.0 you could only enter PDB_ tracing if you disabled
-capturing on the command line via ``py.test -s``. In later versions, pytest
-automatically disables its output capture when you enter PDB_ tracing:
-
-* Output capture in other tests is not affected.
-* Any prior test output that has already been captured and will be processed as
- such.
-* Any later output produced within the same test will not be captured and will
- instead get sent directly to ``sys.stdout``. Note that this holds true even
- for test output occurring after you exit the interactive PDB_ tracing session
- and continue with the regular test run.
-
-.. versionadded: 2.4.0
-
-Since pytest version 2.4.0 you can also use the native Python
-``import pdb;pdb.set_trace()`` call to enter PDB_ tracing without having to use
-the ``pytest.set_trace()`` wrapper or explicitly disable pytest's output
-capturing via ``py.test -s``.
-
-.. _durations:
-
-Profiling test execution duration
--------------------------------------
-
-.. versionadded: 2.2
-
-To get a list of the slowest 10 test durations::
-
- py.test --durations=10
-
-
-Creating JUnitXML format files
-----------------------------------------------------
-
-To create result files which can be read by Hudson_ or other Continuous
-integration servers, use this invocation::
-
- py.test --junitxml=path
-
-to create an XML file at ``path``.
-
-record_xml_property
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. versionadded:: 2.8
-
-If you want to log additional information for a test, you can use the
-``record_xml_property`` fixture:
-
-.. code-block:: python
-
- def test_function(record_xml_property):
- record_xml_property("example_key", 1)
- assert 0
-
-This will add an extra property ``example_key="1"`` to the generated
-``testcase`` tag:
-
-.. code-block:: xml
-
- <testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009">
- <properties>
- <property name="example_key" value="1" />
- </properties>
- </testcase>
-
-.. warning::
-
- This is an experimental feature, and its interface might be replaced
- by something more powerful and general in future versions. The
- functionality per-se will be kept, however.
-
- Currently it does not work when used with the ``pytest-xdist`` plugin.
-
- Also please note that using this feature will break any schema verification.
- This might be a problem when used with some CI servers.
-
-Creating resultlog format files
-----------------------------------------------------
-
-To create plain-text machine-readable result files you can issue::
-
- py.test --resultlog=path
-
-and look at the content at the ``path`` location. Such files are used e.g.
-by the `PyPy-test`_ web page to show test results over several revisions.
-
-.. _`PyPy-test`: http://buildbot.pypy.org/summary
-
-
-Sending test report to online pastebin service
------------------------------------------------------
-
-**Creating a URL for each test failure**::
-
- py.test --pastebin=failed
-
-This will submit test run information to a remote Paste service and
-provide a URL for each failure. You may select tests as usual or add
-for example ``-x`` if you only want to send one particular failure.
-
-**Creating a URL for a whole test session log**::
-
- py.test --pastebin=all
-
-Currently only pasting to the http://bpaste.net service is implemented.
-
-Disabling plugins
------------------
-
-To disable loading specific plugins at invocation time, use the ``-p`` option
-together with the prefix ``no:``.
-
-Example: to disable loading the plugin ``doctest``, which is responsible for
-executing doctest tests from text files, invoke py.test like this::
-
- py.test -p no:doctest
-
-.. _`pytest.main-usage`:
-
-Calling pytest from Python code
-----------------------------------------------------
-
-.. versionadded:: 2.0
-
-You can invoke ``pytest`` from Python code directly::
-
- pytest.main()
-
-this acts as if you would call "py.test" from the command line.
-It will not raise ``SystemExit`` but return the exitcode instead.
-You can pass in options and arguments::
-
- pytest.main(['-x', 'mytestdir'])
-
-or pass in a string::
-
- pytest.main("-x mytestdir")
-
-You can specify additional plugins to ``pytest.main``::
-
- # content of myinvoke.py
- import pytest
- class MyPlugin:
- def pytest_sessionfinish(self):
- print("*** test run reporting finishing")
-
- pytest.main("-qq", plugins=[MyPlugin()])
-
-Running it will show that ``MyPlugin`` was added and its
-hook was invoked::
-
- $ python myinvoke.py
- *** test run reporting finishing
-
-
-.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/writing_plugins.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/writing_plugins.rst
deleted file mode 100644
index cc346aaa85c..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/writing_plugins.rst
+++ /dev/null
@@ -1,575 +0,0 @@
-.. _plugins:
-.. _`writing-plugins`:
-
-Writing plugins
-===============
-
-It is easy to implement `local conftest plugins`_ for your own project
-or `pip-installable plugins`_ that can be used throughout many projects,
-including third party projects. Please refer to :ref:`using plugins` if you
-only want to use but not write plugins.
-
-A plugin contains one or multiple hook functions. :ref:`Writing hooks <writinghooks>`
-explains the basics and details of how you can write a hook function yourself.
-``pytest`` implements all aspects of configuration, collection, running and
-reporting by calling `well specified hooks`_ of the following plugins:
-
-* :ref:`builtin plugins`: loaded from pytest's internal ``_pytest`` directory.
-
-* :ref:`external plugins <extplugins>`: modules discovered through
- `setuptools entry points`_
-
-* `conftest.py plugins`_: modules auto-discovered in test directories
-
-In principle, each hook call is a ``1:N`` Python function call where ``N`` is the
-number of registered implementation functions for a given specification.
-All specifications and implementations following the ``pytest_`` prefix
-naming convention, making them easy to distinguish and find.
-
-.. _`pluginorder`:
-
-Plugin discovery order at tool startup
---------------------------------------
-
-``pytest`` loads plugin modules at tool startup in the following way:
-
-* by loading all builtin plugins
-
-* by loading all plugins registered through `setuptools entry points`_.
-
-* by pre-scanning the command line for the ``-p name`` option
- and loading the specified plugin before actual command line parsing.
-
-* by loading all :file:`conftest.py` files as inferred by the command line
- invocation:
-
- - if no test paths are specified use current dir as a test path
- - if exists, load ``conftest.py`` and ``test*/conftest.py`` relative
- to the directory part of the first test path.
-
- Note that pytest does not find ``conftest.py`` files in deeper nested
- sub directories at tool startup. It is usually a good idea to keep
- your conftest.py file in the top level test or project root directory.
-
-* by recursively loading all plugins specified by the
- ``pytest_plugins`` variable in ``conftest.py`` files
-
-
-.. _`pytest/plugin`: http://bitbucket.org/pytest-dev/pytest/src/tip/pytest/plugin/
-.. _`conftest.py plugins`:
-.. _`conftest.py`:
-.. _`localplugin`:
-.. _`conftest`:
-.. _`local conftest plugins`:
-
-conftest.py: local per-directory plugins
-----------------------------------------
-
-Local ``conftest.py`` plugins contain directory-specific hook
-implementations. Hook Session and test running activities will
-invoke all hooks defined in ``conftest.py`` files closer to the
-root of the filesystem. Example of implementing the
-``pytest_runtest_setup`` hook so that is called for tests in the ``a``
-sub directory but not for other directories::
-
- a/conftest.py:
- def pytest_runtest_setup(item):
- # called for running each test in 'a' directory
- print ("setting up", item)
-
- a/test_sub.py:
- def test_sub():
- pass
-
- test_flat.py:
- def test_flat():
- pass
-
-Here is how you might run it::
-
- py.test test_flat.py # will not show "setting up"
- py.test a/test_sub.py # will show "setting up"
-
-.. Note::
- If you have ``conftest.py`` files which do not reside in a
- python package directory (i.e. one containing an ``__init__.py``) then
- "import conftest" can be ambiguous because there might be other
- ``conftest.py`` files as well on your PYTHONPATH or ``sys.path``.
- It is thus good practice for projects to either put ``conftest.py``
- under a package scope or to never import anything from a
- conftest.py file.
-
-
-Writing your own plugin
------------------------
-
-.. _`setuptools`: http://pypi.python.org/pypi/setuptools
-
-If you want to write a plugin, there are many real-life examples
-you can copy from:
-
-* a custom collection example plugin: :ref:`yaml plugin`
-* around 20 :ref:`builtin plugins` which provide pytest's own functionality
-* many `external plugins <http://plugincompat.herokuapp.com>`_ providing additional features
-
-All of these plugins implement the documented `well specified hooks`_
-to extend and add functionality.
-
-.. note::
- Make sure to check out the excellent
- `cookiecutter-pytest-plugin <https://github.com/pytest-dev/cookiecutter-pytest-plugin>`_
- project, which is a `cookiecutter template <https://github.com/audreyr/cookiecutter>`_
- for authoring plugins.
-
- The template provides an excellent starting point with a working plugin,
- tests running with tox, comprehensive README and
- entry-pointy already pre-configured.
-
-Also consider :ref:`contributing your plugin to pytest-dev<submitplugin>`
-once it has some happy users other than yourself.
-
-
-.. _`setuptools entry points`:
-.. _`pip-installable plugins`:
-
-Making your plugin installable by others
-----------------------------------------
-
-If you want to make your plugin externally available, you
-may define a so-called entry point for your distribution so
-that ``pytest`` finds your plugin module. Entry points are
-a feature that is provided by `setuptools`_. pytest looks up
-the ``pytest11`` entrypoint to discover its
-plugins and you can thus make your plugin available by defining
-it in your setuptools-invocation:
-
-.. sourcecode:: python
-
- # sample ./setup.py file
- from setuptools import setup
-
- setup(
- name="myproject",
- packages = ['myproject']
-
- # the following makes a plugin available to pytest
- entry_points = {
- 'pytest11': [
- 'name_of_plugin = myproject.pluginmodule',
- ]
- },
- )
-
-If a package is installed this way, ``pytest`` will load
-``myproject.pluginmodule`` as a plugin which can define
-`well specified hooks`_.
-
-
-
-
-Requiring/Loading plugins in a test module or conftest file
------------------------------------------------------------
-
-You can require plugins in a test module or a conftest file like this::
-
- pytest_plugins = "name1", "name2",
-
-When the test module or conftest plugin is loaded the specified plugins
-will be loaded as well. You can also use dotted path like this::
-
- pytest_plugins = "myapp.testsupport.myplugin"
-
-which will import the specified module as a ``pytest`` plugin.
-
-
-Accessing another plugin by name
---------------------------------
-
-If a plugin wants to collaborate with code from
-another plugin it can obtain a reference through
-the plugin manager like this:
-
-.. sourcecode:: python
-
- plugin = config.pluginmanager.getplugin("name_of_plugin")
-
-If you want to look at the names of existing plugins, use
-the ``--traceconfig`` option.
-
-Testing plugins
----------------
-
-pytest comes with some facilities that you can enable for testing your
-plugin. Given that you have an installed plugin you can enable the
-:py:class:`testdir <_pytest.pytester.Testdir>` fixture via specifying a
-command line option to include the pytester plugin (``-p pytester``) or
-by putting ``pytest_plugins = "pytester"`` into your test or
-``conftest.py`` file. You then will have a ``testdir`` fixture which you
-can use like this::
-
- # content of test_myplugin.py
-
- pytest_plugins = "pytester" # to get testdir fixture
-
- def test_myplugin(testdir):
- testdir.makepyfile("""
- def test_example():
- pass
- """)
- result = testdir.runpytest("--verbose")
- result.fnmatch_lines("""
- test_example*
- """)
-
-Note that by default ``testdir.runpytest()`` will perform a pytest
-in-process. You can pass the command line option ``--runpytest=subprocess``
-to have it happen in a subprocess.
-
-Also see the :py:class:`RunResult <_pytest.pytester.RunResult>` for more
-methods of the result object that you get from a call to ``runpytest``.
-
-.. _`writinghooks`:
-
-Writing hook functions
-======================
-
-
-.. _validation:
-
-hook function validation and execution
---------------------------------------
-
-pytest calls hook functions from registered plugins for any
-given hook specification. Let's look at a typical hook function
-for the ``pytest_collection_modifyitems(session, config,
-items)`` hook which pytest calls after collection of all test items is
-completed.
-
-When we implement a ``pytest_collection_modifyitems`` function in our plugin
-pytest will during registration verify that you use argument
-names which match the specification and bail out if not.
-
-Let's look at a possible implementation:
-
-.. code-block:: python
-
- def pytest_collection_modifyitems(config, items):
- # called after collection is completed
- # you can modify the ``items`` list
-
-Here, ``pytest`` will pass in ``config`` (the pytest config object)
-and ``items`` (the list of collected test items) but will not pass
-in the ``session`` argument because we didn't list it in the function
-signature. This dynamic "pruning" of arguments allows ``pytest`` to
-be "future-compatible": we can introduce new hook named parameters without
-breaking the signatures of existing hook implementations. It is one of
-the reasons for the general long-lived compatibility of pytest plugins.
-
-Note that hook functions other than ``pytest_runtest_*`` are not
-allowed to raise exceptions. Doing so will break the pytest run.
-
-
-
-firstresult: stop at first non-None result
--------------------------------------------
-
-Most calls to ``pytest`` hooks result in a **list of results** which contains
-all non-None results of the called hook functions.
-
-Some hook specifications use the ``firstresult=True`` option so that the hook
-call only executes until the first of N registered functions returns a
-non-None result which is then taken as result of the overall hook call.
-The remaining hook functions will not be called in this case.
-
-
-hookwrapper: executing around other hooks
--------------------------------------------------
-
-.. currentmodule:: _pytest.core
-
-.. versionadded:: 2.7 (experimental)
-
-pytest plugins can implement hook wrappers which wrap the execution
-of other hook implementations. A hook wrapper is a generator function
-which yields exactly once. When pytest invokes hooks it first executes
-hook wrappers and passes the same arguments as to the regular hooks.
-
-At the yield point of the hook wrapper pytest will execute the next hook
-implementations and return their result to the yield point in the form of
-a :py:class:`CallOutcome` instance which encapsulates a result or
-exception info. The yield point itself will thus typically not raise
-exceptions (unless there are bugs).
-
-Here is an example definition of a hook wrapper::
-
- import pytest
-
- @pytest.hookimpl(hookwrapper=True)
- def pytest_pyfunc_call(pyfuncitem):
- # do whatever you want before the next hook executes
-
- outcome = yield
- # outcome.excinfo may be None or a (cls, val, tb) tuple
-
- res = outcome.get_result() # will raise if outcome was exception
- # postprocess result
-
-Note that hook wrappers don't return results themselves, they merely
-perform tracing or other side effects around the actual hook implementations.
-If the result of the underlying hook is a mutable object, they may modify
-that result but it's probably better to avoid it.
-
-
-Hook function ordering / call example
--------------------------------------
-
-For any given hook specification there may be more than one
-implementation and we thus generally view ``hook`` execution as a
-``1:N`` function call where ``N`` is the number of registered functions.
-There are ways to influence if a hook implementation comes before or
-after others, i.e. the position in the ``N``-sized list of functions:
-
-.. code-block:: python
-
- # Plugin 1
- @pytest.hookimpl(tryfirst=True)
- def pytest_collection_modifyitems(items):
- # will execute as early as possible
-
- # Plugin 2
- @pytest.hookimpl(trylast=True)
- def pytest_collection_modifyitems(items):
- # will execute as late as possible
-
- # Plugin 3
- @pytest.hookimpl(hookwrapper=True)
- def pytest_collection_modifyitems(items):
- # will execute even before the tryfirst one above!
- outcome = yield
- # will execute after all non-hookwrappers executed
-
-Here is the order of execution:
-
-1. Plugin3's pytest_collection_modifyitems called until the yield point
- because it is a hook wrapper.
-
-2. Plugin1's pytest_collection_modifyitems is called because it is marked
- with ``tryfirst=True``.
-
-3. Plugin2's pytest_collection_modifyitems is called because it is marked
- with ``trylast=True`` (but even without this mark it would come after
- Plugin1).
-
-4. Plugin3's pytest_collection_modifyitems then executing the code after the yield
- point. The yield receives a :py:class:`CallOutcome` instance which encapsulates
- the result from calling the non-wrappers. Wrappers shall not modify the result.
-
-It's possible to use ``tryfirst`` and ``trylast`` also in conjunction with
-``hookwrapper=True`` in which case it will influence the ordering of hookwrappers
-among each other.
-
-
-Declaring new hooks
-------------------------
-
-.. currentmodule:: _pytest.hookspec
-
-Plugins and ``conftest.py`` files may declare new hooks that can then be
-implemented by other plugins in order to alter behaviour or interact with
-the new plugin:
-
-.. autofunction:: pytest_addhooks
-
-Hooks are usually declared as do-nothing functions that contain only
-documentation describing when the hook will be called and what return values
-are expected.
-
-For an example, see `newhooks.py`_ from :ref:`xdist`.
-
-.. _`newhooks.py`: https://github.com/pytest-dev/pytest-xdist/blob/974bd566c599dc6a9ea291838c6f226197208b46/xdist/newhooks.py
-
-
-Optionally using hooks from 3rd party plugins
----------------------------------------------
-
-Using new hooks from plugins as explained above might be a little tricky
-because of the standard :ref:`validation mechanism <validation>`:
-if you depend on a plugin that is not installed, validation will fail and
-the error message will not make much sense to your users.
-
-One approach is to defer the hook implementation to a new plugin instead of
-declaring the hook functions directly in your plugin module, for example::
-
- # contents of myplugin.py
-
- class DeferPlugin(object):
- """Simple plugin to defer pytest-xdist hook functions."""
-
- def pytest_testnodedown(self, node, error):
- """standard xdist hook function.
- """
-
- def pytest_configure(config):
- if config.pluginmanager.hasplugin('xdist'):
- config.pluginmanager.register(DeferPlugin())
-
-This has the added benefit of allowing you to conditionally install hooks
-depending on which plugins are installed.
-
-.. _`well specified hooks`:
-
-.. currentmodule:: _pytest.hookspec
-
-pytest hook reference
-=====================
-
-
-Initialization, command line and configuration hooks
-----------------------------------------------------
-
-.. autofunction:: pytest_load_initial_conftests
-.. autofunction:: pytest_cmdline_preparse
-.. autofunction:: pytest_cmdline_parse
-.. autofunction:: pytest_namespace
-.. autofunction:: pytest_addoption
-.. autofunction:: pytest_cmdline_main
-.. autofunction:: pytest_configure
-.. autofunction:: pytest_unconfigure
-
-Generic "runtest" hooks
------------------------
-
-All runtest related hooks receive a :py:class:`pytest.Item` object.
-
-.. autofunction:: pytest_runtest_protocol
-.. autofunction:: pytest_runtest_setup
-.. autofunction:: pytest_runtest_call
-.. autofunction:: pytest_runtest_teardown
-.. autofunction:: pytest_runtest_makereport
-
-For deeper understanding you may look at the default implementation of
-these hooks in :py:mod:`_pytest.runner` and maybe also
-in :py:mod:`_pytest.pdb` which interacts with :py:mod:`_pytest.capture`
-and its input/output capturing in order to immediately drop
-into interactive debugging when a test failure occurs.
-
-The :py:mod:`_pytest.terminal` reported specifically uses
-the reporting hook to print information about a test run.
-
-Collection hooks
-----------------
-
-``pytest`` calls the following hooks for collecting files and directories:
-
-.. autofunction:: pytest_ignore_collect
-.. autofunction:: pytest_collect_directory
-.. autofunction:: pytest_collect_file
-
-For influencing the collection of objects in Python modules
-you can use the following hook:
-
-.. autofunction:: pytest_pycollect_makeitem
-.. autofunction:: pytest_generate_tests
-
-After collection is complete, you can modify the order of
-items, delete or otherwise amend the test items:
-
-.. autofunction:: pytest_collection_modifyitems
-
-Reporting hooks
----------------
-
-Session related reporting hooks:
-
-.. autofunction:: pytest_collectstart
-.. autofunction:: pytest_itemcollected
-.. autofunction:: pytest_collectreport
-.. autofunction:: pytest_deselected
-.. autofunction:: pytest_report_header
-.. autofunction:: pytest_report_teststatus
-.. autofunction:: pytest_terminal_summary
-
-And here is the central hook for reporting about
-test execution:
-
-.. autofunction:: pytest_runtest_logreport
-
-You can also use this hook to customize assertion representation for some
-types:
-
-.. autofunction:: pytest_assertrepr_compare
-
-
-Debugging/Interaction hooks
----------------------------
-
-There are few hooks which can be used for special
-reporting or interaction with exceptions:
-
-.. autofunction:: pytest_internalerror
-.. autofunction:: pytest_keyboard_interrupt
-.. autofunction:: pytest_exception_interact
-.. autofunction:: pytest_enter_pdb
-
-
-Reference of objects involved in hooks
-======================================
-
-.. autoclass:: _pytest.config.Config()
- :members:
-
-.. autoclass:: _pytest.config.Parser()
- :members:
-
-.. autoclass:: _pytest.main.Node()
- :members:
-
-.. autoclass:: _pytest.main.Collector()
- :members:
- :show-inheritance:
-
-.. autoclass:: _pytest.main.Item()
- :members:
- :show-inheritance:
-
-.. autoclass:: _pytest.python.Module()
- :members:
- :show-inheritance:
-
-.. autoclass:: _pytest.python.Class()
- :members:
- :show-inheritance:
-
-.. autoclass:: _pytest.python.Function()
- :members:
- :show-inheritance:
-
-.. autoclass:: _pytest.runner.CallInfo()
- :members:
-
-.. autoclass:: _pytest.runner.TestReport()
- :members:
-
-.. autoclass:: _pytest.vendored_packages.pluggy._CallOutcome()
- :members:
-
-.. autofunction:: _pytest.config.get_plugin_manager()
-
-.. autoclass:: _pytest.config.PytestPluginManager()
- :members:
- :undoc-members:
- :show-inheritance:
-
-.. autoclass:: pluggy.PluginManager()
- :members:
-
-.. currentmodule:: _pytest.pytester
-
-.. autoclass:: Testdir()
- :members: runpytest,runpytest_subprocess,runpytest_inprocess,makeconftest,makepyfile
-
-.. autoclass:: RunResult()
- :members:
-
-.. autoclass:: LineMatcher()
- :members:
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/xdist.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/xdist.rst
deleted file mode 100644
index ee1bd603283..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/xdist.rst
+++ /dev/null
@@ -1,197 +0,0 @@
-
-.. _`xdist`:
-
-xdist: pytest distributed testing plugin
-===============================================================
-
-The `pytest-xdist`_ plugin extends ``pytest`` with some unique
-test execution modes:
-
-* Looponfail: run your tests repeatedly in a subprocess. After each
- run, ``pytest`` waits until a file in your project changes and then
- re-runs the previously failing tests. This is repeated until all
- tests pass. At this point a full run is again performed.
-
-* multiprocess Load-balancing: if you have multiple CPUs or hosts you can use
- them for a combined test run. This allows to speed up
- development or to use special resources of remote machines.
-
-* Multi-Platform coverage: you can specify different Python interpreters
- or different platforms and run tests in parallel on all of them.
-
-Before running tests remotely, ``pytest`` efficiently "rsyncs" your
-program source code to the remote place. All test results
-are reported back and displayed to your local terminal.
-You may specify different Python versions and interpreters.
-
-
-Installation of xdist plugin
-------------------------------
-
-Install the plugin with::
-
- easy_install pytest-xdist
-
- # or
-
- pip install pytest-xdist
-
-or use the package in develop/in-place mode with
-a checkout of the `pytest-xdist repository`_ ::
-
- python setup.py develop
-
-
-Usage examples
----------------------
-
-.. _`xdistcpu`:
-
-Speed up test runs by sending tests to multiple CPUs
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-To send tests to multiple CPUs, type::
-
- py.test -n NUM
-
-Especially for longer running tests or tests requiring
-a lot of I/O this can lead to considerable speed ups.
-
-
-Running tests in a Python subprocess
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-To instantiate a Python-2.7 subprocess and send tests to it, you may type::
-
- py.test -d --tx popen//python=python2.7
-
-This will start a subprocess which is run with the "python2.7"
-Python interpreter, found in your system binary lookup path.
-
-If you prefix the --tx option value like this::
-
- py.test -d --tx 3*popen//python=python2.7
-
-then three subprocesses would be created and the tests
-will be distributed to three subprocesses and run simultanously.
-
-.. _looponfailing:
-
-
-Running tests in looponfailing mode
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-For refactoring a project with a medium or large test suite
-you can use the looponfailing mode. Simply add the ``--f`` option::
-
- py.test -f
-
-and ``pytest`` will run your tests. Assuming you have failures it will then
-wait for file changes and re-run the failing test set. File changes are detected by looking at ``looponfailingroots`` root directories and all of their contents (recursively). If the default for this value does not work for you you
-can change it in your project by setting a configuration option::
-
- # content of a pytest.ini, setup.cfg or tox.ini file
- [pytest]
- looponfailroots = mypkg testdir
-
-This would lead to only looking for file changes in the respective directories, specified relatively to the ini-file's directory.
-
-Sending tests to remote SSH accounts
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-Suppose you have a package ``mypkg`` which contains some
-tests that you can successfully run locally. And you also
-have a ssh-reachable machine ``myhost``. Then
-you can ad-hoc distribute your tests by typing::
-
- py.test -d --tx ssh=myhostpopen --rsyncdir mypkg mypkg
-
-This will synchronize your ``mypkg`` package directory
-with a remote ssh account and then collect and run your
-tests at the remote side.
-
-You can specify multiple ``--rsyncdir`` directories
-to be sent to the remote side.
-
-.. XXX CHECK
-
- **NOTE:** For ``pytest`` to collect and send tests correctly
- you not only need to make sure all code and tests
- directories are rsynced, but that any test (sub) directory
- also has an ``__init__.py`` file because internally
- ``pytest`` references tests as a fully qualified python
- module path. **You will otherwise get strange errors**
- during setup of the remote side.
-
-Sending tests to remote Socket Servers
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-Download the single-module `socketserver.py`_ Python program
-and run it like this::
-
- python socketserver.py
-
-It will tell you that it starts listening on the default
-port. You can now on your home machine specify this
-new socket host with something like this::
-
- py.test -d --tx socket=192.168.1.102:8888 --rsyncdir mypkg mypkg
-
-
-.. _`atonce`:
-
-Running tests on many platforms at once
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-The basic command to run tests on multiple platforms is::
-
- py.test --dist=each --tx=spec1 --tx=spec2
-
-If you specify a windows host, an OSX host and a Linux
-environment this command will send each tests to all
-platforms - and report back failures from all platforms
-at once. The specifications strings use the `xspec syntax`_.
-
-.. _`xspec syntax`: http://codespeak.net/execnet/basics.html#xspec
-
-.. _`socketserver.py`: http://bitbucket.org/hpk42/execnet/raw/2af991418160/execnet/script/socketserver.py
-
-.. _`execnet`: http://codespeak.net/execnet
-
-Specifying test exec environments in an ini file
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-pytest (since version 2.0) supports ini-style configuration.
-For example, you could make running with three subprocesses your default::
-
- [pytest]
- addopts = -n3
-
-You can also add default environments like this::
-
- [pytest]
- addopts = --tx ssh=myhost//python=python2.7 --tx ssh=myhost//python=python2.6
-
-and then just type::
-
- py.test --dist=each
-
-to run tests in each of the environments.
-
-Specifying "rsync" dirs in an ini-file
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-In a ``tox.ini`` or ``setup.cfg`` file in your root project directory
-you may specify directories to include or to exclude in synchronisation::
-
- [pytest]
- rsyncdirs = . mypkg helperpkg
- rsyncignore = .hg
-
-These directory specifications are relative to the directory
-where the configuration file was found.
-
-.. _`pytest-xdist`: http://pypi.python.org/pypi/pytest-xdist
-.. _`pytest-xdist repository`: http://bitbucket.org/pytest-dev/pytest-xdist
-.. _`pytest`: http://pytest.org
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/xunit_setup.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/xunit_setup.rst
deleted file mode 100644
index 7a80f129966..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/xunit_setup.rst
+++ /dev/null
@@ -1,90 +0,0 @@
-
-.. _`classic xunit`:
-.. _xunitsetup:
-
-classic xunit-style setup
-========================================
-
-This section describes a classic and popular way how you can implement
-fixtures (setup and teardown test state) on a per-module/class/function basis.
-pytest started supporting these methods around 2005 and subsequently
-nose and the standard library introduced them (under slightly different
-names). While these setup/teardown methods are and will remain fully
-supported you may also use pytest's more powerful :ref:`fixture mechanism
-<fixture>` which leverages the concept of dependency injection, allowing
-for a more modular and more scalable approach for managing test state,
-especially for larger projects and for functional testing. You can
-mix both fixture mechanisms in the same file but unittest-based
-test methods cannot receive fixture arguments.
-
-.. note::
-
- As of pytest-2.4, teardownX functions are not called if
- setupX existed and failed/was skipped. This harmonizes
- behaviour across all major python testing tools.
-
-Module level setup/teardown
---------------------------------------
-
-If you have multiple test functions and test classes in a single
-module you can optionally implement the following fixture methods
-which will usually be called once for all the functions::
-
- def setup_module(module):
- """ setup any state specific to the execution of the given module."""
-
- def teardown_module(module):
- """ teardown any state that was previously setup with a setup_module
- method.
- """
-
-Class level setup/teardown
-----------------------------------
-
-Similarly, the following methods are called at class level before
-and after all test methods of the class are called::
-
- @classmethod
- def setup_class(cls):
- """ setup any state specific to the execution of the given class (which
- usually contains tests).
- """
-
- @classmethod
- def teardown_class(cls):
- """ teardown any state that was previously setup with a call to
- setup_class.
- """
-
-Method and function level setup/teardown
------------------------------------------------
-
-Similarly, the following methods are called around each method invocation::
-
- def setup_method(self, method):
- """ setup any state tied to the execution of the given method in a
- class. setup_method is invoked for every test method of a class.
- """
-
- def teardown_method(self, method):
- """ teardown any state that was previously setup with a setup_method
- call.
- """
-
-If you would rather define test functions directly at module level
-you can also use the following functions to implement fixtures::
-
- def setup_function(function):
- """ setup any state tied to the execution of the given function.
- Invoked for every test function in the module.
- """
-
- def teardown_function(function):
- """ teardown any state that was previously setup with a setup_function
- call.
- """
-
-Note that it is possible for setup/teardown pairs to be invoked multiple times
-per testing process.
-
-.. _`unittest.py module`: http://docs.python.org/library/unittest.html
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/yieldfixture.rst b/tests/wpt/web-platform-tests/tools/pytest/doc/en/yieldfixture.rst
deleted file mode 100644
index ee88a27df59..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/yieldfixture.rst
+++ /dev/null
@@ -1,100 +0,0 @@
-.. _yieldfixture:
-
-Fixture functions using "yield" / context manager integration
----------------------------------------------------------------
-
-.. versionadded:: 2.4
-
-.. regendoc:wipe
-
-pytest-2.4 allows fixture functions to seamlessly use a ``yield`` instead
-of a ``return`` statement to provide a fixture value while otherwise
-fully supporting all other fixture features.
-
-Let's look at a simple standalone-example using the ``yield`` syntax::
-
- # content of test_yield.py
-
- import pytest
-
- @pytest.yield_fixture
- def passwd():
- print ("\nsetup before yield")
- f = open("/etc/passwd")
- yield f.readlines()
- print ("teardown after yield")
- f.close()
-
- def test_has_lines(passwd):
- print ("test called")
- assert passwd
-
-In contrast to :ref:`finalization through registering callbacks
-<finalization>`, our fixture function used a ``yield``
-statement to provide the lines of the ``/etc/passwd`` file.
-The code after the ``yield`` statement serves as the teardown code,
-avoiding the indirection of registering a teardown callback function.
-
-Let's run it with output capturing disabled::
-
- $ py.test -q -s test_yield.py
-
- setup before yield
- test called
- .teardown after yield
-
- 1 passed in 0.12 seconds
-
-We can also seamlessly use the new syntax with ``with`` statements.
-Let's simplify the above ``passwd`` fixture::
-
- # content of test_yield2.py
-
- import pytest
-
- @pytest.yield_fixture
- def passwd():
- with open("/etc/passwd") as f:
- yield f.readlines()
-
- def test_has_lines(passwd):
- assert len(passwd) >= 1
-
-The file ``f`` will be closed after the test finished execution
-because the Python ``file`` object supports finalization when
-the ``with`` statement ends.
-
-Note that the yield fixture form supports all other fixture
-features such as ``scope``, ``params``, etc., thus changing existing
-fixture functions to use ``yield`` is straightforward.
-
-.. note::
-
- While the ``yield`` syntax is similar to what
- :py:func:`contextlib.contextmanager` decorated functions
- provide, with pytest fixture functions the part after the
- "yield" will always be invoked, independently from the
- exception status of the test function which uses the fixture.
- This behaviour makes sense if you consider that many different
- test functions might use a module or session scoped fixture.
-
-
-Discussion and future considerations / feedback
-++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-There are some topics that are worth mentioning:
-
-- usually ``yield`` is used for producing multiple values.
- But fixture functions can only yield exactly one value.
- Yielding a second fixture value will get you an error.
- It's possible we can evolve pytest to allow for producing
- multiple values as an alternative to current parametrization.
- For now, you can just use the normal
- :ref:`fixture parametrization <fixture-parametrize>`
- mechanisms together with ``yield``-style fixtures.
-
-- lastly ``yield`` introduces more than one way to write
- fixture functions, so what's the obvious way to a newcomer?
-
-If you want to feedback or participate in discussion of the above
-topics, please join our :ref:`contact channels`, you are most welcome.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/extra/get_issues.py b/tests/wpt/web-platform-tests/tools/pytest/extra/get_issues.py
deleted file mode 100644
index 6437ba4c34e..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/extra/get_issues.py
+++ /dev/null
@@ -1,74 +0,0 @@
-import json
-import py
-import textwrap
-
-issues_url = "http://bitbucket.org/api/1.0/repositories/pytest-dev/pytest/issues"
-
-import requests
-
-def get_issues():
- chunksize = 50
- start = 0
- issues = []
- while 1:
- post_data = {"accountname": "pytest-dev",
- "repo_slug": "pytest",
- "start": start,
- "limit": chunksize}
- print ("getting from", start)
- r = requests.get(issues_url, params=post_data)
- data = r.json()
- issues.extend(data["issues"])
- if start + chunksize >= data["count"]:
- return issues
- start += chunksize
-
-kind2num = "bug enhancement task proposal".split()
-
-status2num = "new open resolved duplicate invalid wontfix".split()
-
-def main(args):
- cachefile = py.path.local(args.cache)
- if not cachefile.exists() or args.refresh:
- issues = get_issues()
- cachefile.write(json.dumps(issues))
- else:
- issues = json.loads(cachefile.read())
-
- open_issues = [x for x in issues
- if x["status"] in ("new", "open")]
-
- def kind_and_id(x):
- kind = x["metadata"]["kind"]
- return kind2num.index(kind), len(issues)-int(x["local_id"])
- open_issues.sort(key=kind_and_id)
- report(open_issues)
-
-def report(issues):
- for issue in issues:
- metadata = issue["metadata"]
- priority = issue["priority"]
- title = issue["title"]
- content = issue["content"]
- kind = metadata["kind"]
- status = issue["status"]
- id = issue["local_id"]
- link = "https://bitbucket.org/pytest-dev/pytest/issue/%s/" % id
- print("----")
- print(status, kind, link)
- print(title)
- #print()
- #lines = content.split("\n")
- #print ("\n".join(lines[:3]))
- #if len(lines) > 3 or len(content) > 240:
- # print ("...")
-
-if __name__ == "__main__":
- import argparse
- parser = argparse.ArgumentParser("process bitbucket issues")
- parser.add_argument("--refresh", action="store_true",
- help="invalidate cache, refresh issues")
- parser.add_argument("--cache", action="store", default="issues.json",
- help="cache file")
- args = parser.parse_args()
- main(args)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/plugin-test.sh b/tests/wpt/web-platform-tests/tools/pytest/plugin-test.sh
deleted file mode 100644
index 9c61b50536e..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/plugin-test.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-
-# this assumes plugins are installed as sister directories
-
-set -e
-cd ../pytest-pep8
-py.test
-cd ../pytest-instafail
-py.test
-cd ../pytest-cache
-py.test
-cd ../pytest-xprocess
-py.test
-#cd ../pytest-cov
-#py.test
-cd ../pytest-capturelog
-py.test
-cd ../pytest-xdist
-py.test
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/pytest.py b/tests/wpt/web-platform-tests/tools/pytest/pytest.py
deleted file mode 100644
index e376e417e8a..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/pytest.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# PYTHON_ARGCOMPLETE_OK
-"""
-pytest: unit and functional testing with Python.
-"""
-__all__ = [
- 'main',
- 'UsageError',
- 'cmdline',
- 'hookspec',
- 'hookimpl',
- '__version__',
-]
-
-if __name__ == '__main__': # if run as a script or by 'python -m pytest'
- # we trigger the below "else" condition by the following import
- import pytest
- raise SystemExit(pytest.main())
-
-# else we are imported
-
-from _pytest.config import (
- main, UsageError, _preloadplugins, cmdline,
- hookspec, hookimpl
-)
-from _pytest import __version__
-
-_preloadplugins() # to populate pytest.* namespace so help(pytest) works
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/requirements-docs.txt b/tests/wpt/web-platform-tests/tools/pytest/requirements-docs.txt
deleted file mode 100644
index be3a232e57b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/requirements-docs.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-sphinx==1.2.3
-regendoc
-pyyaml
diff --git a/tests/wpt/web-platform-tests/tools/pytest/runtox.py b/tests/wpt/web-platform-tests/tools/pytest/runtox.py
deleted file mode 100644
index 8c13c53e1c4..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/runtox.py
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env python
-
-if __name__ == "__main__":
- import subprocess
- import sys
- subprocess.call([sys.executable, "-m", "tox",
- "-i", "ALL=https://devpi.net/hpk/dev/",
- "--develop"] + sys.argv[1:])
diff --git a/tests/wpt/web-platform-tests/tools/pytest/setup.cfg b/tests/wpt/web-platform-tests/tools/pytest/setup.cfg
deleted file mode 100644
index 1ab4fd059b2..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/setup.cfg
+++ /dev/null
@@ -1,13 +0,0 @@
-[build_sphinx]
-source-dir = doc/en/
-build-dir = doc/build
-all_files = 1
-
-[upload_sphinx]
-upload-dir = doc/en/build/html
-
-[bdist_wheel]
-universal = 1
-
-[devpi:upload]
-formats = sdist.tgz,bdist_wheel
diff --git a/tests/wpt/web-platform-tests/tools/pytest/setup.py b/tests/wpt/web-platform-tests/tools/pytest/setup.py
deleted file mode 100644
index 6660f21604d..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/setup.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import os, sys
-import setuptools
-import pkg_resources
-from setuptools import setup, Command
-
-classifiers = ['Development Status :: 6 - Mature',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: MIT License',
- 'Operating System :: POSIX',
- 'Operating System :: Microsoft :: Windows',
- 'Operating System :: MacOS :: MacOS X',
- 'Topic :: Software Development :: Testing',
- 'Topic :: Software Development :: Libraries',
- 'Topic :: Utilities'] + [
- ('Programming Language :: Python :: %s' % x) for x in
- '2 2.6 2.7 3 3.2 3.3 3.4 3.5'.split()]
-
-with open('README.rst') as fd:
- long_description = fd.read()
-
-def get_version():
- p = os.path.join(os.path.dirname(
- os.path.abspath(__file__)), "_pytest", "__init__.py")
- with open(p) as f:
- for line in f.readlines():
- if "__version__" in line:
- return line.strip().split("=")[-1].strip(" '")
- raise ValueError("could not read version")
-
-
-def has_environment_marker_support():
- """
- Tests that setuptools has support for PEP-426 environment marker support.
-
- The first known release to support it is 0.7 (and the earliest on PyPI seems to be 0.7.2
- so we're using that), see: http://pythonhosted.org/setuptools/history.html#id142
-
- References:
-
- * https://wheel.readthedocs.org/en/latest/index.html#defining-conditional-dependencies
- * https://www.python.org/dev/peps/pep-0426/#environment-markers
- """
- try:
- return pkg_resources.parse_version(setuptools.__version__) >= pkg_resources.parse_version('0.7.2')
- except Exception as exc:
- sys.stderr.write("Could not test setuptool's version: %s\n" % exc)
- return False
-
-
-def main():
- install_requires = ['py>=1.4.29'] # pluggy is vendored in _pytest.vendored_packages
- extras_require = {}
- if has_environment_marker_support():
- extras_require[':python_version=="2.6" or python_version=="3.0" or python_version=="3.1"'] = ['argparse']
- extras_require[':sys_platform=="win32"'] = ['colorama']
- else:
- if sys.version_info < (2, 7) or (3,) <= sys.version_info < (3, 2):
- install_requires.append('argparse')
- if sys.platform == 'win32':
- install_requires.append('colorama')
-
- setup(
- name='pytest',
- description='pytest: simple powerful testing with Python',
- long_description=long_description,
- version=get_version(),
- url='http://pytest.org',
- license='MIT license',
- platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
- author='Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others',
- author_email='holger at merlinux.eu',
- entry_points=make_entry_points(),
- classifiers=classifiers,
- cmdclass={'test': PyTest},
- # the following should be enabled for release
- install_requires=install_requires,
- extras_require=extras_require,
- packages=['_pytest', '_pytest.assertion', '_pytest._code', '_pytest.vendored_packages'],
- py_modules=['pytest'],
- zip_safe=False,
- )
-
-
-def cmdline_entrypoints(versioninfo, platform, basename):
- target = 'pytest:main'
- if platform.startswith('java'):
- points = {'py.test-jython': target}
- else:
- if basename.startswith('pypy'):
- points = {'py.test-%s' % basename: target}
- else: # cpython
- points = {'py.test-%s.%s' % versioninfo[:2] : target}
- points['py.test'] = target
- return points
-
-
-def make_entry_points():
- basename = os.path.basename(sys.executable)
- points = cmdline_entrypoints(sys.version_info, sys.platform, basename)
- keys = list(points.keys())
- keys.sort()
- l = ['%s = %s' % (x, points[x]) for x in keys]
- return {'console_scripts': l}
-
-
-class PyTest(Command):
- user_options = []
- def initialize_options(self):
- pass
- def finalize_options(self):
- pass
- def run(self):
- import subprocess
- PPATH = [x for x in os.environ.get('PYTHONPATH', '').split(':') if x]
- PPATH.insert(0, os.getcwd())
- os.environ['PYTHONPATH'] = ':'.join(PPATH)
- errno = subprocess.call([sys.executable, 'pytest.py', '--ignore=doc'])
- raise SystemExit(errno)
-
-
-if __name__ == '__main__':
- main()
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/acceptance_test.py b/tests/wpt/web-platform-tests/tools/pytest/testing/acceptance_test.py
deleted file mode 100644
index 9bc3a191a53..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/acceptance_test.py
+++ /dev/null
@@ -1,695 +0,0 @@
-import sys
-
-import _pytest._code
-import py
-import pytest
-from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
-
-
-class TestGeneralUsage:
- def test_config_error(self, testdir):
- testdir.makeconftest("""
- def pytest_configure(config):
- import pytest
- raise pytest.UsageError("hello")
- """)
- result = testdir.runpytest(testdir.tmpdir)
- assert result.ret != 0
- result.stderr.fnmatch_lines([
- '*ERROR: hello'
- ])
-
- def test_root_conftest_syntax_error(self, testdir):
- testdir.makepyfile(conftest="raise SyntaxError\n")
- result = testdir.runpytest()
- result.stderr.fnmatch_lines(["*raise SyntaxError*"])
- assert result.ret != 0
-
- def test_early_hook_error_issue38_1(self, testdir):
- testdir.makeconftest("""
- def pytest_sessionstart():
- 0 / 0
- """)
- result = testdir.runpytest(testdir.tmpdir)
- assert result.ret != 0
- # tracestyle is native by default for hook failures
- result.stdout.fnmatch_lines([
- '*INTERNALERROR*File*conftest.py*line 2*',
- '*0 / 0*',
- ])
- result = testdir.runpytest(testdir.tmpdir, "--fulltrace")
- assert result.ret != 0
- # tracestyle is native by default for hook failures
- result.stdout.fnmatch_lines([
- '*INTERNALERROR*def pytest_sessionstart():*',
- '*INTERNALERROR*0 / 0*',
- ])
-
- def test_early_hook_configure_error_issue38(self, testdir):
- testdir.makeconftest("""
- def pytest_configure():
- 0 / 0
- """)
- result = testdir.runpytest(testdir.tmpdir)
- assert result.ret != 0
- # here we get it on stderr
- result.stderr.fnmatch_lines([
- '*INTERNALERROR*File*conftest.py*line 2*',
- '*0 / 0*',
- ])
-
- def test_file_not_found(self, testdir):
- result = testdir.runpytest("asd")
- assert result.ret != 0
- result.stderr.fnmatch_lines(["ERROR: file not found*asd"])
-
- def test_file_not_found_unconfigure_issue143(self, testdir):
- testdir.makeconftest("""
- def pytest_configure():
- print("---configure")
- def pytest_unconfigure():
- print("---unconfigure")
- """)
- result = testdir.runpytest("-s", "asd")
- assert result.ret == 4 # EXIT_USAGEERROR
- result.stderr.fnmatch_lines(["ERROR: file not found*asd"])
- result.stdout.fnmatch_lines([
- "*---configure",
- "*---unconfigure",
- ])
-
-
- def test_config_preparse_plugin_option(self, testdir):
- testdir.makepyfile(pytest_xyz="""
- def pytest_addoption(parser):
- parser.addoption("--xyz", dest="xyz", action="store")
- """)
- testdir.makepyfile(test_one="""
- def test_option(pytestconfig):
- assert pytestconfig.option.xyz == "123"
- """)
- result = testdir.runpytest("-p", "pytest_xyz", "--xyz=123", syspathinsert=True)
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- '*1 passed*',
- ])
-
- def test_assertion_magic(self, testdir):
- p = testdir.makepyfile("""
- def test_this():
- x = 0
- assert x
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "> assert x",
- "E assert 0",
- ])
- assert result.ret == 1
-
- def test_nested_import_error(self, testdir):
- p = testdir.makepyfile("""
- import import_fails
- def test_this():
- assert import_fails.a == 1
- """)
- testdir.makepyfile(import_fails="import does_not_work")
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- #XXX on jython this fails: "> import import_fails",
- "E ImportError: No module named *does_not_work*",
- ])
- assert result.ret == 1
-
- def test_not_collectable_arguments(self, testdir):
- p1 = testdir.makepyfile("")
- p2 = testdir.makefile(".pyc", "123")
- result = testdir.runpytest(p1, p2)
- assert result.ret
- result.stderr.fnmatch_lines([
- "*ERROR: not found:*%s" %(p2.basename,)
- ])
-
- def test_issue486_better_reporting_on_conftest_load_failure(self, testdir):
- testdir.makepyfile("")
- testdir.makeconftest("import qwerty")
- result = testdir.runpytest("--help")
- result.stdout.fnmatch_lines("""
- *--version*
- *warning*conftest.py*
- """)
- result = testdir.runpytest()
- result.stderr.fnmatch_lines("""
- *ERROR*could not load*conftest.py*
- """)
-
-
- def test_early_skip(self, testdir):
- testdir.mkdir("xyz")
- testdir.makeconftest("""
- import pytest
- def pytest_collect_directory():
- pytest.skip("early")
- """)
- result = testdir.runpytest()
- assert result.ret == EXIT_NOTESTSCOLLECTED
- result.stdout.fnmatch_lines([
- "*1 skip*"
- ])
-
- def test_issue88_initial_file_multinodes(self, testdir):
- testdir.makeconftest("""
- import pytest
- class MyFile(pytest.File):
- def collect(self):
- return [MyItem("hello", parent=self)]
- def pytest_collect_file(path, parent):
- return MyFile(path, parent)
- class MyItem(pytest.Item):
- pass
- """)
- p = testdir.makepyfile("def test_hello(): pass")
- result = testdir.runpytest(p, "--collect-only")
- result.stdout.fnmatch_lines([
- "*MyFile*test_issue88*",
- "*Module*test_issue88*",
- ])
-
- def test_issue93_initialnode_importing_capturing(self, testdir):
- testdir.makeconftest("""
- import sys
- print ("should not be seen")
- sys.stderr.write("stder42\\n")
- """)
- result = testdir.runpytest()
- assert result.ret == EXIT_NOTESTSCOLLECTED
- assert "should not be seen" not in result.stdout.str()
- assert "stderr42" not in result.stderr.str()
-
- def test_conftest_printing_shows_if_error(self, testdir):
- testdir.makeconftest("""
- print ("should be seen")
- assert 0
- """)
- result = testdir.runpytest()
- assert result.ret != 0
- assert "should be seen" in result.stdout.str()
-
- @pytest.mark.skipif(not hasattr(py.path.local, 'mksymlinkto'),
- reason="symlink not available on this platform")
- def test_chdir(self, testdir):
- testdir.tmpdir.join("py").mksymlinkto(py._pydir)
- p = testdir.tmpdir.join("main.py")
- p.write(_pytest._code.Source("""
- import sys, os
- sys.path.insert(0, '')
- import py
- print (py.__file__)
- print (py.__path__)
- os.chdir(os.path.dirname(os.getcwd()))
- print (py.log)
- """))
- result = testdir.runpython(p)
- assert not result.ret
-
- def test_issue109_sibling_conftests_not_loaded(self, testdir):
- sub1 = testdir.tmpdir.mkdir("sub1")
- sub2 = testdir.tmpdir.mkdir("sub2")
- sub1.join("conftest.py").write("assert 0")
- result = testdir.runpytest(sub2)
- assert result.ret == EXIT_NOTESTSCOLLECTED
- sub2.ensure("__init__.py")
- p = sub2.ensure("test_hello.py")
- result = testdir.runpytest(p)
- assert result.ret == EXIT_NOTESTSCOLLECTED
- result = testdir.runpytest(sub1)
- assert result.ret == EXIT_USAGEERROR
-
- def test_directory_skipped(self, testdir):
- testdir.makeconftest("""
- import pytest
- def pytest_ignore_collect():
- pytest.skip("intentional")
- """)
- testdir.makepyfile("def test_hello(): pass")
- result = testdir.runpytest()
- assert result.ret == EXIT_NOTESTSCOLLECTED
- result.stdout.fnmatch_lines([
- "*1 skipped*"
- ])
-
- def test_multiple_items_per_collector_byid(self, testdir):
- c = testdir.makeconftest("""
- import pytest
- class MyItem(pytest.Item):
- def runtest(self):
- pass
- class MyCollector(pytest.File):
- def collect(self):
- return [MyItem(name="xyz", parent=self)]
- def pytest_collect_file(path, parent):
- if path.basename.startswith("conftest"):
- return MyCollector(path, parent)
- """)
- result = testdir.runpytest(c.basename+"::"+"xyz")
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*1 pass*",
- ])
-
- def test_skip_on_generated_funcarg_id(self, testdir):
- testdir.makeconftest("""
- import pytest
- def pytest_generate_tests(metafunc):
- metafunc.addcall({'x': 3}, id='hello-123')
- def pytest_runtest_setup(item):
- print (item.keywords)
- if 'hello-123' in item.keywords:
- pytest.skip("hello")
- assert 0
- """)
- p = testdir.makepyfile("""def test_func(x): pass""")
- res = testdir.runpytest(p)
- assert res.ret == 0
- res.stdout.fnmatch_lines(["*1 skipped*"])
-
- def test_direct_addressing_selects(self, testdir):
- p = testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- metafunc.addcall({'i': 1}, id="1")
- metafunc.addcall({'i': 2}, id="2")
- def test_func(i):
- pass
- """)
- res = testdir.runpytest(p.basename + "::" + "test_func[1]")
- assert res.ret == 0
- res.stdout.fnmatch_lines(["*1 passed*"])
-
- def test_direct_addressing_notfound(self, testdir):
- p = testdir.makepyfile("""
- def test_func():
- pass
- """)
- res = testdir.runpytest(p.basename + "::" + "test_notfound")
- assert res.ret
- res.stderr.fnmatch_lines(["*ERROR*not found*"])
-
- def test_docstring_on_hookspec(self):
- from _pytest import hookspec
- for name, value in vars(hookspec).items():
- if name.startswith("pytest_"):
- assert value.__doc__, "no docstring for %s" % name
-
- def test_initialization_error_issue49(self, testdir):
- testdir.makeconftest("""
- def pytest_configure():
- x
- """)
- result = testdir.runpytest()
- assert result.ret == 3 # internal error
- result.stderr.fnmatch_lines([
- "INTERNAL*pytest_configure*",
- "INTERNAL*x*",
- ])
- assert 'sessionstarttime' not in result.stderr.str()
-
- @pytest.mark.parametrize('lookfor', ['test_fun.py', 'test_fun.py::test_a'])
- def test_issue134_report_syntaxerror_when_collecting_member(self, testdir, lookfor):
- testdir.makepyfile(test_fun="""
- def test_a():
- pass
- def""")
- result = testdir.runpytest(lookfor)
- result.stdout.fnmatch_lines(['*SyntaxError*'])
- if '::' in lookfor:
- result.stderr.fnmatch_lines([
- '*ERROR*',
- ])
- assert result.ret == 4 # usage error only if item not found
-
- def test_report_all_failed_collections_initargs(self, testdir):
- testdir.makepyfile(test_a="def", test_b="def")
- result = testdir.runpytest("test_a.py::a", "test_b.py::b")
- result.stderr.fnmatch_lines([
- "*ERROR*test_a.py::a*",
- "*ERROR*test_b.py::b*",
- ])
-
- def test_namespace_import_doesnt_confuse_import_hook(self, testdir):
- # Ref #383. Python 3.3's namespace package messed with our import hooks
- # Importing a module that didn't exist, even if the ImportError was
- # gracefully handled, would make our test crash.
- testdir.mkdir('not_a_package')
- p = testdir.makepyfile("""
- try:
- from not_a_package import doesnt_exist
- except ImportError:
- # We handle the import error gracefully here
- pass
-
- def test_whatever():
- pass
- """)
- res = testdir.runpytest(p.basename)
- assert res.ret == 0
-
- def test_unknown_option(self, testdir):
- result = testdir.runpytest("--qwlkej")
- result.stderr.fnmatch_lines("""
- *unrecognized*
- """)
-
- def test_getsourcelines_error_issue553(self, testdir, monkeypatch):
- monkeypatch.setattr("inspect.getsourcelines", None)
- p = testdir.makepyfile("""
- def raise_error(obj):
- raise IOError('source code not available')
-
- import inspect
- inspect.getsourcelines = raise_error
-
- def test_foo(invalid_fixture):
- pass
- """)
- res = testdir.runpytest(p)
- res.stdout.fnmatch_lines([
- "*source code not available*",
- "*fixture 'invalid_fixture' not found",
- ])
-
- def test_plugins_given_as_strings(self, tmpdir, monkeypatch):
- """test that str values passed to main() as `plugins` arg
- are interpreted as module names to be imported and registered.
- #855.
- """
- with pytest.raises(ImportError) as excinfo:
- pytest.main([str(tmpdir)], plugins=['invalid.module'])
- assert 'invalid' in str(excinfo.value)
-
- p = tmpdir.join('test_test_plugins_given_as_strings.py')
- p.write('def test_foo(): pass')
- mod = py.std.types.ModuleType("myplugin")
- monkeypatch.setitem(sys.modules, 'myplugin', mod)
- assert pytest.main(args=[str(tmpdir)], plugins=['myplugin']) == 0
-
- def test_parameterized_with_bytes_regex(self, testdir):
- p = testdir.makepyfile("""
- import re
- import pytest
- @pytest.mark.parametrize('r', [re.compile(b'foo')])
- def test_stuff(r):
- pass
- """
- )
- res = testdir.runpytest(p)
- res.stdout.fnmatch_lines([
- '*1 passed*'
- ])
-
-
-class TestInvocationVariants:
- def test_earlyinit(self, testdir):
- p = testdir.makepyfile("""
- import pytest
- assert hasattr(pytest, 'mark')
- """)
- result = testdir.runpython(p)
- assert result.ret == 0
-
- @pytest.mark.xfail("sys.platform.startswith('java')")
- def test_pydoc(self, testdir):
- for name in ('py.test', 'pytest'):
- result = testdir.runpython_c("import %s;help(%s)" % (name, name))
- assert result.ret == 0
- s = result.stdout.str()
- assert 'MarkGenerator' in s
-
- def test_import_star_py_dot_test(self, testdir):
- p = testdir.makepyfile("""
- from py.test import *
- #collect
- #cmdline
- #Item
- #assert collect.Item is Item
- #assert collect.Collector is Collector
- main
- skip
- xfail
- """)
- result = testdir.runpython(p)
- assert result.ret == 0
-
- def test_import_star_pytest(self, testdir):
- p = testdir.makepyfile("""
- from pytest import *
- #Item
- #File
- main
- skip
- xfail
- """)
- result = testdir.runpython(p)
- assert result.ret == 0
-
- def test_double_pytestcmdline(self, testdir):
- p = testdir.makepyfile(run="""
- import pytest
- pytest.main()
- pytest.main()
- """)
- testdir.makepyfile("""
- def test_hello():
- pass
- """)
- result = testdir.runpython(p)
- result.stdout.fnmatch_lines([
- "*1 passed*",
- "*1 passed*",
- ])
-
- def test_python_minus_m_invocation_ok(self, testdir):
- p1 = testdir.makepyfile("def test_hello(): pass")
- res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1))
- assert res.ret == 0
-
- def test_python_minus_m_invocation_fail(self, testdir):
- p1 = testdir.makepyfile("def test_fail(): 0/0")
- res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1))
- assert res.ret == 1
-
- def test_python_pytest_package(self, testdir):
- p1 = testdir.makepyfile("def test_pass(): pass")
- res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1))
- assert res.ret == 0
- res.stdout.fnmatch_lines(["*1 passed*"])
-
- def test_equivalence_pytest_pytest(self):
- assert pytest.main == py.test.cmdline.main
-
- def test_invoke_with_string(self, capsys):
- retcode = pytest.main("-h")
- assert not retcode
- out, err = capsys.readouterr()
- assert "--help" in out
- pytest.raises(ValueError, lambda: pytest.main(0))
-
- def test_invoke_with_path(self, tmpdir, capsys):
- retcode = pytest.main(tmpdir)
- assert retcode == EXIT_NOTESTSCOLLECTED
- out, err = capsys.readouterr()
-
- def test_invoke_plugin_api(self, testdir, capsys):
- class MyPlugin:
- def pytest_addoption(self, parser):
- parser.addoption("--myopt")
-
- pytest.main(["-h"], plugins=[MyPlugin()])
- out, err = capsys.readouterr()
- assert "--myopt" in out
-
- def test_pyargs_importerror(self, testdir, monkeypatch):
- monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', False)
- path = testdir.mkpydir("tpkg")
- path.join("test_hello.py").write('raise ImportError')
-
- result = testdir.runpytest("--pyargs", "tpkg.test_hello")
- assert result.ret != 0
- # FIXME: It would be more natural to match NOT
- # "ERROR*file*or*package*not*found*".
- result.stdout.fnmatch_lines([
- "*collected 0 items*"
- ])
-
- def test_cmdline_python_package(self, testdir, monkeypatch):
- monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', False)
- path = testdir.mkpydir("tpkg")
- path.join("test_hello.py").write("def test_hello(): pass")
- path.join("test_world.py").write("def test_world(): pass")
- result = testdir.runpytest("--pyargs", "tpkg")
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*2 passed*"
- ])
- result = testdir.runpytest("--pyargs", "tpkg.test_hello")
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*1 passed*"
- ])
-
- def join_pythonpath(what):
- cur = py.std.os.environ.get('PYTHONPATH')
- if cur:
- return str(what) + ':' + cur
- return what
- empty_package = testdir.mkpydir("empty_package")
- monkeypatch.setenv('PYTHONPATH', join_pythonpath(empty_package))
- result = testdir.runpytest("--pyargs", ".")
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*2 passed*"
- ])
-
- monkeypatch.setenv('PYTHONPATH', join_pythonpath(testdir))
- path.join('test_hello.py').remove()
- result = testdir.runpytest("--pyargs", "tpkg.test_hello")
- assert result.ret != 0
- result.stderr.fnmatch_lines([
- "*not*found*test_hello*",
- ])
-
- def test_cmdline_python_package_not_exists(self, testdir):
- result = testdir.runpytest("--pyargs", "tpkgwhatv")
- assert result.ret
- result.stderr.fnmatch_lines([
- "ERROR*file*or*package*not*found*",
- ])
-
- @pytest.mark.xfail(reason="decide: feature or bug")
- def test_noclass_discovery_if_not_testcase(self, testdir):
- testpath = testdir.makepyfile("""
- import unittest
- class TestHello(object):
- def test_hello(self):
- assert self.attr
-
- class RealTest(unittest.TestCase, TestHello):
- attr = 42
- """)
- reprec = testdir.inline_run(testpath)
- reprec.assertoutcome(passed=1)
-
- def test_doctest_id(self, testdir):
- testdir.makefile('.txt', """
- >>> x=3
- >>> x
- 4
- """)
- result = testdir.runpytest("-rf")
- lines = result.stdout.str().splitlines()
- for line in lines:
- if line.startswith("FAIL "):
- testid = line[5:].strip()
- break
- result = testdir.runpytest(testid, '-rf')
- result.stdout.fnmatch_lines([
- line,
- "*1 failed*",
- ])
-
- def test_core_backward_compatibility(self):
- """Test backward compatibility for get_plugin_manager function. See #787."""
- import _pytest.config
- assert type(_pytest.config.get_plugin_manager()) is _pytest.config.PytestPluginManager
-
-
- def test_has_plugin(self, request):
- """Test hasplugin function of the plugin manager (#932)."""
- assert request.config.pluginmanager.hasplugin('python')
-
-
-class TestDurations:
- source = """
- import time
- frag = 0.002
- def test_something():
- pass
- def test_2():
- time.sleep(frag*5)
- def test_1():
- time.sleep(frag)
- def test_3():
- time.sleep(frag*10)
- """
-
- def test_calls(self, testdir):
- testdir.makepyfile(self.source)
- result = testdir.runpytest("--durations=10")
- assert result.ret == 0
- result.stdout.fnmatch_lines_random([
- "*durations*",
- "*call*test_3*",
- "*call*test_2*",
- "*call*test_1*",
- ])
-
- def test_calls_show_2(self, testdir):
- testdir.makepyfile(self.source)
- result = testdir.runpytest("--durations=2")
- assert result.ret == 0
- lines = result.stdout.get_lines_after("*slowest*durations*")
- assert "4 passed" in lines[2]
-
- def test_calls_showall(self, testdir):
- testdir.makepyfile(self.source)
- result = testdir.runpytest("--durations=0")
- assert result.ret == 0
- for x in "123":
- for y in 'call',: #'setup', 'call', 'teardown':
- for line in result.stdout.lines:
- if ("test_%s" % x) in line and y in line:
- break
- else:
- raise AssertionError("not found %s %s" % (x,y))
-
- def test_with_deselected(self, testdir):
- testdir.makepyfile(self.source)
- result = testdir.runpytest("--durations=2", "-k test_1")
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*durations*",
- "*call*test_1*",
- ])
-
- def test_with_failing_collection(self, testdir):
- testdir.makepyfile(self.source)
- testdir.makepyfile(test_collecterror="""xyz""")
- result = testdir.runpytest("--durations=2", "-k test_1")
- assert result.ret != 0
- result.stdout.fnmatch_lines([
- "*durations*",
- "*call*test_1*",
- ])
-
-
-class TestDurationWithFixture:
- source = """
- import time
- frag = 0.001
- def setup_function(func):
- time.sleep(frag * 3)
- def test_1():
- time.sleep(frag*2)
- def test_2():
- time.sleep(frag)
- """
- def test_setup_function(self, testdir):
- testdir.makepyfile(self.source)
- result = testdir.runpytest("--durations=10")
- assert result.ret == 0
-
- result.stdout.fnmatch_lines_random("""
- *durations*
- * setup *test_1*
- * call *test_1*
- """)
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/code/test_code.py b/tests/wpt/web-platform-tests/tools/pytest/testing/code/test_code.py
deleted file mode 100644
index 0db4ad2ab4c..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/code/test_code.py
+++ /dev/null
@@ -1,174 +0,0 @@
-import sys
-
-import _pytest._code
-import py
-import pytest
-
-
-def test_ne():
- code1 = _pytest._code.Code(compile('foo = "bar"', '', 'exec'))
- assert code1 == code1
- code2 = _pytest._code.Code(compile('foo = "baz"', '', 'exec'))
- assert code2 != code1
-
-def test_code_gives_back_name_for_not_existing_file():
- name = 'abc-123'
- co_code = compile("pass\n", name, 'exec')
- assert co_code.co_filename == name
- code = _pytest._code.Code(co_code)
- assert str(code.path) == name
- assert code.fullsource is None
-
-def test_code_with_class():
- class A:
- pass
- pytest.raises(TypeError, "_pytest._code.Code(A)")
-
-if True:
- def x():
- pass
-
-def test_code_fullsource():
- code = _pytest._code.Code(x)
- full = code.fullsource
- assert 'test_code_fullsource()' in str(full)
-
-def test_code_source():
- code = _pytest._code.Code(x)
- src = code.source()
- expected = """def x():
- pass"""
- assert str(src) == expected
-
-def test_frame_getsourcelineno_myself():
- def func():
- return sys._getframe(0)
- f = func()
- f = _pytest._code.Frame(f)
- source, lineno = f.code.fullsource, f.lineno
- assert source[lineno].startswith(" return sys._getframe(0)")
-
-def test_getstatement_empty_fullsource():
- def func():
- return sys._getframe(0)
- f = func()
- f = _pytest._code.Frame(f)
- prop = f.code.__class__.fullsource
- try:
- f.code.__class__.fullsource = None
- assert f.statement == _pytest._code.Source("")
- finally:
- f.code.__class__.fullsource = prop
-
-def test_code_from_func():
- co = _pytest._code.Code(test_frame_getsourcelineno_myself)
- assert co.firstlineno
- assert co.path
-
-
-
-def test_builtin_patch_unpatch(monkeypatch):
- cpy_builtin = py.builtin.builtins
- comp = cpy_builtin.compile
- def mycompile(*args, **kwargs):
- return comp(*args, **kwargs)
- class Sub(AssertionError):
- pass
- monkeypatch.setattr(cpy_builtin, 'AssertionError', Sub)
- monkeypatch.setattr(cpy_builtin, 'compile', mycompile)
- _pytest._code.patch_builtins()
- assert cpy_builtin.AssertionError != Sub
- assert cpy_builtin.compile != mycompile
- _pytest._code.unpatch_builtins()
- assert cpy_builtin.AssertionError is Sub
- assert cpy_builtin.compile == mycompile
-
-
-def test_unicode_handling():
- value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
- def f():
- raise Exception(value)
- excinfo = pytest.raises(Exception, f)
- str(excinfo)
- if sys.version_info[0] < 3:
- unicode(excinfo)
-
-
-@pytest.mark.skipif(sys.version_info[0] >= 3, reason='python 2 only issue')
-def test_unicode_handling_syntax_error():
- value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
- def f():
- raise SyntaxError('invalid syntax', (None, 1, 3, value))
- excinfo = pytest.raises(Exception, f)
- str(excinfo)
- if sys.version_info[0] < 3:
- unicode(excinfo)
-
-def test_code_getargs():
- def f1(x):
- pass
- c1 = _pytest._code.Code(f1)
- assert c1.getargs(var=True) == ('x',)
-
- def f2(x, *y):
- pass
- c2 = _pytest._code.Code(f2)
- assert c2.getargs(var=True) == ('x', 'y')
-
- def f3(x, **z):
- pass
- c3 = _pytest._code.Code(f3)
- assert c3.getargs(var=True) == ('x', 'z')
-
- def f4(x, *y, **z):
- pass
- c4 = _pytest._code.Code(f4)
- assert c4.getargs(var=True) == ('x', 'y', 'z')
-
-
-def test_frame_getargs():
- def f1(x):
- return sys._getframe(0)
- fr1 = _pytest._code.Frame(f1('a'))
- assert fr1.getargs(var=True) == [('x', 'a')]
-
- def f2(x, *y):
- return sys._getframe(0)
- fr2 = _pytest._code.Frame(f2('a', 'b', 'c'))
- assert fr2.getargs(var=True) == [('x', 'a'), ('y', ('b', 'c'))]
-
- def f3(x, **z):
- return sys._getframe(0)
- fr3 = _pytest._code.Frame(f3('a', b='c'))
- assert fr3.getargs(var=True) == [('x', 'a'), ('z', {'b': 'c'})]
-
- def f4(x, *y, **z):
- return sys._getframe(0)
- fr4 = _pytest._code.Frame(f4('a', 'b', c='d'))
- assert fr4.getargs(var=True) == [('x', 'a'), ('y', ('b',)),
- ('z', {'c': 'd'})]
-
-
-class TestExceptionInfo:
-
- def test_bad_getsource(self):
- try:
- if False: pass
- else: assert False
- except AssertionError:
- exci = _pytest._code.ExceptionInfo()
- assert exci.getrepr()
-
-
-class TestTracebackEntry:
-
- def test_getsource(self):
- try:
- if False: pass
- else: assert False
- except AssertionError:
- exci = _pytest._code.ExceptionInfo()
- entry = exci.traceback[0]
- source = entry.getsource()
- assert len(source) == 4
- assert 'else: assert False' in source[3]
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/code/test_excinfo.py b/tests/wpt/web-platform-tests/tools/pytest/testing/code/test_excinfo.py
deleted file mode 100644
index 2defa31035b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/code/test_excinfo.py
+++ /dev/null
@@ -1,911 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import _pytest
-import py
-import pytest
-from _pytest._code.code import FormattedExcinfo, ReprExceptionInfo
-
-queue = py.builtin._tryimport('queue', 'Queue')
-
-failsonjython = pytest.mark.xfail("sys.platform.startswith('java')")
-from test_source import astonly
-
-try:
- import importlib
-except ImportError:
- invalidate_import_caches = None
-else:
- invalidate_import_caches = getattr(importlib, "invalidate_caches", None)
-
-import pytest
-pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
-
-class TWMock:
- def __init__(self):
- self.lines = []
- def sep(self, sep, line=None):
- self.lines.append((sep, line))
- def line(self, line, **kw):
- self.lines.append(line)
- def markup(self, text, **kw):
- return text
-
- fullwidth = 80
-
-def test_excinfo_simple():
- try:
- raise ValueError
- except ValueError:
- info = _pytest._code.ExceptionInfo()
- assert info.type == ValueError
-
-def test_excinfo_getstatement():
- def g():
- raise ValueError
- def f():
- g()
- try:
- f()
- except ValueError:
- excinfo = _pytest._code.ExceptionInfo()
- linenumbers = [_pytest._code.getrawcode(f).co_firstlineno - 1 + 3,
- _pytest._code.getrawcode(f).co_firstlineno - 1 + 1,
- _pytest._code.getrawcode(g).co_firstlineno - 1 + 1, ]
- l = list(excinfo.traceback)
- foundlinenumbers = [x.lineno for x in l]
- assert foundlinenumbers == linenumbers
- #for x in info:
- # print "%s:%d %s" %(x.path.relto(root), x.lineno, x.statement)
- #xxx
-
-# testchain for getentries test below
-def f():
- #
- raise ValueError
- #
-def g():
- #
- __tracebackhide__ = True
- f()
- #
-def h():
- #
- g()
- #
-
-class TestTraceback_f_g_h:
- def setup_method(self, method):
- try:
- h()
- except ValueError:
- self.excinfo = _pytest._code.ExceptionInfo()
-
- def test_traceback_entries(self):
- tb = self.excinfo.traceback
- entries = list(tb)
- assert len(tb) == 4 # maybe fragile test
- assert len(entries) == 4 # maybe fragile test
- names = ['f', 'g', 'h']
- for entry in entries:
- try:
- names.remove(entry.frame.code.name)
- except ValueError:
- pass
- assert not names
-
- def test_traceback_entry_getsource(self):
- tb = self.excinfo.traceback
- s = str(tb[-1].getsource() )
- assert s.startswith("def f():")
- assert s.endswith("raise ValueError")
-
- @astonly
- @failsonjython
- def test_traceback_entry_getsource_in_construct(self):
- source = _pytest._code.Source("""\
- def xyz():
- try:
- raise ValueError
- except somenoname:
- pass
- xyz()
- """)
- try:
- exec (source.compile())
- except NameError:
- tb = _pytest._code.ExceptionInfo().traceback
- print (tb[-1].getsource())
- s = str(tb[-1].getsource())
- assert s.startswith("def xyz():\n try:")
- assert s.strip().endswith("except somenoname:")
-
- def test_traceback_cut(self):
- co = _pytest._code.Code(f)
- path, firstlineno = co.path, co.firstlineno
- traceback = self.excinfo.traceback
- newtraceback = traceback.cut(path=path, firstlineno=firstlineno)
- assert len(newtraceback) == 1
- newtraceback = traceback.cut(path=path, lineno=firstlineno+2)
- assert len(newtraceback) == 1
-
- def test_traceback_cut_excludepath(self, testdir):
- p = testdir.makepyfile("def f(): raise ValueError")
- excinfo = pytest.raises(ValueError, "p.pyimport().f()")
- basedir = py.path.local(pytest.__file__).dirpath()
- newtraceback = excinfo.traceback.cut(excludepath=basedir)
- for x in newtraceback:
- if hasattr(x, 'path'):
- assert not py.path.local(x.path).relto(basedir)
- assert newtraceback[-1].frame.code.path == p
-
- def test_traceback_filter(self):
- traceback = self.excinfo.traceback
- ntraceback = traceback.filter()
- assert len(ntraceback) == len(traceback) - 1
-
- def test_traceback_recursion_index(self):
- def f(n):
- if n < 10:
- n += 1
- f(n)
- excinfo = pytest.raises(RuntimeError, f, 8)
- traceback = excinfo.traceback
- recindex = traceback.recursionindex()
- assert recindex == 3
-
- def test_traceback_only_specific_recursion_errors(self, monkeypatch):
- def f(n):
- if n == 0:
- raise RuntimeError("hello")
- f(n-1)
-
- excinfo = pytest.raises(RuntimeError, f, 100)
- monkeypatch.delattr(excinfo.traceback.__class__, "recursionindex")
- repr = excinfo.getrepr()
- assert "RuntimeError: hello" in str(repr.reprcrash)
-
- def test_traceback_no_recursion_index(self):
- def do_stuff():
- raise RuntimeError
- def reraise_me():
- import sys
- exc, val, tb = sys.exc_info()
- py.builtin._reraise(exc, val, tb)
- def f(n):
- try:
- do_stuff()
- except:
- reraise_me()
- excinfo = pytest.raises(RuntimeError, f, 8)
- traceback = excinfo.traceback
- recindex = traceback.recursionindex()
- assert recindex is None
-
- def test_traceback_messy_recursion(self):
- #XXX: simplified locally testable version
- decorator = pytest.importorskip('decorator').decorator
-
- def log(f, *k, **kw):
- print('%s %s' % (k, kw))
- f(*k, **kw)
- log = decorator(log)
-
- def fail():
- raise ValueError('')
-
- fail = log(log(fail))
-
- excinfo = pytest.raises(ValueError, fail)
- assert excinfo.traceback.recursionindex() is None
-
-
-
- def test_traceback_getcrashentry(self):
- def i():
- __tracebackhide__ = True
- raise ValueError
- def h():
- i()
- def g():
- __tracebackhide__ = True
- h()
- def f():
- g()
-
- excinfo = pytest.raises(ValueError, f)
- tb = excinfo.traceback
- entry = tb.getcrashentry()
- co = _pytest._code.Code(h)
- assert entry.frame.code.path == co.path
- assert entry.lineno == co.firstlineno + 1
- assert entry.frame.code.name == 'h'
-
- def test_traceback_getcrashentry_empty(self):
- def g():
- __tracebackhide__ = True
- raise ValueError
- def f():
- __tracebackhide__ = True
- g()
-
- excinfo = pytest.raises(ValueError, f)
- tb = excinfo.traceback
- entry = tb.getcrashentry()
- co = _pytest._code.Code(g)
- assert entry.frame.code.path == co.path
- assert entry.lineno == co.firstlineno + 2
- assert entry.frame.code.name == 'g'
-
-def hello(x):
- x + 5
-
-def test_tbentry_reinterpret():
- try:
- hello("hello")
- except TypeError:
- excinfo = _pytest._code.ExceptionInfo()
- tbentry = excinfo.traceback[-1]
- msg = tbentry.reinterpret()
- assert msg.startswith("TypeError: ('hello' + 5)")
-
-def test_excinfo_exconly():
- excinfo = pytest.raises(ValueError, h)
- assert excinfo.exconly().startswith('ValueError')
- excinfo = pytest.raises(ValueError,
- "raise ValueError('hello\\nworld')")
- msg = excinfo.exconly(tryshort=True)
- assert msg.startswith('ValueError')
- assert msg.endswith("world")
-
-def test_excinfo_repr():
- excinfo = pytest.raises(ValueError, h)
- s = repr(excinfo)
- assert s == "<ExceptionInfo ValueError tblen=4>"
-
-def test_excinfo_str():
- excinfo = pytest.raises(ValueError, h)
- s = str(excinfo)
- assert s.startswith(__file__[:-9]) # pyc file and $py.class
- assert s.endswith("ValueError")
- assert len(s.split(":")) >= 3 # on windows it's 4
-
-def test_excinfo_errisinstance():
- excinfo = pytest.raises(ValueError, h)
- assert excinfo.errisinstance(ValueError)
-
-def test_excinfo_no_sourcecode():
- try:
- exec ("raise ValueError()")
- except ValueError:
- excinfo = _pytest._code.ExceptionInfo()
- s = str(excinfo.traceback[-1])
- if py.std.sys.version_info < (2,5):
- assert s == " File '<string>':1 in ?\n ???\n"
- else:
- assert s == " File '<string>':1 in <module>\n ???\n"
-
-def test_excinfo_no_python_sourcecode(tmpdir):
- #XXX: simplified locally testable version
- tmpdir.join('test.txt').write("{{ h()}}:")
-
- jinja2 = pytest.importorskip('jinja2')
- loader = jinja2.FileSystemLoader(str(tmpdir))
- env = jinja2.Environment(loader=loader)
- template = env.get_template('test.txt')
- excinfo = pytest.raises(ValueError,
- template.render, h=h)
- for item in excinfo.traceback:
- print(item) #XXX: for some reason jinja.Template.render is printed in full
- item.source # shouldnt fail
- if item.path.basename == 'test.txt':
- assert str(item.source) == '{{ h()}}:'
-
-
-def test_entrysource_Queue_example():
- try:
- queue.Queue().get(timeout=0.001)
- except queue.Empty:
- excinfo = _pytest._code.ExceptionInfo()
- entry = excinfo.traceback[-1]
- source = entry.getsource()
- assert source is not None
- s = str(source).strip()
- assert s.startswith("def get")
-
-def test_codepath_Queue_example():
- try:
- queue.Queue().get(timeout=0.001)
- except queue.Empty:
- excinfo = _pytest._code.ExceptionInfo()
- entry = excinfo.traceback[-1]
- path = entry.path
- assert isinstance(path, py.path.local)
- assert path.basename.lower() == "queue.py"
- assert path.check()
-
-class TestFormattedExcinfo:
- def pytest_funcarg__importasmod(self, request):
- def importasmod(source):
- source = _pytest._code.Source(source)
- tmpdir = request.getfuncargvalue("tmpdir")
- modpath = tmpdir.join("mod.py")
- tmpdir.ensure("__init__.py")
- modpath.write(source)
- if invalidate_import_caches is not None:
- invalidate_import_caches()
- return modpath.pyimport()
- return importasmod
-
- def excinfo_from_exec(self, source):
- source = _pytest._code.Source(source).strip()
- try:
- exec (source.compile())
- except KeyboardInterrupt:
- raise
- except:
- return _pytest._code.ExceptionInfo()
- assert 0, "did not raise"
-
- def test_repr_source(self):
- pr = FormattedExcinfo()
- source = _pytest._code.Source("""
- def f(x):
- pass
- """).strip()
- pr.flow_marker = "|"
- lines = pr.get_source(source, 0)
- assert len(lines) == 2
- assert lines[0] == "| def f(x):"
- assert lines[1] == " pass"
-
- def test_repr_source_excinfo(self):
- """ check if indentation is right """
- pr = FormattedExcinfo()
- excinfo = self.excinfo_from_exec("""
- def f():
- assert 0
- f()
- """)
- pr = FormattedExcinfo()
- source = pr._getentrysource(excinfo.traceback[-1])
- lines = pr.get_source(source, 1, excinfo)
- assert lines == [
- ' def f():',
- '> assert 0',
- 'E assert 0'
- ]
-
-
- def test_repr_source_not_existing(self):
- pr = FormattedExcinfo()
- co = compile("raise ValueError()", "", "exec")
- try:
- exec (co)
- except ValueError:
- excinfo = _pytest._code.ExceptionInfo()
- repr = pr.repr_excinfo(excinfo)
- assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
-
- def test_repr_many_line_source_not_existing(self):
- pr = FormattedExcinfo()
- co = compile("""
-a = 1
-raise ValueError()
-""", "", "exec")
- try:
- exec (co)
- except ValueError:
- excinfo = _pytest._code.ExceptionInfo()
- repr = pr.repr_excinfo(excinfo)
- assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
-
- def test_repr_source_failing_fullsource(self):
- pr = FormattedExcinfo()
-
- class FakeCode(object):
- class raw:
- co_filename = '?'
- path = '?'
- firstlineno = 5
-
- def fullsource(self):
- return None
- fullsource = property(fullsource)
-
- class FakeFrame(object):
- code = FakeCode()
- f_locals = {}
- f_globals = {}
-
- class FakeTracebackEntry(_pytest._code.Traceback.Entry):
- def __init__(self, tb):
- self.lineno = 5+3
-
- @property
- def frame(self):
- return FakeFrame()
-
- class Traceback(_pytest._code.Traceback):
- Entry = FakeTracebackEntry
-
- class FakeExcinfo(_pytest._code.ExceptionInfo):
- typename = "Foo"
- def __init__(self):
- pass
-
- def exconly(self, tryshort):
- return "EXC"
- def errisinstance(self, cls):
- return False
-
- excinfo = FakeExcinfo()
- class FakeRawTB(object):
- tb_next = None
- tb = FakeRawTB()
- excinfo.traceback = Traceback(tb)
-
- fail = IOError() # noqa
- repr = pr.repr_excinfo(excinfo)
- assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
-
- fail = py.error.ENOENT # noqa
- repr = pr.repr_excinfo(excinfo)
- assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
-
-
- def test_repr_local(self):
- p = FormattedExcinfo(showlocals=True)
- loc = {'y': 5, 'z': 7, 'x': 3, '@x': 2, '__builtins__': {}}
- reprlocals = p.repr_locals(loc)
- assert reprlocals.lines
- assert reprlocals.lines[0] == '__builtins__ = <builtins>'
- assert reprlocals.lines[1] == 'x = 3'
- assert reprlocals.lines[2] == 'y = 5'
- assert reprlocals.lines[3] == 'z = 7'
-
- def test_repr_tracebackentry_lines(self, importasmod):
- mod = importasmod("""
- def func1():
- raise ValueError("hello\\nworld")
- """)
- excinfo = pytest.raises(ValueError, mod.func1)
- excinfo.traceback = excinfo.traceback.filter()
- p = FormattedExcinfo()
- reprtb = p.repr_traceback_entry(excinfo.traceback[-1])
-
- # test as intermittent entry
- lines = reprtb.lines
- assert lines[0] == ' def func1():'
- assert lines[1] == '> raise ValueError("hello\\nworld")'
-
- # test as last entry
- p = FormattedExcinfo(showlocals=True)
- repr_entry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
- lines = repr_entry.lines
- assert lines[0] == ' def func1():'
- assert lines[1] == '> raise ValueError("hello\\nworld")'
- assert lines[2] == 'E ValueError: hello'
- assert lines[3] == 'E world'
- assert not lines[4:]
-
- loc = repr_entry.reprlocals is not None
- loc = repr_entry.reprfileloc
- assert loc.path == mod.__file__
- assert loc.lineno == 3
- #assert loc.message == "ValueError: hello"
-
- def test_repr_tracebackentry_lines2(self, importasmod):
- mod = importasmod("""
- def func1(m, x, y, z):
- raise ValueError("hello\\nworld")
- """)
- excinfo = pytest.raises(ValueError, mod.func1, "m"*90, 5, 13, "z"*120)
- excinfo.traceback = excinfo.traceback.filter()
- entry = excinfo.traceback[-1]
- p = FormattedExcinfo(funcargs=True)
- reprfuncargs = p.repr_args(entry)
- assert reprfuncargs.args[0] == ('m', repr("m"*90))
- assert reprfuncargs.args[1] == ('x', '5')
- assert reprfuncargs.args[2] == ('y', '13')
- assert reprfuncargs.args[3] == ('z', repr("z" * 120))
-
- p = FormattedExcinfo(funcargs=True)
- repr_entry = p.repr_traceback_entry(entry)
- assert repr_entry.reprfuncargs.args == reprfuncargs.args
- tw = TWMock()
- repr_entry.toterminal(tw)
- assert tw.lines[0] == "m = " + repr('m' * 90)
- assert tw.lines[1] == "x = 5, y = 13"
- assert tw.lines[2] == "z = " + repr('z' * 120)
-
- def test_repr_tracebackentry_lines_var_kw_args(self, importasmod):
- mod = importasmod("""
- def func1(x, *y, **z):
- raise ValueError("hello\\nworld")
- """)
- excinfo = pytest.raises(ValueError, mod.func1, 'a', 'b', c='d')
- excinfo.traceback = excinfo.traceback.filter()
- entry = excinfo.traceback[-1]
- p = FormattedExcinfo(funcargs=True)
- reprfuncargs = p.repr_args(entry)
- assert reprfuncargs.args[0] == ('x', repr('a'))
- assert reprfuncargs.args[1] == ('y', repr(('b',)))
- assert reprfuncargs.args[2] == ('z', repr({'c': 'd'}))
-
- p = FormattedExcinfo(funcargs=True)
- repr_entry = p.repr_traceback_entry(entry)
- assert repr_entry.reprfuncargs.args == reprfuncargs.args
- tw = TWMock()
- repr_entry.toterminal(tw)
- assert tw.lines[0] == "x = 'a', y = ('b',), z = {'c': 'd'}"
-
- def test_repr_tracebackentry_short(self, importasmod):
- mod = importasmod("""
- def func1():
- raise ValueError("hello")
- def entry():
- func1()
- """)
- excinfo = pytest.raises(ValueError, mod.entry)
- p = FormattedExcinfo(style="short")
- reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
- lines = reprtb.lines
- basename = py.path.local(mod.__file__).basename
- assert lines[0] == ' func1()'
- assert basename in str(reprtb.reprfileloc.path)
- assert reprtb.reprfileloc.lineno == 5
-
- # test last entry
- p = FormattedExcinfo(style="short")
- reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
- lines = reprtb.lines
- assert lines[0] == ' raise ValueError("hello")'
- assert lines[1] == 'E ValueError: hello'
- assert basename in str(reprtb.reprfileloc.path)
- assert reprtb.reprfileloc.lineno == 3
-
- def test_repr_tracebackentry_no(self, importasmod):
- mod = importasmod("""
- def func1():
- raise ValueError("hello")
- def entry():
- func1()
- """)
- excinfo = pytest.raises(ValueError, mod.entry)
- p = FormattedExcinfo(style="no")
- p.repr_traceback_entry(excinfo.traceback[-2])
-
- p = FormattedExcinfo(style="no")
- reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
- lines = reprentry.lines
- assert lines[0] == 'E ValueError: hello'
- assert not lines[1:]
-
- def test_repr_traceback_tbfilter(self, importasmod):
- mod = importasmod("""
- def f(x):
- raise ValueError(x)
- def entry():
- f(0)
- """)
- excinfo = pytest.raises(ValueError, mod.entry)
- p = FormattedExcinfo(tbfilter=True)
- reprtb = p.repr_traceback(excinfo)
- assert len(reprtb.reprentries) == 2
- p = FormattedExcinfo(tbfilter=False)
- reprtb = p.repr_traceback(excinfo)
- assert len(reprtb.reprentries) == 3
-
- def test_traceback_short_no_source(self, importasmod, monkeypatch):
- mod = importasmod("""
- def func1():
- raise ValueError("hello")
- def entry():
- func1()
- """)
- excinfo = pytest.raises(ValueError, mod.entry)
- from _pytest._code.code import Code
- monkeypatch.setattr(Code, 'path', 'bogus')
- excinfo.traceback[0].frame.code.path = "bogus"
- p = FormattedExcinfo(style="short")
- reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
- lines = reprtb.lines
- last_p = FormattedExcinfo(style="short")
- last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
- last_lines = last_reprtb.lines
- monkeypatch.undo()
- assert lines[0] == ' func1()'
-
- assert last_lines[0] == ' raise ValueError("hello")'
- assert last_lines[1] == 'E ValueError: hello'
-
- def test_repr_traceback_and_excinfo(self, importasmod):
- mod = importasmod("""
- def f(x):
- raise ValueError(x)
- def entry():
- f(0)
- """)
- excinfo = pytest.raises(ValueError, mod.entry)
-
- for style in ("long", "short"):
- p = FormattedExcinfo(style=style)
- reprtb = p.repr_traceback(excinfo)
- assert len(reprtb.reprentries) == 2
- assert reprtb.style == style
- assert not reprtb.extraline
- repr = p.repr_excinfo(excinfo)
- assert repr.reprtraceback
- assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries)
- assert repr.reprcrash.path.endswith("mod.py")
- assert repr.reprcrash.message == "ValueError: 0"
-
- def test_repr_traceback_with_invalid_cwd(self, importasmod, monkeypatch):
- mod = importasmod("""
- def f(x):
- raise ValueError(x)
- def entry():
- f(0)
- """)
- excinfo = pytest.raises(ValueError, mod.entry)
-
- p = FormattedExcinfo()
- def raiseos():
- raise OSError(2)
- monkeypatch.setattr(py.std.os, 'getcwd', raiseos)
- assert p._makepath(__file__) == __file__
- p.repr_traceback(excinfo)
-
- def test_repr_excinfo_addouterr(self, importasmod):
- mod = importasmod("""
- def entry():
- raise ValueError()
- """)
- excinfo = pytest.raises(ValueError, mod.entry)
- repr = excinfo.getrepr()
- repr.addsection("title", "content")
- twmock = TWMock()
- repr.toterminal(twmock)
- assert twmock.lines[-1] == "content"
- assert twmock.lines[-2] == ("-", "title")
-
- def test_repr_excinfo_reprcrash(self, importasmod):
- mod = importasmod("""
- def entry():
- raise ValueError()
- """)
- excinfo = pytest.raises(ValueError, mod.entry)
- repr = excinfo.getrepr()
- assert repr.reprcrash.path.endswith("mod.py")
- assert repr.reprcrash.lineno == 3
- assert repr.reprcrash.message == "ValueError"
- assert str(repr.reprcrash).endswith("mod.py:3: ValueError")
-
- def test_repr_traceback_recursion(self, importasmod):
- mod = importasmod("""
- def rec2(x):
- return rec1(x+1)
- def rec1(x):
- return rec2(x-1)
- def entry():
- rec1(42)
- """)
- excinfo = pytest.raises(RuntimeError, mod.entry)
-
- for style in ("short", "long", "no"):
- p = FormattedExcinfo(style="short")
- reprtb = p.repr_traceback(excinfo)
- assert reprtb.extraline == "!!! Recursion detected (same locals & position)"
- assert str(reprtb)
-
- def test_tb_entry_AssertionError(self, importasmod):
- # probably this test is a bit redundant
- # as py/magic/testing/test_assertion.py
- # already tests correctness of
- # assertion-reinterpretation logic
- mod = importasmod("""
- def somefunc():
- x = 1
- assert x == 2
- """)
- excinfo = pytest.raises(AssertionError, mod.somefunc)
-
- p = FormattedExcinfo()
- reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
- lines = reprentry.lines
- assert lines[-1] == "E assert 1 == 2"
-
- def test_reprexcinfo_getrepr(self, importasmod):
- mod = importasmod("""
- def f(x):
- raise ValueError(x)
- def entry():
- f(0)
- """)
- excinfo = pytest.raises(ValueError, mod.entry)
-
- for style in ("short", "long", "no"):
- for showlocals in (True, False):
- repr = excinfo.getrepr(style=style, showlocals=showlocals)
- assert isinstance(repr, ReprExceptionInfo)
- assert repr.reprtraceback.style == style
-
- def test_reprexcinfo_unicode(self):
- from _pytest._code.code import TerminalRepr
- class MyRepr(TerminalRepr):
- def toterminal(self, tw):
- tw.line(py.builtin._totext("я", "utf-8"))
- x = py.builtin._totext(MyRepr())
- assert x == py.builtin._totext("я", "utf-8")
-
- def test_toterminal_long(self, importasmod):
- mod = importasmod("""
- def g(x):
- raise ValueError(x)
- def f():
- g(3)
- """)
- excinfo = pytest.raises(ValueError, mod.f)
- excinfo.traceback = excinfo.traceback.filter()
- repr = excinfo.getrepr()
- tw = TWMock()
- repr.toterminal(tw)
- assert tw.lines[0] == ""
- tw.lines.pop(0)
- assert tw.lines[0] == " def f():"
- assert tw.lines[1] == "> g(3)"
- assert tw.lines[2] == ""
- assert tw.lines[3].endswith("mod.py:5: ")
- assert tw.lines[4] == ("_ ", None)
- assert tw.lines[5] == ""
- assert tw.lines[6] == " def g(x):"
- assert tw.lines[7] == "> raise ValueError(x)"
- assert tw.lines[8] == "E ValueError: 3"
- assert tw.lines[9] == ""
- assert tw.lines[10].endswith("mod.py:3: ValueError")
-
- def test_toterminal_long_missing_source(self, importasmod, tmpdir):
- mod = importasmod("""
- def g(x):
- raise ValueError(x)
- def f():
- g(3)
- """)
- excinfo = pytest.raises(ValueError, mod.f)
- tmpdir.join('mod.py').remove()
- excinfo.traceback = excinfo.traceback.filter()
- repr = excinfo.getrepr()
- tw = TWMock()
- repr.toterminal(tw)
- assert tw.lines[0] == ""
- tw.lines.pop(0)
- assert tw.lines[0] == "> ???"
- assert tw.lines[1] == ""
- assert tw.lines[2].endswith("mod.py:5: ")
- assert tw.lines[3] == ("_ ", None)
- assert tw.lines[4] == ""
- assert tw.lines[5] == "> ???"
- assert tw.lines[6] == "E ValueError: 3"
- assert tw.lines[7] == ""
- assert tw.lines[8].endswith("mod.py:3: ValueError")
-
- def test_toterminal_long_incomplete_source(self, importasmod, tmpdir):
- mod = importasmod("""
- def g(x):
- raise ValueError(x)
- def f():
- g(3)
- """)
- excinfo = pytest.raises(ValueError, mod.f)
- tmpdir.join('mod.py').write('asdf')
- excinfo.traceback = excinfo.traceback.filter()
- repr = excinfo.getrepr()
- tw = TWMock()
- repr.toterminal(tw)
- assert tw.lines[0] == ""
- tw.lines.pop(0)
- assert tw.lines[0] == "> ???"
- assert tw.lines[1] == ""
- assert tw.lines[2].endswith("mod.py:5: ")
- assert tw.lines[3] == ("_ ", None)
- assert tw.lines[4] == ""
- assert tw.lines[5] == "> ???"
- assert tw.lines[6] == "E ValueError: 3"
- assert tw.lines[7] == ""
- assert tw.lines[8].endswith("mod.py:3: ValueError")
-
- def test_toterminal_long_filenames(self, importasmod):
- mod = importasmod("""
- def f():
- raise ValueError()
- """)
- excinfo = pytest.raises(ValueError, mod.f)
- tw = TWMock()
- path = py.path.local(mod.__file__)
- old = path.dirpath().chdir()
- try:
- repr = excinfo.getrepr(abspath=False)
- repr.toterminal(tw)
- line = tw.lines[-1]
- x = py.path.local().bestrelpath(path)
- if len(x) < len(str(path)):
- assert line == "mod.py:3: ValueError"
-
- repr = excinfo.getrepr(abspath=True)
- repr.toterminal(tw)
- line = tw.lines[-1]
- assert line == "%s:3: ValueError" %(path,)
- finally:
- old.chdir()
-
- @pytest.mark.parametrize('reproptions', [
- {'style': style, 'showlocals': showlocals,
- 'funcargs': funcargs, 'tbfilter': tbfilter
- } for style in ("long", "short", "no")
- for showlocals in (True, False)
- for tbfilter in (True, False)
- for funcargs in (True, False)])
- def test_format_excinfo(self, importasmod, reproptions):
- mod = importasmod("""
- def g(x):
- raise ValueError(x)
- def f():
- g(3)
- """)
- excinfo = pytest.raises(ValueError, mod.f)
- tw = py.io.TerminalWriter(stringio=True)
- repr = excinfo.getrepr(**reproptions)
- repr.toterminal(tw)
- assert tw.stringio.getvalue()
-
-
- def test_native_style(self):
- excinfo = self.excinfo_from_exec("""
- assert 0
- """)
- repr = excinfo.getrepr(style='native')
- assert "assert 0" in str(repr.reprcrash)
- s = str(repr)
- assert s.startswith('Traceback (most recent call last):\n File')
- assert s.endswith('\nAssertionError: assert 0')
- assert 'exec (source.compile())' in s
- # python 2.4 fails to get the source line for the assert
- if py.std.sys.version_info >= (2, 5):
- assert s.count('assert 0') == 2
-
- def test_traceback_repr_style(self, importasmod):
- mod = importasmod("""
- def f():
- g()
- def g():
- h()
- def h():
- i()
- def i():
- raise ValueError()
- """)
- excinfo = pytest.raises(ValueError, mod.f)
- excinfo.traceback = excinfo.traceback.filter()
- excinfo.traceback[1].set_repr_style("short")
- excinfo.traceback[2].set_repr_style("short")
- r = excinfo.getrepr(style="long")
- tw = TWMock()
- r.toterminal(tw)
- for line in tw.lines: print (line)
- assert tw.lines[0] == ""
- assert tw.lines[1] == " def f():"
- assert tw.lines[2] == "> g()"
- assert tw.lines[3] == ""
- assert tw.lines[4].endswith("mod.py:3: ")
- assert tw.lines[5] == ("_ ", None)
- assert tw.lines[6].endswith("in g")
- assert tw.lines[7] == " h()"
- assert tw.lines[8].endswith("in h")
- assert tw.lines[9] == " i()"
- assert tw.lines[10] == ("_ ", None)
- assert tw.lines[11] == ""
- assert tw.lines[12] == " def i():"
- assert tw.lines[13] == "> raise ValueError()"
- assert tw.lines[14] == "E ValueError"
- assert tw.lines[15] == ""
- assert tw.lines[16].endswith("mod.py:9: ValueError")
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/code/test_source.py b/tests/wpt/web-platform-tests/tools/pytest/testing/code/test_source.py
deleted file mode 100644
index 007ad1433aa..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/code/test_source.py
+++ /dev/null
@@ -1,659 +0,0 @@
-# flake8: noqa
-# disable flake check on this file because some constructs are strange
-# or redundant on purpose and can't be disable on a line-by-line basis
-import sys
-
-import _pytest._code
-import py
-import pytest
-from _pytest._code import Source
-from _pytest._code.source import _ast
-
-if _ast is not None:
- astonly = pytest.mark.nothing
-else:
- astonly = pytest.mark.xfail("True", reason="only works with AST-compile")
-
-failsonjython = pytest.mark.xfail("sys.platform.startswith('java')")
-
-def test_source_str_function():
- x = Source("3")
- assert str(x) == "3"
-
- x = Source(" 3")
- assert str(x) == "3"
-
- x = Source("""
- 3
- """, rstrip=False)
- assert str(x) == "\n3\n "
-
- x = Source("""
- 3
- """, rstrip=True)
- assert str(x) == "\n3"
-
-def test_unicode():
- try:
- unicode
- except NameError:
- return
- x = Source(unicode("4"))
- assert str(x) == "4"
- co = _pytest._code.compile(unicode('u"\xc3\xa5"', 'utf8'), mode='eval')
- val = eval(co)
- assert isinstance(val, unicode)
-
-def test_source_from_function():
- source = _pytest._code.Source(test_source_str_function)
- assert str(source).startswith('def test_source_str_function():')
-
-def test_source_from_method():
- class TestClass:
- def test_method(self):
- pass
- source = _pytest._code.Source(TestClass().test_method)
- assert source.lines == ["def test_method(self):",
- " pass"]
-
-def test_source_from_lines():
- lines = ["a \n", "b\n", "c"]
- source = _pytest._code.Source(lines)
- assert source.lines == ['a ', 'b', 'c']
-
-def test_source_from_inner_function():
- def f():
- pass
- source = _pytest._code.Source(f, deindent=False)
- assert str(source).startswith(' def f():')
- source = _pytest._code.Source(f)
- assert str(source).startswith('def f():')
-
-def test_source_putaround_simple():
- source = Source("raise ValueError")
- source = source.putaround(
- "try:", """\
- except ValueError:
- x = 42
- else:
- x = 23""")
- assert str(source)=="""\
-try:
- raise ValueError
-except ValueError:
- x = 42
-else:
- x = 23"""
-
-def test_source_putaround():
- source = Source()
- source = source.putaround("""
- if 1:
- x=1
- """)
- assert str(source).strip() == "if 1:\n x=1"
-
-def test_source_strips():
- source = Source("")
- assert source == Source()
- assert str(source) == ''
- assert source.strip() == source
-
-def test_source_strip_multiline():
- source = Source()
- source.lines = ["", " hello", " "]
- source2 = source.strip()
- assert source2.lines == [" hello"]
-
-def test_syntaxerror_rerepresentation():
- ex = pytest.raises(SyntaxError, _pytest._code.compile, 'xyz xyz')
- assert ex.value.lineno == 1
- assert ex.value.offset in (4,7) # XXX pypy/jython versus cpython?
- assert ex.value.text.strip(), 'x x'
-
-def test_isparseable():
- assert Source("hello").isparseable()
- assert Source("if 1:\n pass").isparseable()
- assert Source(" \nif 1:\n pass").isparseable()
- assert not Source("if 1:\n").isparseable()
- assert not Source(" \nif 1:\npass").isparseable()
- assert not Source(chr(0)).isparseable()
-
-class TestAccesses:
- source = Source("""\
- def f(x):
- pass
- def g(x):
- pass
- """)
- def test_getrange(self):
- x = self.source[0:2]
- assert x.isparseable()
- assert len(x.lines) == 2
- assert str(x) == "def f(x):\n pass"
-
- def test_getline(self):
- x = self.source[0]
- assert x == "def f(x):"
-
- def test_len(self):
- assert len(self.source) == 4
-
- def test_iter(self):
- l = [x for x in self.source]
- assert len(l) == 4
-
-class TestSourceParsingAndCompiling:
- source = Source("""\
- def f(x):
- assert (x ==
- 3 +
- 4)
- """).strip()
-
- def test_compile(self):
- co = _pytest._code.compile("x=3")
- d = {}
- exec (co, d)
- assert d['x'] == 3
-
- def test_compile_and_getsource_simple(self):
- co = _pytest._code.compile("x=3")
- exec (co)
- source = _pytest._code.Source(co)
- assert str(source) == "x=3"
-
- def test_compile_and_getsource_through_same_function(self):
- def gensource(source):
- return _pytest._code.compile(source)
- co1 = gensource("""
- def f():
- raise KeyError()
- """)
- co2 = gensource("""
- def f():
- raise ValueError()
- """)
- source1 = py.std.inspect.getsource(co1)
- assert 'KeyError' in source1
- source2 = py.std.inspect.getsource(co2)
- assert 'ValueError' in source2
-
- def test_getstatement(self):
- #print str(self.source)
- ass = str(self.source[1:])
- for i in range(1, 4):
- #print "trying start in line %r" % self.source[i]
- s = self.source.getstatement(i)
- #x = s.deindent()
- assert str(s) == ass
-
- def test_getstatementrange_triple_quoted(self):
- #print str(self.source)
- source = Source("""hello('''
- ''')""")
- s = source.getstatement(0)
- assert s == str(source)
- s = source.getstatement(1)
- assert s == str(source)
-
- @astonly
- def test_getstatementrange_within_constructs(self):
- source = Source("""\
- try:
- try:
- raise ValueError
- except SomeThing:
- pass
- finally:
- 42
- """)
- assert len(source) == 7
- # check all lineno's that could occur in a traceback
- #assert source.getstatementrange(0) == (0, 7)
- #assert source.getstatementrange(1) == (1, 5)
- assert source.getstatementrange(2) == (2, 3)
- assert source.getstatementrange(3) == (3, 4)
- assert source.getstatementrange(4) == (4, 5)
- #assert source.getstatementrange(5) == (0, 7)
- assert source.getstatementrange(6) == (6, 7)
-
- def test_getstatementrange_bug(self):
- source = Source("""\
- try:
- x = (
- y +
- z)
- except:
- pass
- """)
- assert len(source) == 6
- assert source.getstatementrange(2) == (1, 4)
-
- def test_getstatementrange_bug2(self):
- source = Source("""\
- assert (
- 33
- ==
- [
- X(3,
- b=1, c=2
- ),
- ]
- )
- """)
- assert len(source) == 9
- assert source.getstatementrange(5) == (0, 9)
-
- def test_getstatementrange_ast_issue58(self):
- source = Source("""\
-
- def test_some():
- for a in [a for a in
- CAUSE_ERROR]: pass
-
- x = 3
- """)
- assert getstatement(2, source).lines == source.lines[2:3]
- assert getstatement(3, source).lines == source.lines[3:4]
-
- @pytest.mark.skipif("sys.version_info < (2,6)")
- def test_getstatementrange_out_of_bounds_py3(self):
- source = Source("if xxx:\n from .collections import something")
- r = source.getstatementrange(1)
- assert r == (1,2)
-
- def test_getstatementrange_with_syntaxerror_issue7(self):
- source = Source(":")
- pytest.raises(SyntaxError, lambda: source.getstatementrange(0))
-
- @pytest.mark.skipif("sys.version_info < (2,6)")
- def test_compile_to_ast(self):
- import ast
- source = Source("x = 4")
- mod = source.compile(flag=ast.PyCF_ONLY_AST)
- assert isinstance(mod, ast.Module)
- compile(mod, "<filename>", "exec")
-
- def test_compile_and_getsource(self):
- co = self.source.compile()
- py.builtin.exec_(co, globals())
- f(7)
- excinfo = pytest.raises(AssertionError, "f(6)")
- frame = excinfo.traceback[-1].frame
- stmt = frame.code.fullsource.getstatement(frame.lineno)
- #print "block", str(block)
- assert str(stmt).strip().startswith('assert')
-
- def test_compilefuncs_and_path_sanity(self):
- def check(comp, name):
- co = comp(self.source, name)
- if not name:
- expected = "codegen %s:%d>" %(mypath, mylineno+2+1)
- else:
- expected = "codegen %r %s:%d>" % (name, mypath, mylineno+2+1)
- fn = co.co_filename
- assert fn.endswith(expected)
-
- mycode = _pytest._code.Code(self.test_compilefuncs_and_path_sanity)
- mylineno = mycode.firstlineno
- mypath = mycode.path
-
- for comp in _pytest._code.compile, _pytest._code.Source.compile:
- for name in '', None, 'my':
- yield check, comp, name
-
- def test_offsetless_synerr(self):
- pytest.raises(SyntaxError, _pytest._code.compile, "lambda a,a: 0", mode='eval')
-
-def test_getstartingblock_singleline():
- class A:
- def __init__(self, *args):
- frame = sys._getframe(1)
- self.source = _pytest._code.Frame(frame).statement
-
- x = A('x', 'y')
-
- l = [i for i in x.source.lines if i.strip()]
- assert len(l) == 1
-
-def test_getstartingblock_multiline():
- class A:
- def __init__(self, *args):
- frame = sys._getframe(1)
- self.source = _pytest._code.Frame(frame).statement
-
- x = A('x',
- 'y' \
- ,
- 'z')
-
- l = [i for i in x.source.lines if i.strip()]
- assert len(l) == 4
-
-def test_getline_finally():
- def c(): pass
- excinfo = pytest.raises(TypeError, """
- teardown = None
- try:
- c(1)
- finally:
- if teardown:
- teardown()
- """)
- source = excinfo.traceback[-1].statement
- assert str(source).strip() == 'c(1)'
-
-def test_getfuncsource_dynamic():
- source = """
- def f():
- raise ValueError
-
- def g(): pass
- """
- co = _pytest._code.compile(source)
- py.builtin.exec_(co, globals())
- assert str(_pytest._code.Source(f)).strip() == 'def f():\n raise ValueError'
- assert str(_pytest._code.Source(g)).strip() == 'def g(): pass'
-
-
-def test_getfuncsource_with_multine_string():
- def f():
- c = '''while True:
- pass
-'''
- assert str(_pytest._code.Source(f)).strip() == "def f():\n c = '''while True:\n pass\n'''"
-
-
-def test_deindent():
- from _pytest._code.source import deindent as deindent
- assert deindent(['\tfoo', '\tbar', ]) == ['foo', 'bar']
-
- def f():
- c = '''while True:
- pass
-'''
- import inspect
- lines = deindent(inspect.getsource(f).splitlines())
- assert lines == ["def f():", " c = '''while True:", " pass", "'''"]
-
- source = """
- def f():
- def g():
- pass
- """
- lines = deindent(source.splitlines())
- assert lines == ['', 'def f():', ' def g():', ' pass', ' ']
-
-@pytest.mark.xfail("sys.version_info[:3] < (2,7,0) or "
- "((3,0) <= sys.version_info[:2] < (3,2))")
-def test_source_of_class_at_eof_without_newline(tmpdir):
- # this test fails because the implicit inspect.getsource(A) below
- # does not return the "x = 1" last line.
- source = _pytest._code.Source('''
- class A(object):
- def method(self):
- x = 1
- ''')
- path = tmpdir.join("a.py")
- path.write(source)
- s2 = _pytest._code.Source(tmpdir.join("a.py").pyimport().A)
- assert str(source).strip() == str(s2).strip()
-
-if True:
- def x():
- pass
-
-def test_getsource_fallback():
- from _pytest._code.source import getsource
- expected = """def x():
- pass"""
- src = getsource(x)
- assert src == expected
-
-def test_idem_compile_and_getsource():
- from _pytest._code.source import getsource
- expected = "def x(): pass"
- co = _pytest._code.compile(expected)
- src = getsource(co)
- assert src == expected
-
-def test_findsource_fallback():
- from _pytest._code.source import findsource
- src, lineno = findsource(x)
- assert 'test_findsource_simple' in str(src)
- assert src[lineno] == ' def x():'
-
-def test_findsource():
- from _pytest._code.source import findsource
- co = _pytest._code.compile("""if 1:
- def x():
- pass
-""")
-
- src, lineno = findsource(co)
- assert 'if 1:' in str(src)
-
- d = {}
- eval(co, d)
- src, lineno = findsource(d['x'])
- assert 'if 1:' in str(src)
- assert src[lineno] == " def x():"
-
-
-def test_getfslineno():
- from _pytest._code import getfslineno
-
- def f(x):
- pass
-
- fspath, lineno = getfslineno(f)
-
- assert fspath.basename == "test_source.py"
- assert lineno == _pytest._code.getrawcode(f).co_firstlineno - 1 # see findsource
-
- class A(object):
- pass
-
- fspath, lineno = getfslineno(A)
-
- _, A_lineno = py.std.inspect.findsource(A)
- assert fspath.basename == "test_source.py"
- assert lineno == A_lineno
-
- assert getfslineno(3) == ("", -1)
- class B:
- pass
- B.__name__ = "B2"
- assert getfslineno(B)[1] == -1
-
-def test_code_of_object_instance_with_call():
- class A:
- pass
- pytest.raises(TypeError, lambda: _pytest._code.Source(A()))
- class WithCall:
- def __call__(self):
- pass
-
- code = _pytest._code.Code(WithCall())
- assert 'pass' in str(code.source())
-
- class Hello(object):
- def __call__(self):
- pass
- pytest.raises(TypeError, lambda: _pytest._code.Code(Hello))
-
-
-def getstatement(lineno, source):
- from _pytest._code.source import getstatementrange_ast
- source = _pytest._code.Source(source, deindent=False)
- ast, start, end = getstatementrange_ast(lineno, source)
- return source[start:end]
-
-def test_oneline():
- source = getstatement(0, "raise ValueError")
- assert str(source) == "raise ValueError"
-
-def test_comment_and_no_newline_at_end():
- from _pytest._code.source import getstatementrange_ast
- source = Source(['def test_basic_complex():',
- ' assert 1 == 2',
- '# vim: filetype=pyopencl:fdm=marker'])
- ast, start, end = getstatementrange_ast(1, source)
- assert end == 2
-
-def test_oneline_and_comment():
- source = getstatement(0, "raise ValueError\n#hello")
- assert str(source) == "raise ValueError"
-
-@pytest.mark.xfail(hasattr(sys, "pypy_version_info"),
- reason='does not work on pypy')
-def test_comments():
- source = '''def test():
- "comment 1"
- x = 1
- # comment 2
- # comment 3
-
- assert False
-
-"""
-comment 4
-"""
-'''
- for line in range(2,6):
- assert str(getstatement(line, source)) == ' x = 1'
- for line in range(6,10):
- assert str(getstatement(line, source)) == ' assert False'
- assert str(getstatement(10, source)) == '"""'
-
-def test_comment_in_statement():
- source = '''test(foo=1,
- # comment 1
- bar=2)
-'''
- for line in range(1,3):
- assert str(getstatement(line, source)) == \
- 'test(foo=1,\n # comment 1\n bar=2)'
-
-def test_single_line_else():
- source = getstatement(1, "if False: 2\nelse: 3")
- assert str(source) == "else: 3"
-
-def test_single_line_finally():
- source = getstatement(1, "try: 1\nfinally: 3")
- assert str(source) == "finally: 3"
-
-def test_issue55():
- source = ('def round_trip(dinp):\n assert 1 == dinp\n'
- 'def test_rt():\n round_trip("""\n""")\n')
- s = getstatement(3, source)
- assert str(s) == ' round_trip("""\n""")'
-
-
-def XXXtest_multiline():
- source = getstatement(0, """\
-raise ValueError(
- 23
-)
-x = 3
-""")
- assert str(source) == "raise ValueError(\n 23\n)"
-
-class TestTry:
- pytestmark = astonly
- source = """\
-try:
- raise ValueError
-except Something:
- raise IndexError(1)
-else:
- raise KeyError()
-"""
-
- def test_body(self):
- source = getstatement(1, self.source)
- assert str(source) == " raise ValueError"
-
- def test_except_line(self):
- source = getstatement(2, self.source)
- assert str(source) == "except Something:"
-
- def test_except_body(self):
- source = getstatement(3, self.source)
- assert str(source) == " raise IndexError(1)"
-
- def test_else(self):
- source = getstatement(5, self.source)
- assert str(source) == " raise KeyError()"
-
-class TestTryFinally:
- source = """\
-try:
- raise ValueError
-finally:
- raise IndexError(1)
-"""
-
- def test_body(self):
- source = getstatement(1, self.source)
- assert str(source) == " raise ValueError"
-
- def test_finally(self):
- source = getstatement(3, self.source)
- assert str(source) == " raise IndexError(1)"
-
-
-
-class TestIf:
- pytestmark = astonly
- source = """\
-if 1:
- y = 3
-elif False:
- y = 5
-else:
- y = 7
-"""
-
- def test_body(self):
- source = getstatement(1, self.source)
- assert str(source) == " y = 3"
-
- def test_elif_clause(self):
- source = getstatement(2, self.source)
- assert str(source) == "elif False:"
-
- def test_elif(self):
- source = getstatement(3, self.source)
- assert str(source) == " y = 5"
-
- def test_else(self):
- source = getstatement(5, self.source)
- assert str(source) == " y = 7"
-
-def test_semicolon():
- s = """\
-hello ; pytest.skip()
-"""
- source = getstatement(0, s)
- assert str(source) == s.strip()
-
-def test_def_online():
- s = """\
-def func(): raise ValueError(42)
-
-def something():
- pass
-"""
- source = getstatement(0, s)
- assert str(source) == "def func(): raise ValueError(42)"
-
-def XXX_test_expression_multiline():
- source = """\
-something
-'''
-'''"""
- result = getstatement(1, source)
- assert str(result) == "'''\n'''"
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/install_cx_freeze.py b/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/install_cx_freeze.py
deleted file mode 100644
index 83dce87aa56..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/install_cx_freeze.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""
-Installs cx_freeze from source, but first patching
-setup.py as described here:
-
-http://stackoverflow.com/questions/25107697/compiling-cx-freeze-under-ubuntu
-"""
-import glob
-import tarfile
-import os
-import sys
-import platform
-import py
-
-if __name__ == '__main__':
- if 'ubuntu' not in platform.version().lower():
-
- print('Not Ubuntu, installing using pip. (platform.version() is %r)' %
- platform.version())
- res = os.system('pip install cx_freeze')
- if res != 0:
- sys.exit(res)
- sys.exit(0)
-
- rootdir = py.path.local.make_numbered_dir(prefix='cx_freeze')
-
- res = os.system('pip install --download %s --no-use-wheel '
- 'cx_freeze' % rootdir)
- if res != 0:
- sys.exit(res)
-
- packages = glob.glob('%s/*.tar.gz' % rootdir)
- assert len(packages) == 1
- tar_filename = packages[0]
-
- tar_file = tarfile.open(tar_filename)
- try:
- tar_file.extractall(path=str(rootdir))
- finally:
- tar_file.close()
-
- basename = os.path.basename(tar_filename).replace('.tar.gz', '')
- setup_py_filename = '%s/%s/setup.py' % (rootdir, basename)
- with open(setup_py_filename) as f:
- lines = f.readlines()
-
- line_to_patch = 'if not vars.get("Py_ENABLE_SHARED", 0):'
- for index, line in enumerate(lines):
- if line_to_patch in line:
- indent = line[:line.index(line_to_patch)]
- lines[index] = indent + 'if True:\n'
- print('Patched line %d' % (index + 1))
- break
- else:
- sys.exit('Could not find line in setup.py to patch!')
-
- with open(setup_py_filename, 'w') as f:
- f.writelines(lines)
-
- os.chdir('%s/%s' % (rootdir, basename))
- res = os.system('python setup.py install')
- if res != 0:
- sys.exit(res)
-
- sys.exit(0)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/runtests_script.py b/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/runtests_script.py
deleted file mode 100644
index f2b032d7655..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/runtests_script.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""
-This is the script that is actually frozen into an executable: simply executes
-py.test main().
-"""
-
-if __name__ == '__main__':
- import sys
- import pytest
- sys.exit(pytest.main()) \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/runtests_setup.py b/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/runtests_setup.py
deleted file mode 100644
index a2874a655eb..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/runtests_setup.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""
-Sample setup.py script that generates an executable with pytest runner embedded.
-"""
-if __name__ == '__main__':
- from cx_Freeze import setup, Executable
- import pytest
-
- setup(
- name="runtests",
- version="0.1",
- description="exemple of how embedding py.test into an executable using cx_freeze",
- executables=[Executable("runtests_script.py")],
- options={"build_exe": {'includes': pytest.freeze_includes()}},
- )
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/tests/test_trivial.py b/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/tests/test_trivial.py
deleted file mode 100644
index d8a572baaed..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/tests/test_trivial.py
+++ /dev/null
@@ -1,6 +0,0 @@
-
-def test_upper():
- assert 'foo'.upper() == 'FOO'
-
-def test_lower():
- assert 'FOO'.lower() == 'foo' \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/tox_run.py b/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/tox_run.py
deleted file mode 100644
index e8df2684bb1..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/tox_run.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"""
-Called by tox.ini: uses the generated executable to run the tests in ./tests/
-directory.
-
-.. note:: somehow calling "build/runtests_script" directly from tox doesn't
- seem to work (at least on Windows).
-"""
-if __name__ == '__main__':
- import os
- import sys
-
- executable = os.path.join(os.getcwd(), 'build', 'runtests_script')
- if sys.platform.startswith('win'):
- executable += '.exe'
- sys.exit(os.system('%s tests' % executable)) \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/python/collect.py b/tests/wpt/web-platform-tests/tools/pytest/testing/python/collect.py
deleted file mode 100644
index 22433da7716..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/python/collect.py
+++ /dev/null
@@ -1,1200 +0,0 @@
-# -*- coding: utf-8 -*-
-import sys
-from textwrap import dedent
-
-import _pytest._code
-import py
-import pytest
-from _pytest.main import EXIT_NOTESTSCOLLECTED
-
-
-class TestModule:
- def test_failing_import(self, testdir):
- modcol = testdir.getmodulecol("import alksdjalskdjalkjals")
- pytest.raises(ImportError, modcol.collect)
- pytest.raises(ImportError, modcol.collect)
-
- def test_import_duplicate(self, testdir):
- a = testdir.mkdir("a")
- b = testdir.mkdir("b")
- p = a.ensure("test_whatever.py")
- p.pyimport()
- del py.std.sys.modules['test_whatever']
- b.ensure("test_whatever.py")
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*import*mismatch*",
- "*imported*test_whatever*",
- "*%s*" % a.join("test_whatever.py"),
- "*not the same*",
- "*%s*" % b.join("test_whatever.py"),
- "*HINT*",
- ])
-
- def test_import_prepend_append(self, testdir, monkeypatch):
- syspath = list(sys.path)
- monkeypatch.setattr(sys, "path", syspath)
- root1 = testdir.mkdir("root1")
- root2 = testdir.mkdir("root2")
- root1.ensure("x456.py")
- root2.ensure("x456.py")
- p = root2.join("test_x456.py")
- monkeypatch.syspath_prepend(str(root1))
- p.write(dedent("""\
- import x456
- def test():
- assert x456.__file__.startswith(%r)
- """ % str(root2)))
- with root2.as_cwd():
- reprec = testdir.inline_run("--import-mode=append")
- reprec.assertoutcome(passed=0, failed=1)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_syntax_error_in_module(self, testdir):
- modcol = testdir.getmodulecol("this is a syntax error")
- pytest.raises(modcol.CollectError, modcol.collect)
- pytest.raises(modcol.CollectError, modcol.collect)
-
- def test_module_considers_pluginmanager_at_import(self, testdir):
- modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',")
- pytest.raises(ImportError, lambda: modcol.obj)
-
-class TestClass:
- def test_class_with_init_warning(self, testdir):
- testdir.makepyfile("""
- class TestClass1:
- def __init__(self):
- pass
- """)
- result = testdir.runpytest("-rw")
- result.stdout.fnmatch_lines_random("""
- WC1*test_class_with_init_warning.py*__init__*
- """)
-
- def test_class_subclassobject(self, testdir):
- testdir.getmodulecol("""
- class test(object):
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*collected 0*",
- ])
-
- def test_setup_teardown_class_as_classmethod(self, testdir):
- testdir.makepyfile(test_mod1="""
- class TestClassMethod:
- @classmethod
- def setup_class(cls):
- pass
- def test_1(self):
- pass
- @classmethod
- def teardown_class(cls):
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*1 passed*",
- ])
-
- def test_issue1035_obj_has_getattr(self, testdir):
- modcol = testdir.getmodulecol("""
- class Chameleon(object):
- def __getattr__(self, name):
- return True
- chameleon = Chameleon()
- """)
- colitems = modcol.collect()
- assert len(colitems) == 0
-
-
-class TestGenerator:
- def test_generative_functions(self, testdir):
- modcol = testdir.getmodulecol("""
- def func1(arg, arg2):
- assert arg == arg2
-
- def test_gen():
- yield func1, 17, 3*5
- yield func1, 42, 6*7
- """)
- colitems = modcol.collect()
- assert len(colitems) == 1
- gencol = colitems[0]
- assert isinstance(gencol, pytest.Generator)
- gencolitems = gencol.collect()
- assert len(gencolitems) == 2
- assert isinstance(gencolitems[0], pytest.Function)
- assert isinstance(gencolitems[1], pytest.Function)
- assert gencolitems[0].name == '[0]'
- assert gencolitems[0].obj.__name__ == 'func1'
-
- def test_generative_methods(self, testdir):
- modcol = testdir.getmodulecol("""
- def func1(arg, arg2):
- assert arg == arg2
- class TestGenMethods:
- def test_gen(self):
- yield func1, 17, 3*5
- yield func1, 42, 6*7
- """)
- gencol = modcol.collect()[0].collect()[0].collect()[0]
- assert isinstance(gencol, pytest.Generator)
- gencolitems = gencol.collect()
- assert len(gencolitems) == 2
- assert isinstance(gencolitems[0], pytest.Function)
- assert isinstance(gencolitems[1], pytest.Function)
- assert gencolitems[0].name == '[0]'
- assert gencolitems[0].obj.__name__ == 'func1'
-
- def test_generative_functions_with_explicit_names(self, testdir):
- modcol = testdir.getmodulecol("""
- def func1(arg, arg2):
- assert arg == arg2
-
- def test_gen():
- yield "seventeen", func1, 17, 3*5
- yield "fortytwo", func1, 42, 6*7
- """)
- colitems = modcol.collect()
- assert len(colitems) == 1
- gencol = colitems[0]
- assert isinstance(gencol, pytest.Generator)
- gencolitems = gencol.collect()
- assert len(gencolitems) == 2
- assert isinstance(gencolitems[0], pytest.Function)
- assert isinstance(gencolitems[1], pytest.Function)
- assert gencolitems[0].name == "['seventeen']"
- assert gencolitems[0].obj.__name__ == 'func1'
- assert gencolitems[1].name == "['fortytwo']"
- assert gencolitems[1].obj.__name__ == 'func1'
-
- def test_generative_functions_unique_explicit_names(self, testdir):
- # generative
- modcol = testdir.getmodulecol("""
- def func(): pass
- def test_gen():
- yield "name", func
- yield "name", func
- """)
- colitems = modcol.collect()
- assert len(colitems) == 1
- gencol = colitems[0]
- assert isinstance(gencol, pytest.Generator)
- pytest.raises(ValueError, "gencol.collect()")
-
- def test_generative_methods_with_explicit_names(self, testdir):
- modcol = testdir.getmodulecol("""
- def func1(arg, arg2):
- assert arg == arg2
- class TestGenMethods:
- def test_gen(self):
- yield "m1", func1, 17, 3*5
- yield "m2", func1, 42, 6*7
- """)
- gencol = modcol.collect()[0].collect()[0].collect()[0]
- assert isinstance(gencol, pytest.Generator)
- gencolitems = gencol.collect()
- assert len(gencolitems) == 2
- assert isinstance(gencolitems[0], pytest.Function)
- assert isinstance(gencolitems[1], pytest.Function)
- assert gencolitems[0].name == "['m1']"
- assert gencolitems[0].obj.__name__ == 'func1'
- assert gencolitems[1].name == "['m2']"
- assert gencolitems[1].obj.__name__ == 'func1'
-
- def test_order_of_execution_generator_same_codeline(self, testdir, tmpdir):
- o = testdir.makepyfile("""
- def test_generative_order_of_execution():
- import py, pytest
- test_list = []
- expected_list = list(range(6))
-
- def list_append(item):
- test_list.append(item)
-
- def assert_order_of_execution():
- py.builtin.print_('expected order', expected_list)
- py.builtin.print_('but got ', test_list)
- assert test_list == expected_list
-
- for i in expected_list:
- yield list_append, i
- yield assert_order_of_execution
- """)
- reprec = testdir.inline_run(o)
- passed, skipped, failed = reprec.countoutcomes()
- assert passed == 7
- assert not skipped and not failed
-
- def test_order_of_execution_generator_different_codeline(self, testdir):
- o = testdir.makepyfile("""
- def test_generative_tests_different_codeline():
- import py, pytest
- test_list = []
- expected_list = list(range(3))
-
- def list_append_2():
- test_list.append(2)
-
- def list_append_1():
- test_list.append(1)
-
- def list_append_0():
- test_list.append(0)
-
- def assert_order_of_execution():
- py.builtin.print_('expected order', expected_list)
- py.builtin.print_('but got ', test_list)
- assert test_list == expected_list
-
- yield list_append_0
- yield list_append_1
- yield list_append_2
- yield assert_order_of_execution
- """)
- reprec = testdir.inline_run(o)
- passed, skipped, failed = reprec.countoutcomes()
- assert passed == 4
- assert not skipped and not failed
-
- def test_setupstate_is_preserved_134(self, testdir):
- # yield-based tests are messy wrt to setupstate because
- # during collection they already invoke setup functions
- # and then again when they are run. For now, we want to make sure
- # that the old 1.3.4 behaviour is preserved such that all
- # yielded functions all share the same "self" instance that
- # has been used during collection.
- o = testdir.makepyfile("""
- setuplist = []
- class TestClass:
- def setup_method(self, func):
- #print "setup_method", self, func
- setuplist.append(self)
- self.init = 42
-
- def teardown_method(self, func):
- self.init = None
-
- def test_func1(self):
- pass
-
- def test_func2(self):
- yield self.func2
- yield self.func2
-
- def func2(self):
- assert self.init
-
- def test_setuplist():
- # once for test_func2 during collection
- # once for test_func1 during test run
- # once for test_func2 during test run
- #print setuplist
- assert len(setuplist) == 3, len(setuplist)
- assert setuplist[0] == setuplist[2], setuplist
- assert setuplist[1] != setuplist[2], setuplist
- """)
- reprec = testdir.inline_run(o, '-v')
- passed, skipped, failed = reprec.countoutcomes()
- assert passed == 4
- assert not skipped and not failed
-
-
-class TestFunction:
- def test_getmodulecollector(self, testdir):
- item = testdir.getitem("def test_func(): pass")
- modcol = item.getparent(pytest.Module)
- assert isinstance(modcol, pytest.Module)
- assert hasattr(modcol.obj, 'test_func')
-
- def test_function_as_object_instance_ignored(self, testdir):
- testdir.makepyfile("""
- class A:
- def __call__(self, tmpdir):
- 0/0
-
- test_a = A()
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome()
-
- def test_function_equality(self, testdir, tmpdir):
- from _pytest.python import FixtureManager
- config = testdir.parseconfigure()
- session = testdir.Session(config)
- session._fixturemanager = FixtureManager(session)
- def func1():
- pass
- def func2():
- pass
- f1 = pytest.Function(name="name", parent=session, config=config,
- args=(1,), callobj=func1)
- assert f1 == f1
- f2 = pytest.Function(name="name",config=config,
- callobj=func2, parent=session)
- assert f1 != f2
-
- def test_issue197_parametrize_emptyset(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.parametrize('arg', [])
- def test_function(arg):
- pass
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(skipped=1)
-
- def test_single_tuple_unwraps_values(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.parametrize(('arg',), [(1,)])
- def test_function(arg):
- assert arg == 1
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_issue213_parametrize_value_no_equal(self, testdir):
- testdir.makepyfile("""
- import pytest
- class A:
- def __eq__(self, other):
- raise ValueError("not possible")
- @pytest.mark.parametrize('arg', [A()])
- def test_function(arg):
- assert arg.__class__.__name__ == "A"
- """)
- reprec = testdir.inline_run("--fulltrace")
- reprec.assertoutcome(passed=1)
-
- def test_parametrize_with_non_hashable_values(self, testdir):
- """Test parametrization with non-hashable values."""
- testdir.makepyfile("""
- archival_mapping = {
- '1.0': {'tag': '1.0'},
- '1.2.2a1': {'tag': 'release-1.2.2a1'},
- }
-
- import pytest
- @pytest.mark.parametrize('key value'.split(),
- archival_mapping.items())
- def test_archival_to_version(key, value):
- assert key in archival_mapping
- assert value == archival_mapping[key]
- """)
- rec = testdir.inline_run()
- rec.assertoutcome(passed=2)
-
-
- def test_parametrize_with_non_hashable_values_indirect(self, testdir):
- """Test parametrization with non-hashable values with indirect parametrization."""
- testdir.makepyfile("""
- archival_mapping = {
- '1.0': {'tag': '1.0'},
- '1.2.2a1': {'tag': 'release-1.2.2a1'},
- }
-
- import pytest
-
- @pytest.fixture
- def key(request):
- return request.param
-
- @pytest.fixture
- def value(request):
- return request.param
-
- @pytest.mark.parametrize('key value'.split(),
- archival_mapping.items(), indirect=True)
- def test_archival_to_version(key, value):
- assert key in archival_mapping
- assert value == archival_mapping[key]
- """)
- rec = testdir.inline_run()
- rec.assertoutcome(passed=2)
-
-
- def test_parametrize_overrides_fixture(self, testdir):
- """Test parametrization when parameter overrides existing fixture with same name."""
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture
- def value():
- return 'value'
-
- @pytest.mark.parametrize('value',
- ['overridden'])
- def test_overridden_via_param(value):
- assert value == 'overridden'
-
- @pytest.mark.parametrize('somevalue', ['overridden'])
- def test_not_overridden(value, somevalue):
- assert value == 'value'
- assert somevalue == 'overridden'
-
- @pytest.mark.parametrize('other,value', [('foo', 'overridden')])
- def test_overridden_via_multiparam(other, value):
- assert other == 'foo'
- assert value == 'overridden'
- """)
- rec = testdir.inline_run()
- rec.assertoutcome(passed=3)
-
-
- def test_parametrize_overrides_parametrized_fixture(self, testdir):
- """Test parametrization when parameter overrides existing parametrized fixture with same name."""
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture(params=[1, 2])
- def value(request):
- return request.param
-
- @pytest.mark.parametrize('value',
- ['overridden'])
- def test_overridden_via_param(value):
- assert value == 'overridden'
- """)
- rec = testdir.inline_run()
- rec.assertoutcome(passed=1)
-
- def test_parametrize_with_mark(selfself, testdir):
- items = testdir.getitems("""
- import pytest
- @pytest.mark.foo
- @pytest.mark.parametrize('arg', [
- 1,
- pytest.mark.bar(pytest.mark.baz(2))
- ])
- def test_function(arg):
- pass
- """)
- keywords = [item.keywords for item in items]
- assert 'foo' in keywords[0] and 'bar' not in keywords[0] and 'baz' not in keywords[0]
- assert 'foo' in keywords[1] and 'bar' in keywords[1] and 'baz' in keywords[1]
-
- def test_function_equality_with_callspec(self, testdir, tmpdir):
- items = testdir.getitems("""
- import pytest
- @pytest.mark.parametrize('arg', [1,2])
- def test_function(arg):
- pass
- """)
- assert items[0] != items[1]
- assert not (items[0] == items[1])
-
- def test_pyfunc_call(self, testdir):
- item = testdir.getitem("def test_func(): raise ValueError")
- config = item.config
- class MyPlugin1:
- def pytest_pyfunc_call(self, pyfuncitem):
- raise ValueError
- class MyPlugin2:
- def pytest_pyfunc_call(self, pyfuncitem):
- return True
- config.pluginmanager.register(MyPlugin1())
- config.pluginmanager.register(MyPlugin2())
- config.hook.pytest_runtest_setup(item=item)
- config.hook.pytest_pyfunc_call(pyfuncitem=item)
-
- def test_multiple_parametrize(self, testdir):
- modcol = testdir.getmodulecol("""
- import pytest
- @pytest.mark.parametrize('x', [0, 1])
- @pytest.mark.parametrize('y', [2, 3])
- def test1(x, y):
- pass
- """)
- colitems = modcol.collect()
- assert colitems[0].name == 'test1[2-0]'
- assert colitems[1].name == 'test1[2-1]'
- assert colitems[2].name == 'test1[3-0]'
- assert colitems[3].name == 'test1[3-1]'
-
- def test_issue751_multiple_parametrize_with_ids(self, testdir):
- modcol = testdir.getmodulecol("""
- import pytest
- @pytest.mark.parametrize('x', [0], ids=['c'])
- @pytest.mark.parametrize('y', [0, 1], ids=['a', 'b'])
- class Test(object):
- def test1(self, x, y):
- pass
- def test2(self, x, y):
- pass
- """)
- colitems = modcol.collect()[0].collect()[0].collect()
- assert colitems[0].name == 'test1[a-c]'
- assert colitems[1].name == 'test1[b-c]'
- assert colitems[2].name == 'test2[a-c]'
- assert colitems[3].name == 'test2[b-c]'
-
- def test_parametrize_skipif(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- m = pytest.mark.skipif('True')
-
- @pytest.mark.parametrize('x', [0, 1, m(2)])
- def test_skip_if(x):
- assert x < 2
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines('* 2 passed, 1 skipped in *')
-
- def test_parametrize_skip(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- m = pytest.mark.skip('')
-
- @pytest.mark.parametrize('x', [0, 1, m(2)])
- def test_skip(x):
- assert x < 2
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines('* 2 passed, 1 skipped in *')
-
- def test_parametrize_skipif_no_skip(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- m = pytest.mark.skipif('False')
-
- @pytest.mark.parametrize('x', [0, 1, m(2)])
- def test_skipif_no_skip(x):
- assert x < 2
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines('* 1 failed, 2 passed in *')
-
- def test_parametrize_xfail(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- m = pytest.mark.xfail('True')
-
- @pytest.mark.parametrize('x', [0, 1, m(2)])
- def test_xfail(x):
- assert x < 2
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines('* 2 passed, 1 xfailed in *')
-
- def test_parametrize_passed(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- m = pytest.mark.xfail('True')
-
- @pytest.mark.parametrize('x', [0, 1, m(2)])
- def test_xfail(x):
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines('* 2 passed, 1 xpassed in *')
-
- def test_parametrize_xfail_passed(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- m = pytest.mark.xfail('False')
-
- @pytest.mark.parametrize('x', [0, 1, m(2)])
- def test_passed(x):
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines('* 3 passed in *')
-
-
-class TestSorting:
- def test_check_equality(self, testdir):
- modcol = testdir.getmodulecol("""
- def test_pass(): pass
- def test_fail(): assert 0
- """)
- fn1 = testdir.collect_by_name(modcol, "test_pass")
- assert isinstance(fn1, pytest.Function)
- fn2 = testdir.collect_by_name(modcol, "test_pass")
- assert isinstance(fn2, pytest.Function)
-
- assert fn1 == fn2
- assert fn1 != modcol
- if py.std.sys.version_info < (3, 0):
- assert cmp(fn1, fn2) == 0
- assert hash(fn1) == hash(fn2)
-
- fn3 = testdir.collect_by_name(modcol, "test_fail")
- assert isinstance(fn3, pytest.Function)
- assert not (fn1 == fn3)
- assert fn1 != fn3
-
- for fn in fn1,fn2,fn3:
- assert fn != 3
- assert fn != modcol
- assert fn != [1,2,3]
- assert [1,2,3] != fn
- assert modcol != fn
-
- def test_allow_sane_sorting_for_decorators(self, testdir):
- modcol = testdir.getmodulecol("""
- def dec(f):
- g = lambda: f(2)
- g.place_as = f
- return g
-
-
- def test_b(y):
- pass
- test_b = dec(test_b)
-
- def test_a(y):
- pass
- test_a = dec(test_a)
- """)
- colitems = modcol.collect()
- assert len(colitems) == 2
- assert [item.name for item in colitems] == ['test_b', 'test_a']
-
-
-class TestConftestCustomization:
- def test_pytest_pycollect_module(self, testdir):
- testdir.makeconftest("""
- import pytest
- class MyModule(pytest.Module):
- pass
- def pytest_pycollect_makemodule(path, parent):
- if path.basename == "test_xyz.py":
- return MyModule(path, parent)
- """)
- testdir.makepyfile("def test_some(): pass")
- testdir.makepyfile(test_xyz="def test_func(): pass")
- result = testdir.runpytest("--collect-only")
- result.stdout.fnmatch_lines([
- "*<Module*test_pytest*",
- "*<MyModule*xyz*",
- ])
-
- def test_customized_pymakemodule_issue205_subdir(self, testdir):
- b = testdir.mkdir("a").mkdir("b")
- b.join("conftest.py").write(_pytest._code.Source("""
- def pytest_pycollect_makemodule(__multicall__):
- mod = __multicall__.execute()
- mod.obj.hello = "world"
- return mod
- """))
- b.join("test_module.py").write(_pytest._code.Source("""
- def test_hello():
- assert hello == "world"
- """))
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_customized_pymakeitem(self, testdir):
- b = testdir.mkdir("a").mkdir("b")
- b.join("conftest.py").write(_pytest._code.Source("""
- import pytest
- @pytest.hookimpl(hookwrapper=True)
- def pytest_pycollect_makeitem():
- outcome = yield
- if outcome.excinfo is None:
- result = outcome.result
- if result:
- for func in result:
- func._some123 = "world"
- """))
- b.join("test_module.py").write(_pytest._code.Source("""
- import pytest
-
- @pytest.fixture()
- def obj(request):
- return request.node._some123
- def test_hello(obj):
- assert obj == "world"
- """))
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_pytest_pycollect_makeitem(self, testdir):
- testdir.makeconftest("""
- import pytest
- class MyFunction(pytest.Function):
- pass
- def pytest_pycollect_makeitem(collector, name, obj):
- if name == "some":
- return MyFunction(name, collector)
- """)
- testdir.makepyfile("def some(): pass")
- result = testdir.runpytest("--collect-only")
- result.stdout.fnmatch_lines([
- "*MyFunction*some*",
- ])
-
- def test_makeitem_non_underscore(self, testdir, monkeypatch):
- modcol = testdir.getmodulecol("def _hello(): pass")
- l = []
- monkeypatch.setattr(pytest.Module, 'makeitem',
- lambda self, name, obj: l.append(name))
- l = modcol.collect()
- assert '_hello' not in l
-
-def test_setup_only_available_in_subdir(testdir):
- sub1 = testdir.mkpydir("sub1")
- sub2 = testdir.mkpydir("sub2")
- sub1.join("conftest.py").write(_pytest._code.Source("""
- import pytest
- def pytest_runtest_setup(item):
- assert item.fspath.purebasename == "test_in_sub1"
- def pytest_runtest_call(item):
- assert item.fspath.purebasename == "test_in_sub1"
- def pytest_runtest_teardown(item):
- assert item.fspath.purebasename == "test_in_sub1"
- """))
- sub2.join("conftest.py").write(_pytest._code.Source("""
- import pytest
- def pytest_runtest_setup(item):
- assert item.fspath.purebasename == "test_in_sub2"
- def pytest_runtest_call(item):
- assert item.fspath.purebasename == "test_in_sub2"
- def pytest_runtest_teardown(item):
- assert item.fspath.purebasename == "test_in_sub2"
- """))
- sub1.join("test_in_sub1.py").write("def test_1(): pass")
- sub2.join("test_in_sub2.py").write("def test_2(): pass")
- result = testdir.runpytest("-v", "-s")
- result.assert_outcomes(passed=2)
-
-def test_modulecol_roundtrip(testdir):
- modcol = testdir.getmodulecol("pass", withinit=True)
- trail = modcol.nodeid
- newcol = modcol.session.perform_collect([trail], genitems=0)[0]
- assert modcol.name == newcol.name
-
-
-class TestTracebackCutting:
- def test_skip_simple(self):
- excinfo = pytest.raises(pytest.skip.Exception, 'pytest.skip("xxx")')
- assert excinfo.traceback[-1].frame.code.name == "skip"
- assert excinfo.traceback[-1].ishidden()
-
- def test_traceback_argsetup(self, testdir):
- testdir.makeconftest("""
- def pytest_funcarg__hello(request):
- raise ValueError("xyz")
- """)
- p = testdir.makepyfile("def test(hello): pass")
- result = testdir.runpytest(p)
- assert result.ret != 0
- out = result.stdout.str()
- assert out.find("xyz") != -1
- assert out.find("conftest.py:2: ValueError") != -1
- numentries = out.count("_ _ _") # separator for traceback entries
- assert numentries == 0
-
- result = testdir.runpytest("--fulltrace", p)
- out = result.stdout.str()
- assert out.find("conftest.py:2: ValueError") != -1
- numentries = out.count("_ _ _ _") # separator for traceback entries
- assert numentries > 3
-
- def test_traceback_error_during_import(self, testdir):
- testdir.makepyfile("""
- x = 1
- x = 2
- x = 17
- asd
- """)
- result = testdir.runpytest()
- assert result.ret != 0
- out = result.stdout.str()
- assert "x = 1" not in out
- assert "x = 2" not in out
- result.stdout.fnmatch_lines([
- " *asd*",
- "E*NameError*",
- ])
- result = testdir.runpytest("--fulltrace")
- out = result.stdout.str()
- assert "x = 1" in out
- assert "x = 2" in out
- result.stdout.fnmatch_lines([
- ">*asd*",
- "E*NameError*",
- ])
-
- def test_traceback_filter_error_during_fixture_collection(self, testdir):
- """integration test for issue #995.
- """
- testdir.makepyfile("""
- import pytest
-
- def fail_me(func):
- ns = {}
- exec('def w(): raise ValueError("fail me")', ns)
- return ns['w']
-
- @pytest.fixture(scope='class')
- @fail_me
- def fail_fixture():
- pass
-
- def test_failing_fixture(fail_fixture):
- pass
- """)
- result = testdir.runpytest()
- assert result.ret != 0
- out = result.stdout.str()
- assert "INTERNALERROR>" not in out
- result.stdout.fnmatch_lines([
- "*ValueError: fail me*",
- "* 1 error in *",
- ])
-
- def test_filter_traceback_generated_code(self):
- """test that filter_traceback() works with the fact that
- py.code.Code.path attribute might return an str object.
- In this case, one of the entries on the traceback was produced by
- dynamically generated code.
- See: https://bitbucket.org/pytest-dev/py/issues/71
- This fixes #995.
- """
- from _pytest.python import filter_traceback
- try:
- ns = {}
- exec('def foo(): raise ValueError', ns)
- ns['foo']()
- except ValueError:
- _, _, tb = sys.exc_info()
-
- tb = _pytest._code.Traceback(tb)
- assert isinstance(tb[-1].path, str)
- assert not filter_traceback(tb[-1])
-
- def test_filter_traceback_path_no_longer_valid(self, testdir):
- """test that filter_traceback() works with the fact that
- py.code.Code.path attribute might return an str object.
- In this case, one of the files in the traceback no longer exists.
- This fixes #1133.
- """
- from _pytest.python import filter_traceback
- testdir.syspathinsert()
- testdir.makepyfile(filter_traceback_entry_as_str='''
- def foo():
- raise ValueError
- ''')
- try:
- import filter_traceback_entry_as_str
- filter_traceback_entry_as_str.foo()
- except ValueError:
- _, _, tb = sys.exc_info()
-
- testdir.tmpdir.join('filter_traceback_entry_as_str.py').remove()
- tb = _pytest._code.Traceback(tb)
- assert isinstance(tb[-1].path, str)
- assert filter_traceback(tb[-1])
-
-
-class TestReportInfo:
- def test_itemreport_reportinfo(self, testdir, linecomp):
- testdir.makeconftest("""
- import pytest
- class MyFunction(pytest.Function):
- def reportinfo(self):
- return "ABCDE", 42, "custom"
- def pytest_pycollect_makeitem(collector, name, obj):
- if name == "test_func":
- return MyFunction(name, parent=collector)
- """)
- item = testdir.getitem("def test_func(): pass")
- item.config.pluginmanager.getplugin("runner")
- assert item.location == ("ABCDE", 42, "custom")
-
- def test_func_reportinfo(self, testdir):
- item = testdir.getitem("def test_func(): pass")
- fspath, lineno, modpath = item.reportinfo()
- assert fspath == item.fspath
- assert lineno == 0
- assert modpath == "test_func"
-
- def test_class_reportinfo(self, testdir):
- modcol = testdir.getmodulecol("""
- # lineno 0
- class TestClass:
- def test_hello(self): pass
- """)
- classcol = testdir.collect_by_name(modcol, "TestClass")
- fspath, lineno, msg = classcol.reportinfo()
- assert fspath == modcol.fspath
- assert lineno == 1
- assert msg == "TestClass"
-
- def test_generator_reportinfo(self, testdir):
- modcol = testdir.getmodulecol("""
- # lineno 0
- def test_gen():
- def check(x):
- assert x
- yield check, 3
- """)
- gencol = testdir.collect_by_name(modcol, "test_gen")
- fspath, lineno, modpath = gencol.reportinfo()
- assert fspath == modcol.fspath
- assert lineno == 1
- assert modpath == "test_gen"
-
- genitem = gencol.collect()[0]
- fspath, lineno, modpath = genitem.reportinfo()
- assert fspath == modcol.fspath
- assert lineno == 2
- assert modpath == "test_gen[0]"
- """
- def test_func():
- pass
- def test_genfunc():
- def check(x):
- pass
- yield check, 3
- class TestClass:
- def test_method(self):
- pass
- """
-
- def test_reportinfo_with_nasty_getattr(self, testdir):
- # https://github.com/pytest-dev/pytest/issues/1204
- modcol = testdir.getmodulecol("""
- # lineno 0
- class TestClass:
- def __getattr__(self, name):
- return "this is not an int"
-
- def test_foo(self):
- pass
- """)
- classcol = testdir.collect_by_name(modcol, "TestClass")
- instance = classcol.collect()[0]
- fspath, lineno, msg = instance.reportinfo()
-
-
-def test_customized_python_discovery(testdir):
- testdir.makeini("""
- [pytest]
- python_files=check_*.py
- python_classes=Check
- python_functions=check
- """)
- p = testdir.makepyfile("""
- def check_simple():
- pass
- class CheckMyApp:
- def check_meth(self):
- pass
- """)
- p2 = p.new(basename=p.basename.replace("test", "check"))
- p.move(p2)
- result = testdir.runpytest("--collect-only", "-s")
- result.stdout.fnmatch_lines([
- "*check_customized*",
- "*check_simple*",
- "*CheckMyApp*",
- "*check_meth*",
- ])
-
- result = testdir.runpytest()
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*2 passed*",
- ])
-
-
-def test_customized_python_discovery_functions(testdir):
- testdir.makeini("""
- [pytest]
- python_functions=_test
- """)
- testdir.makepyfile("""
- def _test_underscore():
- pass
- """)
- result = testdir.runpytest("--collect-only", "-s")
- result.stdout.fnmatch_lines([
- "*_test_underscore*",
- ])
-
- result = testdir.runpytest()
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*1 passed*",
- ])
-
-
-def test_collector_attributes(testdir):
- testdir.makeconftest("""
- import pytest
- def pytest_pycollect_makeitem(collector):
- assert collector.Function == pytest.Function
- assert collector.Class == pytest.Class
- assert collector.Instance == pytest.Instance
- assert collector.Module == pytest.Module
- """)
- testdir.makepyfile("""
- def test_hello():
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*1 passed*",
- ])
-
-def test_customize_through_attributes(testdir):
- testdir.makeconftest("""
- import pytest
- class MyFunction(pytest.Function):
- pass
- class MyInstance(pytest.Instance):
- Function = MyFunction
- class MyClass(pytest.Class):
- Instance = MyInstance
-
- def pytest_pycollect_makeitem(collector, name, obj):
- if name.startswith("MyTestClass"):
- return MyClass(name, parent=collector)
- """)
- testdir.makepyfile("""
- class MyTestClass:
- def test_hello(self):
- pass
- """)
- result = testdir.runpytest("--collect-only")
- result.stdout.fnmatch_lines([
- "*MyClass*",
- "*MyInstance*",
- "*MyFunction*test_hello*",
- ])
-
-
-def test_unorderable_types(testdir):
- testdir.makepyfile("""
- class TestJoinEmpty:
- pass
-
- def make_test():
- class Test:
- pass
- Test.__name__ = "TestFoo"
- return Test
- TestFoo = make_test()
- """)
- result = testdir.runpytest()
- assert "TypeError" not in result.stdout.str()
- assert result.ret == EXIT_NOTESTSCOLLECTED
-
-
-def test_collect_functools_partial(testdir):
- """
- Test that collection of functools.partial object works, and arguments
- to the wrapped functions are dealt correctly (see #811).
- """
- testdir.makepyfile("""
- import functools
- import pytest
-
- @pytest.fixture
- def fix1():
- return 'fix1'
-
- @pytest.fixture
- def fix2():
- return 'fix2'
-
- def check1(i, fix1):
- assert i == 2
- assert fix1 == 'fix1'
-
- def check2(fix1, i):
- assert i == 2
- assert fix1 == 'fix1'
-
- def check3(fix1, i, fix2):
- assert i == 2
- assert fix1 == 'fix1'
- assert fix2 == 'fix2'
-
- test_ok_1 = functools.partial(check1, i=2)
- test_ok_2 = functools.partial(check1, i=2, fix1='fix1')
- test_ok_3 = functools.partial(check1, 2)
- test_ok_4 = functools.partial(check2, i=2)
- test_ok_5 = functools.partial(check3, i=2)
- test_ok_6 = functools.partial(check3, i=2, fix1='fix1')
-
- test_fail_1 = functools.partial(check2, 2)
- test_fail_2 = functools.partial(check3, 2)
- """)
- result = testdir.inline_run()
- result.assertoutcome(passed=6, failed=2)
-
-
-def test_dont_collect_non_function_callable(testdir):
- """Test for issue https://github.com/pytest-dev/pytest/issues/331
-
- In this case an INTERNALERROR occurred trying to report the failure of
- a test like this one because py test failed to get the source lines.
- """
- testdir.makepyfile("""
- class Oh(object):
- def __call__(self):
- pass
-
- test_a = Oh()
-
- def test_real():
- pass
- """)
- result = testdir.runpytest('-rw')
- result.stdout.fnmatch_lines([
- '*collected 1 item*',
- 'WC2 *',
- '*1 passed, 1 pytest-warnings in *',
- ])
-
-
-def test_class_injection_does_not_break_collection(testdir):
- """Tests whether injection during collection time will terminate testing.
-
- In this case the error should not occur if the TestClass itself
- is modified during collection time, and the original method list
- is still used for collection.
- """
- testdir.makeconftest("""
- from test_inject import TestClass
- def pytest_generate_tests(metafunc):
- TestClass.changed_var = {}
- """)
- testdir.makepyfile(test_inject='''
- class TestClass(object):
- def test_injection(self):
- """Test being parametrized."""
- pass
- ''')
- result = testdir.runpytest()
- assert "RuntimeError: dictionary changed size during iteration" not in result.stdout.str()
- result.stdout.fnmatch_lines(['*1 passed*'])
-
-
-def test_syntax_error_with_non_ascii_chars(testdir):
- """Fix decoding issue while formatting SyntaxErrors during collection (#578)
- """
- testdir.makepyfile(u"""
- # -*- coding: UTF-8 -*-
-
- ☃
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- '*ERROR collecting*',
- '*SyntaxError*',
- '*1 error in*',
- ])
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/python/fixture.py b/tests/wpt/web-platform-tests/tools/pytest/testing/python/fixture.py
deleted file mode 100644
index 506d8426e3c..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/python/fixture.py
+++ /dev/null
@@ -1,2693 +0,0 @@
-from textwrap import dedent
-
-import _pytest._code
-import pytest
-import sys
-from _pytest import python as funcargs
-from _pytest.pytester import get_public_names
-from _pytest.python import FixtureLookupError
-
-
-def test_getfuncargnames():
- def f(): pass
- assert not funcargs.getfuncargnames(f)
- def g(arg): pass
- assert funcargs.getfuncargnames(g) == ('arg',)
- def h(arg1, arg2="hello"): pass
- assert funcargs.getfuncargnames(h) == ('arg1',)
- def h(arg1, arg2, arg3="hello"): pass
- assert funcargs.getfuncargnames(h) == ('arg1', 'arg2')
- class A:
- def f(self, arg1, arg2="hello"):
- pass
- assert funcargs.getfuncargnames(A().f) == ('arg1',)
- if sys.version_info < (3,0):
- assert funcargs.getfuncargnames(A.f) == ('arg1',)
-
-class TestFillFixtures:
- def test_fillfuncargs_exposed(self):
- # used by oejskit, kept for compatibility
- assert pytest._fillfuncargs == funcargs.fillfixtures
-
- def test_funcarg_lookupfails(self, testdir):
- testdir.makepyfile("""
- def pytest_funcarg__xyzsomething(request):
- return 42
-
- def test_func(some):
- pass
- """)
- result = testdir.runpytest() # "--collect-only")
- assert result.ret != 0
- result.stdout.fnmatch_lines([
- "*def test_func(some)*",
- "*fixture*some*not found*",
- "*xyzsomething*",
- ])
-
- def test_funcarg_basic(self, testdir):
- item = testdir.getitem("""
- def pytest_funcarg__some(request):
- return request.function.__name__
- def pytest_funcarg__other(request):
- return 42
- def test_func(some, other):
- pass
- """)
- funcargs.fillfixtures(item)
- del item.funcargs["request"]
- assert len(get_public_names(item.funcargs)) == 2
- assert item.funcargs['some'] == "test_func"
- assert item.funcargs['other'] == 42
-
- def test_funcarg_lookup_modulelevel(self, testdir):
- testdir.makepyfile("""
- def pytest_funcarg__something(request):
- return request.function.__name__
-
- class TestClass:
- def test_method(self, something):
- assert something == "test_method"
- def test_func(something):
- assert something == "test_func"
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
- def test_funcarg_lookup_classlevel(self, testdir):
- p = testdir.makepyfile("""
- class TestClass:
- def pytest_funcarg__something(self, request):
- return request.instance
- def test_method(self, something):
- assert something is self
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*1 passed*"
- ])
-
- def test_conftest_funcargs_only_available_in_subdir(self, testdir):
- sub1 = testdir.mkpydir("sub1")
- sub2 = testdir.mkpydir("sub2")
- sub1.join("conftest.py").write(_pytest._code.Source("""
- import pytest
- def pytest_funcarg__arg1(request):
- pytest.raises(Exception, "request.getfuncargvalue('arg2')")
- """))
- sub2.join("conftest.py").write(_pytest._code.Source("""
- import pytest
- def pytest_funcarg__arg2(request):
- pytest.raises(Exception, "request.getfuncargvalue('arg1')")
- """))
-
- sub1.join("test_in_sub1.py").write("def test_1(arg1): pass")
- sub2.join("test_in_sub2.py").write("def test_2(arg2): pass")
- result = testdir.runpytest("-v")
- result.assert_outcomes(passed=2)
-
- def test_extend_fixture_module_class(self, testdir):
- testfile = testdir.makepyfile("""
- import pytest
-
- @pytest.fixture
- def spam():
- return 'spam'
-
- class TestSpam:
-
- @pytest.fixture
- def spam(self, spam):
- return spam * 2
-
- def test_spam(self, spam):
- assert spam == 'spamspam'
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines(["*1 passed*"])
- result = testdir.runpytest(testfile)
- result.stdout.fnmatch_lines(["*1 passed*"])
-
- def test_extend_fixture_conftest_module(self, testdir):
- testdir.makeconftest("""
- import pytest
-
- @pytest.fixture
- def spam():
- return 'spam'
- """)
- testfile = testdir.makepyfile("""
- import pytest
-
- @pytest.fixture
- def spam(spam):
- return spam * 2
-
- def test_spam(spam):
- assert spam == 'spamspam'
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines(["*1 passed*"])
- result = testdir.runpytest(testfile)
- result.stdout.fnmatch_lines(["*1 passed*"])
-
- def test_extend_fixture_conftest_conftest(self, testdir):
- testdir.makeconftest("""
- import pytest
-
- @pytest.fixture
- def spam():
- return 'spam'
- """)
- pkg = testdir.mkpydir("pkg")
- pkg.join("conftest.py").write(_pytest._code.Source("""
- import pytest
-
- @pytest.fixture
- def spam(spam):
- return spam * 2
- """))
- testfile = pkg.join("test_spam.py")
- testfile.write(_pytest._code.Source("""
- def test_spam(spam):
- assert spam == "spamspam"
- """))
- result = testdir.runpytest()
- result.stdout.fnmatch_lines(["*1 passed*"])
- result = testdir.runpytest(testfile)
- result.stdout.fnmatch_lines(["*1 passed*"])
-
- def test_extend_fixture_conftest_plugin(self, testdir):
- testdir.makepyfile(testplugin="""
- import pytest
-
- @pytest.fixture
- def foo():
- return 7
- """)
- testdir.syspathinsert()
- testdir.makeconftest("""
- import pytest
-
- pytest_plugins = 'testplugin'
-
- @pytest.fixture
- def foo(foo):
- return foo + 7
- """)
- testdir.makepyfile("""
- def test_foo(foo):
- assert foo == 14
- """)
- result = testdir.runpytest('-s')
- assert result.ret == 0
-
- def test_extend_fixture_plugin_plugin(self, testdir):
- # Two plugins should extend each order in loading order
- testdir.makepyfile(testplugin0="""
- import pytest
-
- @pytest.fixture
- def foo():
- return 7
- """)
- testdir.makepyfile(testplugin1="""
- import pytest
-
- @pytest.fixture
- def foo(foo):
- return foo + 7
- """)
- testdir.syspathinsert()
- testdir.makepyfile("""
- pytest_plugins = ['testplugin0', 'testplugin1']
-
- def test_foo(foo):
- assert foo == 14
- """)
- result = testdir.runpytest()
- assert result.ret == 0
-
- def test_override_parametrized_fixture_conftest_module(self, testdir):
- """Test override of the parametrized fixture with non-parametrized one on the test module level."""
- testdir.makeconftest("""
- import pytest
-
- @pytest.fixture(params=[1, 2, 3])
- def spam(request):
- return request.param
- """)
- testfile = testdir.makepyfile("""
- import pytest
-
- @pytest.fixture
- def spam():
- return 'spam'
-
- def test_spam(spam):
- assert spam == 'spam'
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines(["*1 passed*"])
- result = testdir.runpytest(testfile)
- result.stdout.fnmatch_lines(["*1 passed*"])
-
- def test_override_parametrized_fixture_conftest_conftest(self, testdir):
- """Test override of the parametrized fixture with non-parametrized one on the conftest level."""
- testdir.makeconftest("""
- import pytest
-
- @pytest.fixture(params=[1, 2, 3])
- def spam(request):
- return request.param
- """)
- subdir = testdir.mkpydir('subdir')
- subdir.join("conftest.py").write(_pytest._code.Source("""
- import pytest
-
- @pytest.fixture
- def spam():
- return 'spam'
- """))
- testfile = subdir.join("test_spam.py")
- testfile.write(_pytest._code.Source("""
- def test_spam(spam):
- assert spam == "spam"
- """))
- result = testdir.runpytest()
- result.stdout.fnmatch_lines(["*1 passed*"])
- result = testdir.runpytest(testfile)
- result.stdout.fnmatch_lines(["*1 passed*"])
-
- def test_override_non_parametrized_fixture_conftest_module(self, testdir):
- """Test override of the non-parametrized fixture with parametrized one on the test module level."""
- testdir.makeconftest("""
- import pytest
-
- @pytest.fixture
- def spam():
- return 'spam'
- """)
- testfile = testdir.makepyfile("""
- import pytest
-
- @pytest.fixture(params=[1, 2, 3])
- def spam(request):
- return request.param
-
- params = {'spam': 1}
-
- def test_spam(spam):
- assert spam == params['spam']
- params['spam'] += 1
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines(["*3 passed*"])
- result = testdir.runpytest(testfile)
- result.stdout.fnmatch_lines(["*3 passed*"])
-
- def test_override_non_parametrized_fixture_conftest_conftest(self, testdir):
- """Test override of the non-parametrized fixture with parametrized one on the conftest level."""
- testdir.makeconftest("""
- import pytest
-
- @pytest.fixture
- def spam():
- return 'spam'
- """)
- subdir = testdir.mkpydir('subdir')
- subdir.join("conftest.py").write(_pytest._code.Source("""
- import pytest
-
- @pytest.fixture(params=[1, 2, 3])
- def spam(request):
- return request.param
- """))
- testfile = subdir.join("test_spam.py")
- testfile.write(_pytest._code.Source("""
- params = {'spam': 1}
-
- def test_spam(spam):
- assert spam == params['spam']
- params['spam'] += 1
- """))
- result = testdir.runpytest()
- result.stdout.fnmatch_lines(["*3 passed*"])
- result = testdir.runpytest(testfile)
- result.stdout.fnmatch_lines(["*3 passed*"])
-
- def test_autouse_fixture_plugin(self, testdir):
- # A fixture from a plugin has no baseid set, which screwed up
- # the autouse fixture handling.
- testdir.makepyfile(testplugin="""
- import pytest
-
- @pytest.fixture(autouse=True)
- def foo(request):
- request.function.foo = 7
- """)
- testdir.syspathinsert()
- testdir.makepyfile("""
- pytest_plugins = 'testplugin'
-
- def test_foo(request):
- assert request.function.foo == 7
- """)
- result = testdir.runpytest()
- assert result.ret == 0
-
- def test_funcarg_lookup_error(self, testdir):
- testdir.makepyfile("""
- def test_lookup_error(unknown):
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*ERROR*test_lookup_error*",
- "*def test_lookup_error(unknown):*",
- "*fixture*unknown*not found*",
- "*available fixtures*",
- "*1 error*",
- ])
- assert "INTERNAL" not in result.stdout.str()
-
- def test_fixture_excinfo_leak(self, testdir):
- # on python2 sys.excinfo would leak into fixture executions
- testdir.makepyfile("""
- import sys
- import traceback
- import pytest
-
- @pytest.fixture
- def leak():
- if sys.exc_info()[0]: # python3 bug :)
- traceback.print_exc()
- #fails
- assert sys.exc_info() == (None, None, None)
-
- def test_leak(leak):
- if sys.exc_info()[0]: # python3 bug :)
- traceback.print_exc()
- assert sys.exc_info() == (None, None, None)
- """)
- result = testdir.runpytest()
- assert result.ret == 0
-
-
-class TestRequestBasic:
- def test_request_attributes(self, testdir):
- item = testdir.getitem("""
- def pytest_funcarg__something(request): pass
- def test_func(something): pass
- """)
- req = funcargs.FixtureRequest(item)
- assert req.function == item.obj
- assert req.keywords == item.keywords
- assert hasattr(req.module, 'test_func')
- assert req.cls is None
- assert req.function.__name__ == "test_func"
- assert req.config == item.config
- assert repr(req).find(req.function.__name__) != -1
-
- def test_request_attributes_method(self, testdir):
- item, = testdir.getitems("""
- class TestB:
- def pytest_funcarg__something(self, request):
- return 1
- def test_func(self, something):
- pass
- """)
- req = item._request
- assert req.cls.__name__ == "TestB"
- assert req.instance.__class__ == req.cls
-
- def XXXtest_request_contains_funcarg_arg2fixturedefs(self, testdir):
- modcol = testdir.getmodulecol("""
- def pytest_funcarg__something(request):
- pass
- class TestClass:
- def test_method(self, something):
- pass
- """)
- item1, = testdir.genitems([modcol])
- assert item1.name == "test_method"
- arg2fixturedefs = funcargs.FixtureRequest(item1)._arg2fixturedefs
- assert len(arg2fixturedefs) == 1
- assert arg2fixturedefs[0].__name__ == "pytest_funcarg__something"
-
- def test_getfuncargvalue_recursive(self, testdir):
- testdir.makeconftest("""
- def pytest_funcarg__something(request):
- return 1
- """)
- testdir.makepyfile("""
- def pytest_funcarg__something(request):
- return request.getfuncargvalue("something") + 1
- def test_func(something):
- assert something == 2
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_getfuncargvalue(self, testdir):
- item = testdir.getitem("""
- l = [2]
- def pytest_funcarg__something(request): return 1
- def pytest_funcarg__other(request):
- return l.pop()
- def test_func(something): pass
- """)
- req = item._request
- pytest.raises(FixtureLookupError, req.getfuncargvalue, "notexists")
- val = req.getfuncargvalue("something")
- assert val == 1
- val = req.getfuncargvalue("something")
- assert val == 1
- val2 = req.getfuncargvalue("other")
- assert val2 == 2
- val2 = req.getfuncargvalue("other") # see about caching
- assert val2 == 2
- pytest._fillfuncargs(item)
- assert item.funcargs["something"] == 1
- assert len(get_public_names(item.funcargs)) == 2
- assert "request" in item.funcargs
- #assert item.funcargs == {'something': 1, "other": 2}
-
- def test_request_addfinalizer(self, testdir):
- item = testdir.getitem("""
- teardownlist = []
- def pytest_funcarg__something(request):
- request.addfinalizer(lambda: teardownlist.append(1))
- def test_func(something): pass
- """)
- item.session._setupstate.prepare(item)
- pytest._fillfuncargs(item)
- # successively check finalization calls
- teardownlist = item.getparent(pytest.Module).obj.teardownlist
- ss = item.session._setupstate
- assert not teardownlist
- ss.teardown_exact(item, None)
- print(ss.stack)
- assert teardownlist == [1]
-
- def test_request_addfinalizer_failing_setup(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = [1]
- @pytest.fixture
- def myfix(request):
- request.addfinalizer(l.pop)
- assert 0
- def test_fix(myfix):
- pass
- def test_finalizer_ran():
- assert not l
- """)
- reprec = testdir.inline_run("-s")
- reprec.assertoutcome(failed=1, passed=1)
-
- def test_request_addfinalizer_failing_setup_module(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = [1, 2]
- @pytest.fixture(scope="module")
- def myfix(request):
- request.addfinalizer(l.pop)
- request.addfinalizer(l.pop)
- assert 0
- def test_fix(myfix):
- pass
- """)
- reprec = testdir.inline_run("-s")
- mod = reprec.getcalls("pytest_runtest_setup")[0].item.module
- assert not mod.l
-
-
- def test_request_addfinalizer_partial_setup_failure(self, testdir):
- p = testdir.makepyfile("""
- l = []
- def pytest_funcarg__something(request):
- request.addfinalizer(lambda: l.append(None))
- def test_func(something, missingarg):
- pass
- def test_second():
- assert len(l) == 1
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*1 error*" # XXX the whole module collection fails
- ])
-
- def test_request_getmodulepath(self, testdir):
- modcol = testdir.getmodulecol("def test_somefunc(): pass")
- item, = testdir.genitems([modcol])
- req = funcargs.FixtureRequest(item)
- assert req.fspath == modcol.fspath
-
- def test_request_fixturenames(self, testdir):
- testdir.makepyfile("""
- import pytest
- from _pytest.pytester import get_public_names
- @pytest.fixture()
- def arg1():
- pass
- @pytest.fixture()
- def farg(arg1):
- pass
- @pytest.fixture(autouse=True)
- def sarg(tmpdir):
- pass
- def test_function(request, farg):
- assert set(get_public_names(request.fixturenames)) == \
- set(["tmpdir", "sarg", "arg1", "request", "farg",
- "tmpdir_factory"])
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_funcargnames_compatattr(self, testdir):
- testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- assert metafunc.funcargnames == metafunc.fixturenames
- def pytest_funcarg__fn(request):
- assert request._pyfuncitem.funcargnames == \
- request._pyfuncitem.fixturenames
- return request.funcargnames, request.fixturenames
-
- def test_hello(fn):
- assert fn[0] == fn[1]
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_setupdecorator_and_xunit(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(scope='module', autouse=True)
- def setup_module():
- l.append("module")
- @pytest.fixture(autouse=True)
- def setup_function():
- l.append("function")
-
- def test_func():
- pass
-
- class TestClass:
- @pytest.fixture(scope="class", autouse=True)
- def setup_class(self):
- l.append("class")
- @pytest.fixture(autouse=True)
- def setup_method(self):
- l.append("method")
- def test_method(self):
- pass
- def test_all():
- assert l == ["module", "function", "class",
- "function", "method", "function"]
- """)
- reprec = testdir.inline_run("-v")
- reprec.assertoutcome(passed=3)
-
- def test_fixtures_sub_subdir_normalize_sep(self, testdir):
- # this tests that normalization of nodeids takes place
- b = testdir.mkdir("tests").mkdir("unit")
- b.join("conftest.py").write(_pytest._code.Source("""
- def pytest_funcarg__arg1():
- pass
- """))
- p = b.join("test_module.py")
- p.write("def test_func(arg1): pass")
- result = testdir.runpytest(p, "--fixtures")
- assert result.ret == 0
- result.stdout.fnmatch_lines("""
- *fixtures defined*conftest*
- *arg1*
- """)
-
- def test_show_fixtures_color_yes(self, testdir):
- testdir.makepyfile("def test_this(): assert 1")
- result = testdir.runpytest('--color=yes', '--fixtures')
- assert '\x1b[32mtmpdir' in result.stdout.str()
-
- def test_newstyle_with_request(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture()
- def arg(request):
- pass
- def test_1(arg):
- pass
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_setupcontext_no_param(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(params=[1,2])
- def arg(request):
- return request.param
-
- @pytest.fixture(autouse=True)
- def mysetup(request, arg):
- assert not hasattr(request, "param")
- def test_1(arg):
- assert arg in (1,2)
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
-class TestRequestMarking:
- def test_applymarker(self, testdir):
- item1,item2 = testdir.getitems("""
- def pytest_funcarg__something(request):
- pass
- class TestClass:
- def test_func1(self, something):
- pass
- def test_func2(self, something):
- pass
- """)
- req1 = funcargs.FixtureRequest(item1)
- assert 'xfail' not in item1.keywords
- req1.applymarker(pytest.mark.xfail)
- assert 'xfail' in item1.keywords
- assert 'skipif' not in item1.keywords
- req1.applymarker(pytest.mark.skipif)
- assert 'skipif' in item1.keywords
- pytest.raises(ValueError, "req1.applymarker(42)")
-
- def test_accesskeywords(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture()
- def keywords(request):
- return request.keywords
- @pytest.mark.XYZ
- def test_function(keywords):
- assert keywords["XYZ"]
- assert "abc" not in keywords
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_accessmarker_dynamic(self, testdir):
- testdir.makeconftest("""
- import pytest
- @pytest.fixture()
- def keywords(request):
- return request.keywords
-
- @pytest.fixture(scope="class", autouse=True)
- def marking(request):
- request.applymarker(pytest.mark.XYZ("hello"))
- """)
- testdir.makepyfile("""
- import pytest
- def test_fun1(keywords):
- assert keywords["XYZ"] is not None
- assert "abc" not in keywords
- def test_fun2(keywords):
- assert keywords["XYZ"] is not None
- assert "abc" not in keywords
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
-class TestRequestCachedSetup:
- def test_request_cachedsetup_defaultmodule(self, testdir):
- reprec = testdir.inline_runsource("""
- mysetup = ["hello",].pop
-
- def pytest_funcarg__something(request):
- return request.cached_setup(mysetup, scope="module")
-
- def test_func1(something):
- assert something == "hello"
- class TestClass:
- def test_func1a(self, something):
- assert something == "hello"
- """)
- reprec.assertoutcome(passed=2)
-
- def test_request_cachedsetup_class(self, testdir):
- reprec = testdir.inline_runsource("""
- mysetup = ["hello", "hello2", "hello3"].pop
-
- def pytest_funcarg__something(request):
- return request.cached_setup(mysetup, scope="class")
- def test_func1(something):
- assert something == "hello3"
- def test_func2(something):
- assert something == "hello2"
- class TestClass:
- def test_func1a(self, something):
- assert something == "hello"
- def test_func2b(self, something):
- assert something == "hello"
- """)
- reprec.assertoutcome(passed=4)
-
- def test_request_cachedsetup_extrakey(self, testdir):
- item1 = testdir.getitem("def test_func(): pass")
- req1 = funcargs.FixtureRequest(item1)
- l = ["hello", "world"]
- def setup():
- return l.pop()
- ret1 = req1.cached_setup(setup, extrakey=1)
- ret2 = req1.cached_setup(setup, extrakey=2)
- assert ret2 == "hello"
- assert ret1 == "world"
- ret1b = req1.cached_setup(setup, extrakey=1)
- ret2b = req1.cached_setup(setup, extrakey=2)
- assert ret1 == ret1b
- assert ret2 == ret2b
-
- def test_request_cachedsetup_cache_deletion(self, testdir):
- item1 = testdir.getitem("def test_func(): pass")
- req1 = funcargs.FixtureRequest(item1)
- l = []
- def setup():
- l.append("setup")
- def teardown(val):
- l.append("teardown")
- req1.cached_setup(setup, teardown, scope="function")
- assert l == ['setup']
- # artificial call of finalizer
- setupstate = req1._pyfuncitem.session._setupstate
- setupstate._callfinalizers(item1)
- assert l == ["setup", "teardown"]
- req1.cached_setup(setup, teardown, scope="function")
- assert l == ["setup", "teardown", "setup"]
- setupstate._callfinalizers(item1)
- assert l == ["setup", "teardown", "setup", "teardown"]
-
- def test_request_cached_setup_two_args(self, testdir):
- testdir.makepyfile("""
- def pytest_funcarg__arg1(request):
- return request.cached_setup(lambda: 42)
- def pytest_funcarg__arg2(request):
- return request.cached_setup(lambda: 17)
- def test_two_different_setups(arg1, arg2):
- assert arg1 != arg2
- """)
- result = testdir.runpytest("-v")
- result.stdout.fnmatch_lines([
- "*1 passed*"
- ])
-
- def test_request_cached_setup_getfuncargvalue(self, testdir):
- testdir.makepyfile("""
- def pytest_funcarg__arg1(request):
- arg1 = request.getfuncargvalue("arg2")
- return request.cached_setup(lambda: arg1 + 1)
- def pytest_funcarg__arg2(request):
- return request.cached_setup(lambda: 10)
- def test_two_funcarg(arg1):
- assert arg1 == 11
- """)
- result = testdir.runpytest("-v")
- result.stdout.fnmatch_lines([
- "*1 passed*"
- ])
-
- def test_request_cached_setup_functional(self, testdir):
- testdir.makepyfile(test_0="""
- l = []
- def pytest_funcarg__something(request):
- val = request.cached_setup(fsetup, fteardown)
- return val
- def fsetup(mycache=[1]):
- l.append(mycache.pop())
- return l
- def fteardown(something):
- l.remove(something[0])
- l.append(2)
- def test_list_once(something):
- assert something == [1]
- def test_list_twice(something):
- assert something == [1]
- """)
- testdir.makepyfile(test_1="""
- import test_0 # should have run already
- def test_check_test0_has_teardown_correct():
- assert test_0.l == [2]
- """)
- result = testdir.runpytest("-v")
- result.stdout.fnmatch_lines([
- "*3 passed*"
- ])
-
- def test_issue117_sessionscopeteardown(self, testdir):
- testdir.makepyfile("""
- def pytest_funcarg__app(request):
- app = request.cached_setup(
- scope='session',
- setup=lambda: 0,
- teardown=lambda x: 3/x)
- return app
- def test_func(app):
- pass
- """)
- result = testdir.runpytest()
- assert result.ret != 0
- result.stdout.fnmatch_lines([
- "*3/x*",
- "*ZeroDivisionError*",
- ])
-
-class TestFixtureUsages:
- def test_noargfixturedec(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture
- def arg1():
- return 1
-
- def test_func(arg1):
- assert arg1 == 1
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_receives_funcargs(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture()
- def arg1():
- return 1
-
- @pytest.fixture()
- def arg2(arg1):
- return arg1 + 1
-
- def test_add(arg2):
- assert arg2 == 2
- def test_all(arg1, arg2):
- assert arg1 == 1
- assert arg2 == 2
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
- def test_receives_funcargs_scope_mismatch(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope="function")
- def arg1():
- return 1
-
- @pytest.fixture(scope="module")
- def arg2(arg1):
- return arg1 + 1
-
- def test_add(arg2):
- assert arg2 == 2
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*ScopeMismatch*involved factories*",
- "* def arg2*",
- "* def arg1*",
- "*1 error*"
- ])
-
- def test_receives_funcargs_scope_mismatch_issue660(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope="function")
- def arg1():
- return 1
-
- @pytest.fixture(scope="module")
- def arg2(arg1):
- return arg1 + 1
-
- def test_add(arg1, arg2):
- assert arg2 == 2
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*ScopeMismatch*involved factories*",
- "* def arg2*",
- "*1 error*"
- ])
-
- def test_funcarg_parametrized_and_used_twice(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(params=[1,2])
- def arg1(request):
- l.append(1)
- return request.param
-
- @pytest.fixture()
- def arg2(arg1):
- return arg1 + 1
-
- def test_add(arg1, arg2):
- assert arg2 == arg1 + 1
- assert len(l) == arg1
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*2 passed*"
- ])
-
- def test_factory_uses_unknown_funcarg_as_dependency_error(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture()
- def fail(missing):
- return
-
- @pytest.fixture()
- def call_fail(fail):
- return
-
- def test_missing(call_fail):
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines("""
- *pytest.fixture()*
- *def call_fail(fail)*
- *pytest.fixture()*
- *def fail*
- *fixture*'missing'*not found*
- """)
-
- def test_factory_setup_as_classes_fails(self, testdir):
- testdir.makepyfile("""
- import pytest
- class arg1:
- def __init__(self, request):
- self.x = 1
- arg1 = pytest.fixture()(arg1)
-
- """)
- reprec = testdir.inline_run()
- l = reprec.getfailedcollections()
- assert len(l) == 1
-
- def test_request_can_be_overridden(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture()
- def request(request):
- request.a = 1
- return request
- def test_request(request):
- assert request.a == 1
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_usefixtures_marker(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- l = []
-
- @pytest.fixture(scope="class")
- def myfix(request):
- request.cls.hello = "world"
- l.append(1)
-
- class TestClass:
- def test_one(self):
- assert self.hello == "world"
- assert len(l) == 1
- def test_two(self):
- assert self.hello == "world"
- assert len(l) == 1
- pytest.mark.usefixtures("myfix")(TestClass)
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
- def test_usefixtures_ini(self, testdir):
- testdir.makeini("""
- [pytest]
- usefixtures = myfix
- """)
- testdir.makeconftest("""
- import pytest
-
- @pytest.fixture(scope="class")
- def myfix(request):
- request.cls.hello = "world"
-
- """)
- testdir.makepyfile("""
- class TestClass:
- def test_one(self):
- assert self.hello == "world"
- def test_two(self):
- assert self.hello == "world"
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
- def test_usefixtures_seen_in_showmarkers(self, testdir):
- result = testdir.runpytest("--markers")
- result.stdout.fnmatch_lines("""
- *usefixtures(fixturename1*mark tests*fixtures*
- """)
-
- def test_request_instance_issue203(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- class TestClass:
- @pytest.fixture
- def setup1(self, request):
- assert self == request.instance
- self.arg1 = 1
- def test_hello(self, setup1):
- assert self.arg1 == 1
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_fixture_parametrized_with_iterator(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- l = []
- def f():
- yield 1
- yield 2
- dec = pytest.fixture(scope="module", params=f())
-
- @dec
- def arg(request):
- return request.param
- @dec
- def arg2(request):
- return request.param
-
- def test_1(arg):
- l.append(arg)
- def test_2(arg2):
- l.append(arg2*10)
- """)
- reprec = testdir.inline_run("-v")
- reprec.assertoutcome(passed=4)
- l = reprec.getcalls("pytest_runtest_call")[0].item.module.l
- assert l == [1,2, 10,20]
-
-
-class TestFixtureManagerParseFactories:
- def pytest_funcarg__testdir(self, request):
- testdir = request.getfuncargvalue("testdir")
- testdir.makeconftest("""
- def pytest_funcarg__hello(request):
- return "conftest"
-
- def pytest_funcarg__fm(request):
- return request._fixturemanager
-
- def pytest_funcarg__item(request):
- return request._pyfuncitem
- """)
- return testdir
-
- def test_parsefactories_evil_objects_issue214(self, testdir):
- testdir.makepyfile("""
- class A:
- def __call__(self):
- pass
- def __getattr__(self, name):
- raise RuntimeError()
- a = A()
- def test_hello():
- pass
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1, failed=0)
-
- def test_parsefactories_conftest(self, testdir):
- testdir.makepyfile("""
- def test_hello(item, fm):
- for name in ("fm", "hello", "item"):
- faclist = fm.getfixturedefs(name, item.nodeid)
- assert len(faclist) == 1
- fac = faclist[0]
- assert fac.func.__name__ == "pytest_funcarg__" + name
- """)
- reprec = testdir.inline_run("-s")
- reprec.assertoutcome(passed=1)
-
- def test_parsefactories_conftest_and_module_and_class(self, testdir):
- testdir.makepyfile("""
- def pytest_funcarg__hello(request):
- return "module"
- class TestClass:
- def pytest_funcarg__hello(self, request):
- return "class"
- def test_hello(self, item, fm):
- faclist = fm.getfixturedefs("hello", item.nodeid)
- print (faclist)
- assert len(faclist) == 3
- assert faclist[0].func(item._request) == "conftest"
- assert faclist[1].func(item._request) == "module"
- assert faclist[2].func(item._request) == "class"
- """)
- reprec = testdir.inline_run("-s")
- reprec.assertoutcome(passed=1)
-
- def test_parsefactories_relative_node_ids(self, testdir):
- # example mostly taken from:
- # https://mail.python.org/pipermail/pytest-dev/2014-September/002617.html
- runner = testdir.mkdir("runner")
- package = testdir.mkdir("package")
- package.join("conftest.py").write(dedent("""\
- import pytest
- @pytest.fixture
- def one():
- return 1
- """))
- package.join("test_x.py").write(dedent("""\
- def test_x(one):
- assert one == 1
- """))
- sub = package.mkdir("sub")
- sub.join("__init__.py").ensure()
- sub.join("conftest.py").write(dedent("""\
- import pytest
- @pytest.fixture
- def one():
- return 2
- """))
- sub.join("test_y.py").write(dedent("""\
- def test_x(one):
- assert one == 2
- """))
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
- with runner.as_cwd():
- reprec = testdir.inline_run("..")
- reprec.assertoutcome(passed=2)
-
-
-class TestAutouseDiscovery:
- def pytest_funcarg__testdir(self, testdir):
- testdir.makeconftest("""
- import pytest
- @pytest.fixture(autouse=True)
- def perfunction(request, tmpdir):
- pass
-
- @pytest.fixture()
- def arg1(tmpdir):
- pass
- @pytest.fixture(autouse=True)
- def perfunction2(arg1):
- pass
-
- def pytest_funcarg__fm(request):
- return request._fixturemanager
-
- def pytest_funcarg__item(request):
- return request._pyfuncitem
- """)
- return testdir
-
- def test_parsefactories_conftest(self, testdir):
- testdir.makepyfile("""
- from _pytest.pytester import get_public_names
- def test_check_setup(item, fm):
- autousenames = fm._getautousenames(item.nodeid)
- assert len(get_public_names(autousenames)) == 2
- assert "perfunction2" in autousenames
- assert "perfunction" in autousenames
- """)
- reprec = testdir.inline_run("-s")
- reprec.assertoutcome(passed=1)
-
- def test_two_classes_separated_autouse(self, testdir):
- testdir.makepyfile("""
- import pytest
- class TestA:
- l = []
- @pytest.fixture(autouse=True)
- def setup1(self):
- self.l.append(1)
- def test_setup1(self):
- assert self.l == [1]
- class TestB:
- l = []
- @pytest.fixture(autouse=True)
- def setup2(self):
- self.l.append(1)
- def test_setup2(self):
- assert self.l == [1]
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
- def test_setup_at_classlevel(self, testdir):
- testdir.makepyfile("""
- import pytest
- class TestClass:
- @pytest.fixture(autouse=True)
- def permethod(self, request):
- request.instance.funcname = request.function.__name__
- def test_method1(self):
- assert self.funcname == "test_method1"
- def test_method2(self):
- assert self.funcname == "test_method2"
- """)
- reprec = testdir.inline_run("-s")
- reprec.assertoutcome(passed=2)
-
- @pytest.mark.xfail(reason="'enabled' feature not implemented")
- def test_setup_enabled_functionnode(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- def enabled(parentnode, markers):
- return "needsdb" in markers
-
- @pytest.fixture(params=[1,2])
- def db(request):
- return request.param
-
- @pytest.fixture(enabled=enabled, autouse=True)
- def createdb(db):
- pass
-
- def test_func1(request):
- assert "db" not in request.fixturenames
-
- @pytest.mark.needsdb
- def test_func2(request):
- assert "db" in request.fixturenames
- """)
- reprec = testdir.inline_run("-s")
- reprec.assertoutcome(passed=2)
-
- def test_callables_nocode(self, testdir):
- """
- a imported mock.call would break setup/factory discovery
- due to it being callable and __code__ not being a code object
- """
- testdir.makepyfile("""
- class _call(tuple):
- def __call__(self, *k, **kw):
- pass
- def __getattr__(self, k):
- return self
-
- call = _call()
- """)
- reprec = testdir.inline_run("-s")
- reprec.assertoutcome(failed=0, passed=0)
-
- def test_autouse_in_conftests(self, testdir):
- a = testdir.mkdir("a")
- b = testdir.mkdir("a1")
- conftest = testdir.makeconftest("""
- import pytest
- @pytest.fixture(autouse=True)
- def hello():
- xxx
- """)
- conftest.move(a.join(conftest.basename))
- a.join("test_something.py").write("def test_func(): pass")
- b.join("test_otherthing.py").write("def test_func(): pass")
- result = testdir.runpytest()
- result.stdout.fnmatch_lines("""
- *1 passed*1 error*
- """)
-
- def test_autouse_in_module_and_two_classes(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(autouse=True)
- def append1():
- l.append("module")
- def test_x():
- assert l == ["module"]
-
- class TestA:
- @pytest.fixture(autouse=True)
- def append2(self):
- l.append("A")
- def test_hello(self):
- assert l == ["module", "module", "A"], l
- class TestA2:
- def test_world(self):
- assert l == ["module", "module", "A", "module"], l
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=3)
-
-
-class TestAutouseManagement:
- def test_autouse_conftest_mid_directory(self, testdir):
- pkgdir = testdir.mkpydir("xyz123")
- pkgdir.join("conftest.py").write(_pytest._code.Source("""
- import pytest
- @pytest.fixture(autouse=True)
- def app():
- import sys
- sys._myapp = "hello"
- """))
- t = pkgdir.ensure("tests", "test_app.py")
- t.write(_pytest._code.Source("""
- import sys
- def test_app():
- assert sys._myapp == "hello"
- """))
- reprec = testdir.inline_run("-s")
- reprec.assertoutcome(passed=1)
-
- def test_autouse_honored_for_yield(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(autouse=True)
- def tst():
- global x
- x = 3
- def test_gen():
- def f(hello):
- assert x == abs(hello)
- yield f, 3
- yield f, -3
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
-
-
- def test_funcarg_and_setup(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(scope="module")
- def arg():
- l.append(1)
- return 0
- @pytest.fixture(scope="module", autouse=True)
- def something(arg):
- l.append(2)
-
- def test_hello(arg):
- assert len(l) == 2
- assert l == [1,2]
- assert arg == 0
-
- def test_hello2(arg):
- assert len(l) == 2
- assert l == [1,2]
- assert arg == 0
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
- def test_uses_parametrized_resource(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(params=[1,2])
- def arg(request):
- return request.param
-
- @pytest.fixture(autouse=True)
- def something(arg):
- l.append(arg)
-
- def test_hello():
- if len(l) == 1:
- assert l == [1]
- elif len(l) == 2:
- assert l == [1, 2]
- else:
- 0/0
-
- """)
- reprec = testdir.inline_run("-s")
- reprec.assertoutcome(passed=2)
-
- def test_session_parametrized_function(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- l = []
-
- @pytest.fixture(scope="session", params=[1,2])
- def arg(request):
- return request.param
-
- @pytest.fixture(scope="function", autouse=True)
- def append(request, arg):
- if request.function.__name__ == "test_some":
- l.append(arg)
-
- def test_some():
- pass
-
- def test_result(arg):
- assert len(l) == arg
- assert l[:arg] == [1,2][:arg]
- """)
- reprec = testdir.inline_run("-v", "-s")
- reprec.assertoutcome(passed=4)
-
- def test_class_function_parametrization_finalization(self, testdir):
- p = testdir.makeconftest("""
- import pytest
- import pprint
-
- l = []
-
- @pytest.fixture(scope="function", params=[1,2])
- def farg(request):
- return request.param
-
- @pytest.fixture(scope="class", params=list("ab"))
- def carg(request):
- return request.param
-
- @pytest.fixture(scope="function", autouse=True)
- def append(request, farg, carg):
- def fin():
- l.append("fin_%s%s" % (carg, farg))
- request.addfinalizer(fin)
- """)
- testdir.makepyfile("""
- import pytest
-
- class TestClass:
- def test_1(self):
- pass
- class TestClass2:
- def test_2(self):
- pass
- """)
- reprec = testdir.inline_run("-v","-s")
- reprec.assertoutcome(passed=8)
- config = reprec.getcalls("pytest_unconfigure")[0].config
- l = config.pluginmanager._getconftestmodules(p)[0].l
- assert l == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2
-
- def test_scope_ordering(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(scope="function", autouse=True)
- def fappend2():
- l.append(2)
- @pytest.fixture(scope="class", autouse=True)
- def classappend3():
- l.append(3)
- @pytest.fixture(scope="module", autouse=True)
- def mappend():
- l.append(1)
-
- class TestHallo:
- def test_method(self):
- assert l == [1,3,2]
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_parametrization_setup_teardown_ordering(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- def pytest_generate_tests(metafunc):
- if metafunc.cls is not None:
- metafunc.parametrize("item", [1,2], scope="class")
- class TestClass:
- @pytest.fixture(scope="class", autouse=True)
- def addteardown(self, item, request):
- l.append("setup-%d" % item)
- request.addfinalizer(lambda: l.append("teardown-%d" % item))
- def test_step1(self, item):
- l.append("step1-%d" % item)
- def test_step2(self, item):
- l.append("step2-%d" % item)
-
- def test_finish():
- print (l)
- assert l == ["setup-1", "step1-1", "step2-1", "teardown-1",
- "setup-2", "step1-2", "step2-2", "teardown-2",]
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=5)
-
- def test_ordering_autouse_before_explicit(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- l = []
- @pytest.fixture(autouse=True)
- def fix1():
- l.append(1)
- @pytest.fixture()
- def arg1():
- l.append(2)
- def test_hello(arg1):
- assert l == [1,2]
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- @pytest.mark.issue226
- @pytest.mark.parametrize("param1", ["", "params=[1]"], ids=["p00","p01"])
- @pytest.mark.parametrize("param2", ["", "params=[1]"], ids=["p10","p11"])
- def test_ordering_dependencies_torndown_first(self, testdir, param1, param2):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(%(param1)s)
- def arg1(request):
- request.addfinalizer(lambda: l.append("fin1"))
- l.append("new1")
- @pytest.fixture(%(param2)s)
- def arg2(request, arg1):
- request.addfinalizer(lambda: l.append("fin2"))
- l.append("new2")
-
- def test_arg(arg2):
- pass
- def test_check():
- assert l == ["new1", "new2", "fin2", "fin1"]
- """ % locals())
- reprec = testdir.inline_run("-s")
- reprec.assertoutcome(passed=2)
-
-
-class TestFixtureMarker:
- def test_parametrize(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(params=["a", "b", "c"])
- def arg(request):
- return request.param
- l = []
- def test_param(arg):
- l.append(arg)
- def test_result():
- assert l == list("abc")
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=4)
-
- def test_multiple_parametrization_issue_736(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture(params=[1,2,3])
- def foo(request):
- return request.param
-
- @pytest.mark.parametrize('foobar', [4,5,6])
- def test_issue(foo, foobar):
- assert foo in [1,2,3]
- assert foobar in [4,5,6]
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=9)
-
- @pytest.mark.parametrize('param_args', ["'fixt, val'", "'fixt,val'", "['fixt', 'val']", "('fixt', 'val')"])
- def test_override_parametrized_fixture_issue_979(self, testdir, param_args):
- """Make sure a parametrized argument can override a parametrized fixture.
-
- This was a regression introduced in the fix for #736.
- """
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture(params=[1, 2])
- def fixt(request):
- return request.param
-
- @pytest.mark.parametrize(%s, [(3, 'x'), (4, 'x')])
- def test_foo(fixt, val):
- pass
- """ % param_args)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
- def test_scope_session(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(scope="module")
- def arg():
- l.append(1)
- return 1
-
- def test_1(arg):
- assert arg == 1
- def test_2(arg):
- assert arg == 1
- assert len(l) == 1
- class TestClass:
- def test3(self, arg):
- assert arg == 1
- assert len(l) == 1
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=3)
-
- def test_scope_session_exc(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(scope="session")
- def fix():
- l.append(1)
- pytest.skip('skipping')
-
- def test_1(fix):
- pass
- def test_2(fix):
- pass
- def test_last():
- assert l == [1]
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(skipped=2, passed=1)
-
- def test_scope_session_exc_two_fix(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- m = []
- @pytest.fixture(scope="session")
- def a():
- l.append(1)
- pytest.skip('skipping')
- @pytest.fixture(scope="session")
- def b(a):
- m.append(1)
-
- def test_1(b):
- pass
- def test_2(b):
- pass
- def test_last():
- assert l == [1]
- assert m == []
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(skipped=2, passed=1)
-
- def test_scope_exc(self, testdir):
- testdir.makepyfile(
- test_foo="""
- def test_foo(fix):
- pass
- """,
- test_bar="""
- def test_bar(fix):
- pass
- """,
- conftest="""
- import pytest
- reqs = []
- @pytest.fixture(scope="session")
- def fix(request):
- reqs.append(1)
- pytest.skip()
- @pytest.fixture
- def req_list():
- return reqs
- """,
- test_real="""
- def test_last(req_list):
- assert req_list == [1]
- """
- )
- reprec = testdir.inline_run()
- reprec.assertoutcome(skipped=2, passed=1)
-
- def test_scope_module_uses_session(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(scope="module")
- def arg():
- l.append(1)
- return 1
-
- def test_1(arg):
- assert arg == 1
- def test_2(arg):
- assert arg == 1
- assert len(l) == 1
- class TestClass:
- def test3(self, arg):
- assert arg == 1
- assert len(l) == 1
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=3)
-
- def test_scope_module_and_finalizer(self, testdir):
- testdir.makeconftest("""
- import pytest
- finalized = []
- created = []
- @pytest.fixture(scope="module")
- def arg(request):
- created.append(1)
- assert request.scope == "module"
- request.addfinalizer(lambda: finalized.append(1))
- def pytest_funcarg__created(request):
- return len(created)
- def pytest_funcarg__finalized(request):
- return len(finalized)
- """)
- testdir.makepyfile(
- test_mod1="""
- def test_1(arg, created, finalized):
- assert created == 1
- assert finalized == 0
- def test_2(arg, created, finalized):
- assert created == 1
- assert finalized == 0""",
- test_mod2="""
- def test_3(arg, created, finalized):
- assert created == 2
- assert finalized == 1""",
- test_mode3="""
- def test_4(arg, created, finalized):
- assert created == 3
- assert finalized == 2
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=4)
-
- @pytest.mark.parametrize("method", [
- 'request.getfuncargvalue("arg")',
- 'request.cached_setup(lambda: None, scope="function")',
- ], ids=["getfuncargvalue", "cached_setup"])
- def test_scope_mismatch_various(self, testdir, method):
- testdir.makeconftest("""
- import pytest
- finalized = []
- created = []
- @pytest.fixture(scope="function")
- def arg(request):
- pass
- """)
- testdir.makepyfile(
- test_mod1="""
- import pytest
- @pytest.fixture(scope="session")
- def arg(request):
- %s
- def test_1(arg):
- pass
- """ % method)
- result = testdir.runpytest()
- assert result.ret != 0
- result.stdout.fnmatch_lines([
- "*ScopeMismatch*You tried*function*session*request*",
- ])
-
- def test_register_only_with_mark(self, testdir):
- testdir.makeconftest("""
- import pytest
- @pytest.fixture()
- def arg():
- return 1
- """)
- testdir.makepyfile(
- test_mod1="""
- import pytest
- @pytest.fixture()
- def arg(arg):
- return arg + 1
- def test_1(arg):
- assert arg == 2
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_parametrize_and_scope(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope="module", params=["a", "b", "c"])
- def arg(request):
- return request.param
- l = []
- def test_param(arg):
- l.append(arg)
- """)
- reprec = testdir.inline_run("-v")
- reprec.assertoutcome(passed=3)
- l = reprec.getcalls("pytest_runtest_call")[0].item.module.l
- assert len(l) == 3
- assert "a" in l
- assert "b" in l
- assert "c" in l
-
- def test_scope_mismatch(self, testdir):
- testdir.makeconftest("""
- import pytest
- @pytest.fixture(scope="function")
- def arg(request):
- pass
- """)
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope="session")
- def arg(arg):
- pass
- def test_mismatch(arg):
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*ScopeMismatch*",
- "*1 error*",
- ])
-
- def test_parametrize_separated_order(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture(scope="module", params=[1, 2])
- def arg(request):
- return request.param
-
- l = []
- def test_1(arg):
- l.append(arg)
- def test_2(arg):
- l.append(arg)
- """)
- reprec = testdir.inline_run("-v")
- reprec.assertoutcome(passed=4)
- l = reprec.getcalls("pytest_runtest_call")[0].item.module.l
- assert l == [1,1,2,2]
-
- def test_module_parametrized_ordering(self, testdir):
- testdir.makeconftest("""
- import pytest
-
- @pytest.fixture(scope="session", params="s1 s2".split())
- def sarg():
- pass
- @pytest.fixture(scope="module", params="m1 m2".split())
- def marg():
- pass
- """)
- testdir.makepyfile(test_mod1="""
- def test_func(sarg):
- pass
- def test_func1(marg):
- pass
- """, test_mod2="""
- def test_func2(sarg):
- pass
- def test_func3(sarg, marg):
- pass
- def test_func3b(sarg, marg):
- pass
- def test_func4(marg):
- pass
- """)
- result = testdir.runpytest("-v")
- result.stdout.fnmatch_lines("""
- test_mod1.py::test_func[s1] PASSED
- test_mod2.py::test_func2[s1] PASSED
- test_mod2.py::test_func3[s1-m1] PASSED
- test_mod2.py::test_func3b[s1-m1] PASSED
- test_mod2.py::test_func3[s1-m2] PASSED
- test_mod2.py::test_func3b[s1-m2] PASSED
- test_mod1.py::test_func[s2] PASSED
- test_mod2.py::test_func2[s2] PASSED
- test_mod2.py::test_func3[s2-m1] PASSED
- test_mod2.py::test_func3b[s2-m1] PASSED
- test_mod2.py::test_func4[m1] PASSED
- test_mod2.py::test_func3[s2-m2] PASSED
- test_mod2.py::test_func3b[s2-m2] PASSED
- test_mod2.py::test_func4[m2] PASSED
- test_mod1.py::test_func1[m1] PASSED
- test_mod1.py::test_func1[m2] PASSED
- """)
-
- def test_class_ordering(self, testdir):
- testdir.makeconftest("""
- import pytest
-
- l = []
-
- @pytest.fixture(scope="function", params=[1,2])
- def farg(request):
- return request.param
-
- @pytest.fixture(scope="class", params=list("ab"))
- def carg(request):
- return request.param
-
- @pytest.fixture(scope="function", autouse=True)
- def append(request, farg, carg):
- def fin():
- l.append("fin_%s%s" % (carg, farg))
- request.addfinalizer(fin)
- """)
- testdir.makepyfile("""
- import pytest
-
- class TestClass2:
- def test_1(self):
- pass
- def test_2(self):
- pass
- class TestClass:
- def test_3(self):
- pass
- """)
- result = testdir.runpytest("-vs")
- result.stdout.fnmatch_lines("""
- test_class_ordering.py::TestClass2::test_1[1-a] PASSED
- test_class_ordering.py::TestClass2::test_1[2-a] PASSED
- test_class_ordering.py::TestClass2::test_2[1-a] PASSED
- test_class_ordering.py::TestClass2::test_2[2-a] PASSED
- test_class_ordering.py::TestClass2::test_1[1-b] PASSED
- test_class_ordering.py::TestClass2::test_1[2-b] PASSED
- test_class_ordering.py::TestClass2::test_2[1-b] PASSED
- test_class_ordering.py::TestClass2::test_2[2-b] PASSED
- test_class_ordering.py::TestClass::test_3[1-a] PASSED
- test_class_ordering.py::TestClass::test_3[2-a] PASSED
- test_class_ordering.py::TestClass::test_3[1-b] PASSED
- test_class_ordering.py::TestClass::test_3[2-b] PASSED
- """)
-
- def test_parametrize_separated_order_higher_scope_first(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture(scope="function", params=[1, 2])
- def arg(request):
- param = request.param
- request.addfinalizer(lambda: l.append("fin:%s" % param))
- l.append("create:%s" % param)
- return request.param
-
- @pytest.fixture(scope="module", params=["mod1", "mod2"])
- def modarg(request):
- param = request.param
- request.addfinalizer(lambda: l.append("fin:%s" % param))
- l.append("create:%s" % param)
- return request.param
-
- l = []
- def test_1(arg):
- l.append("test1")
- def test_2(modarg):
- l.append("test2")
- def test_3(arg, modarg):
- l.append("test3")
- def test_4(modarg, arg):
- l.append("test4")
- """)
- reprec = testdir.inline_run("-v")
- reprec.assertoutcome(passed=12)
- l = reprec.getcalls("pytest_runtest_call")[0].item.module.l
- expected = [
- 'create:1', 'test1', 'fin:1', 'create:2', 'test1',
- 'fin:2', 'create:mod1', 'test2', 'create:1', 'test3',
- 'fin:1', 'create:2', 'test3', 'fin:2', 'create:1',
- 'test4', 'fin:1', 'create:2', 'test4', 'fin:2',
- 'fin:mod1', 'create:mod2', 'test2', 'create:1', 'test3',
- 'fin:1', 'create:2', 'test3', 'fin:2', 'create:1',
- 'test4', 'fin:1', 'create:2', 'test4', 'fin:2',
- 'fin:mod2']
- import pprint
- pprint.pprint(list(zip(l, expected)))
- assert l == expected
-
- def test_parametrized_fixture_teardown_order(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(params=[1,2], scope="class")
- def param1(request):
- return request.param
-
- l = []
-
- class TestClass:
- @classmethod
- @pytest.fixture(scope="class", autouse=True)
- def setup1(self, request, param1):
- l.append(1)
- request.addfinalizer(self.teardown1)
- @classmethod
- def teardown1(self):
- assert l.pop() == 1
- @pytest.fixture(scope="class", autouse=True)
- def setup2(self, request, param1):
- l.append(2)
- request.addfinalizer(self.teardown2)
- @classmethod
- def teardown2(self):
- assert l.pop() == 2
- def test(self):
- pass
-
- def test_finish():
- assert not l
- """)
- result = testdir.runpytest("-v")
- result.stdout.fnmatch_lines("""
- *3 passed*
- """)
- assert "error" not in result.stdout.str()
-
- def test_fixture_finalizer(self, testdir):
- testdir.makeconftest("""
- import pytest
- import sys
-
- @pytest.fixture
- def browser(request):
-
- def finalize():
- sys.stdout.write('Finalized')
- request.addfinalizer(finalize)
- return {}
- """)
- b = testdir.mkdir("subdir")
- b.join("test_overriden_fixture_finalizer.py").write(dedent("""
- import pytest
- @pytest.fixture
- def browser(browser):
- browser['visited'] = True
- return browser
-
- def test_browser(browser):
- assert browser['visited'] is True
- """))
- reprec = testdir.runpytest("-s")
- for test in ['test_browser']:
- reprec.stdout.fnmatch_lines('*Finalized*')
-
- def test_class_scope_with_normal_tests(self, testdir):
- testpath = testdir.makepyfile("""
- import pytest
-
- class Box:
- value = 0
-
- @pytest.fixture(scope='class')
- def a(request):
- Box.value += 1
- return Box.value
-
- def test_a(a):
- assert a == 1
-
- class Test1:
- def test_b(self, a):
- assert a == 2
-
- class Test2:
- def test_c(self, a):
- assert a == 3""")
- reprec = testdir.inline_run(testpath)
- for test in ['test_a', 'test_b', 'test_c']:
- assert reprec.matchreport(test).passed
-
- def test_request_is_clean(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(params=[1, 2])
- def fix(request):
- request.addfinalizer(lambda: l.append(request.param))
- def test_fix(fix):
- pass
- """)
- reprec = testdir.inline_run("-s")
- l = reprec.getcalls("pytest_runtest_call")[0].item.module.l
- assert l == [1,2]
-
- def test_parametrize_separated_lifecycle(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- l = []
- @pytest.fixture(scope="module", params=[1, 2])
- def arg(request):
- x = request.param
- request.addfinalizer(lambda: l.append("fin%s" % x))
- return request.param
- def test_1(arg):
- l.append(arg)
- def test_2(arg):
- l.append(arg)
- """)
- reprec = testdir.inline_run("-vs")
- reprec.assertoutcome(passed=4)
- l = reprec.getcalls("pytest_runtest_call")[0].item.module.l
- import pprint
- pprint.pprint(l)
- #assert len(l) == 6
- assert l[0] == l[1] == 1
- assert l[2] == "fin1"
- assert l[3] == l[4] == 2
- assert l[5] == "fin2"
-
- def test_parametrize_function_scoped_finalizers_called(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture(scope="function", params=[1, 2])
- def arg(request):
- x = request.param
- request.addfinalizer(lambda: l.append("fin%s" % x))
- return request.param
-
- l = []
- def test_1(arg):
- l.append(arg)
- def test_2(arg):
- l.append(arg)
- def test_3():
- assert len(l) == 8
- assert l == [1, "fin1", 2, "fin2", 1, "fin1", 2, "fin2"]
- """)
- reprec = testdir.inline_run("-v")
- reprec.assertoutcome(passed=5)
-
-
- @pytest.mark.issue246
- @pytest.mark.parametrize("scope", ["session", "function", "module"])
- def test_finalizer_order_on_parametrization(self, scope, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
-
- @pytest.fixture(scope=%(scope)r, params=["1"])
- def fix1(request):
- return request.param
-
- @pytest.fixture(scope=%(scope)r)
- def fix2(request, base):
- def cleanup_fix2():
- assert not l, "base should not have been finalized"
- request.addfinalizer(cleanup_fix2)
-
- @pytest.fixture(scope=%(scope)r)
- def base(request, fix1):
- def cleanup_base():
- l.append("fin_base")
- print ("finalizing base")
- request.addfinalizer(cleanup_base)
-
- def test_begin():
- pass
- def test_baz(base, fix2):
- pass
- def test_other():
- pass
- """ % {"scope": scope})
- reprec = testdir.inline_run("-lvs")
- reprec.assertoutcome(passed=3)
-
- @pytest.mark.issue396
- def test_class_scope_parametrization_ordering(self, testdir):
- testdir.makepyfile("""
- import pytest
- l = []
- @pytest.fixture(params=["John", "Doe"], scope="class")
- def human(request):
- request.addfinalizer(lambda: l.append("fin %s" % request.param))
- return request.param
-
- class TestGreetings:
- def test_hello(self, human):
- l.append("test_hello")
-
- class TestMetrics:
- def test_name(self, human):
- l.append("test_name")
-
- def test_population(self, human):
- l.append("test_population")
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=6)
- l = reprec.getcalls("pytest_runtest_call")[0].item.module.l
- assert l == ["test_hello", "fin John", "test_hello", "fin Doe",
- "test_name", "test_population", "fin John",
- "test_name", "test_population", "fin Doe"]
-
- def test_parametrize_setup_function(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture(scope="module", params=[1, 2])
- def arg(request):
- return request.param
-
- @pytest.fixture(scope="module", autouse=True)
- def mysetup(request, arg):
- request.addfinalizer(lambda: l.append("fin%s" % arg))
- l.append("setup%s" % arg)
-
- l = []
- def test_1(arg):
- l.append(arg)
- def test_2(arg):
- l.append(arg)
- def test_3():
- import pprint
- pprint.pprint(l)
- if arg == 1:
- assert l == ["setup1", 1, 1, ]
- elif arg == 2:
- assert l == ["setup1", 1, 1, "fin1",
- "setup2", 2, 2, ]
-
- """)
- reprec = testdir.inline_run("-v")
- reprec.assertoutcome(passed=6)
-
- def test_fixture_marked_function_not_collected_as_test(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture
- def test_app():
- return 1
-
- def test_something(test_app):
- assert test_app == 1
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_params_and_ids(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture(params=[object(), object()],
- ids=['alpha', 'beta'])
- def fix(request):
- return request.param
-
- def test_foo(fix):
- assert 1
- """)
- res = testdir.runpytest('-v')
- res.stdout.fnmatch_lines([
- '*test_foo*alpha*',
- '*test_foo*beta*'])
-
- def test_params_and_ids_yieldfixture(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.yield_fixture(params=[object(), object()],
- ids=['alpha', 'beta'])
- def fix(request):
- yield request.param
-
- def test_foo(fix):
- assert 1
- """)
- res = testdir.runpytest('-v')
- res.stdout.fnmatch_lines([
- '*test_foo*alpha*',
- '*test_foo*beta*'])
-
-
-class TestRequestScopeAccess:
- pytestmark = pytest.mark.parametrize(("scope", "ok", "error"),[
- ["session", "", "fspath class function module"],
- ["module", "module fspath", "cls function"],
- ["class", "module fspath cls", "function"],
- ["function", "module fspath cls function", ""]
- ])
-
- def test_setup(self, testdir, scope, ok, error):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope=%r, autouse=True)
- def myscoped(request):
- for x in %r:
- assert hasattr(request, x)
- for x in %r:
- pytest.raises(AttributeError, lambda:
- getattr(request, x))
- assert request.session
- assert request.config
- def test_func():
- pass
- """ %(scope, ok.split(), error.split()))
- reprec = testdir.inline_run("-l")
- reprec.assertoutcome(passed=1)
-
- def test_funcarg(self, testdir, scope, ok, error):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope=%r)
- def arg(request):
- for x in %r:
- assert hasattr(request, x)
- for x in %r:
- pytest.raises(AttributeError, lambda:
- getattr(request, x))
- assert request.session
- assert request.config
- def test_func(arg):
- pass
- """ %(scope, ok.split(), error.split()))
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
-class TestErrors:
- def test_subfactory_missing_funcarg(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture()
- def gen(qwe123):
- return 1
- def test_something(gen):
- pass
- """)
- result = testdir.runpytest()
- assert result.ret != 0
- result.stdout.fnmatch_lines([
- "*def gen(qwe123):*",
- "*fixture*qwe123*not found*",
- "*1 error*",
- ])
-
- def test_issue498_fixture_finalizer_failing(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture
- def fix1(request):
- def f():
- raise KeyError
- request.addfinalizer(f)
- return object()
-
- l = []
- def test_1(fix1):
- l.append(fix1)
- def test_2(fix1):
- l.append(fix1)
- def test_3():
- assert l[0] != l[1]
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines("""
- *ERROR*teardown*test_1*
- *KeyError*
- *ERROR*teardown*test_2*
- *KeyError*
- *3 pass*2 error*
- """)
-
-
-
- def test_setupfunc_missing_funcarg(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(autouse=True)
- def gen(qwe123):
- return 1
- def test_something():
- pass
- """)
- result = testdir.runpytest()
- assert result.ret != 0
- result.stdout.fnmatch_lines([
- "*def gen(qwe123):*",
- "*fixture*qwe123*not found*",
- "*1 error*",
- ])
-
-class TestShowFixtures:
- def test_funcarg_compat(self, testdir):
- config = testdir.parseconfigure("--funcargs")
- assert config.option.showfixtures
-
- def test_show_fixtures(self, testdir):
- result = testdir.runpytest("--fixtures")
- result.stdout.fnmatch_lines([
- "*tmpdir*",
- "*temporary directory*",
- ]
- )
-
- def test_show_fixtures_verbose(self, testdir):
- result = testdir.runpytest("--fixtures", "-v")
- result.stdout.fnmatch_lines([
- "*tmpdir*--*tmpdir.py*",
- "*temporary directory*",
- ]
- )
-
- def test_show_fixtures_testmodule(self, testdir):
- p = testdir.makepyfile('''
- import pytest
- @pytest.fixture
- def _arg0():
- """ hidden """
- @pytest.fixture
- def arg1():
- """ hello world """
- ''')
- result = testdir.runpytest("--fixtures", p)
- result.stdout.fnmatch_lines("""
- *tmpdir
- *fixtures defined from*
- *arg1*
- *hello world*
- """)
- assert "arg0" not in result.stdout.str()
-
- @pytest.mark.parametrize("testmod", [True, False])
- def test_show_fixtures_conftest(self, testdir, testmod):
- testdir.makeconftest('''
- import pytest
- @pytest.fixture
- def arg1():
- """ hello world """
- ''')
- if testmod:
- testdir.makepyfile("""
- def test_hello():
- pass
- """)
- result = testdir.runpytest("--fixtures")
- result.stdout.fnmatch_lines("""
- *tmpdir*
- *fixtures defined from*conftest*
- *arg1*
- *hello world*
- """)
-
- def test_show_fixtures_trimmed_doc(self, testdir):
- p = testdir.makepyfile('''
- import pytest
- @pytest.fixture
- def arg1():
- """
- line1
- line2
-
- """
- @pytest.fixture
- def arg2():
- """
- line1
- line2
-
- """
- ''')
- result = testdir.runpytest("--fixtures", p)
- result.stdout.fnmatch_lines("""
- * fixtures defined from test_show_fixtures_trimmed_doc *
- arg2
- line1
- line2
- arg1
- line1
- line2
-
- """)
-
-
- def test_show_fixtures_different_files(self, testdir):
- """
- #833: --fixtures only shows fixtures from first file
- """
- testdir.makepyfile(test_a='''
- import pytest
-
- @pytest.fixture
- def fix_a():
- """Fixture A"""
- pass
-
- def test_a(fix_a):
- pass
- ''')
- testdir.makepyfile(test_b='''
- import pytest
-
- @pytest.fixture
- def fix_b():
- """Fixture B"""
- pass
-
- def test_b(fix_b):
- pass
- ''')
- result = testdir.runpytest("--fixtures")
- result.stdout.fnmatch_lines("""
- * fixtures defined from test_a *
- fix_a
- Fixture A
-
- * fixtures defined from test_b *
- fix_b
- Fixture B
- """)
-
-
-class TestContextManagerFixtureFuncs:
- def test_simple(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.yield_fixture
- def arg1():
- print ("setup")
- yield 1
- print ("teardown")
- def test_1(arg1):
- print ("test1 %s" % arg1)
- def test_2(arg1):
- print ("test2 %s" % arg1)
- assert 0
- """)
- result = testdir.runpytest("-s")
- result.stdout.fnmatch_lines("""
- *setup*
- *test1 1*
- *teardown*
- *setup*
- *test2 1*
- *teardown*
- """)
-
- def test_scoped(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.yield_fixture(scope="module")
- def arg1():
- print ("setup")
- yield 1
- print ("teardown")
- def test_1(arg1):
- print ("test1 %s" % arg1)
- def test_2(arg1):
- print ("test2 %s" % arg1)
- """)
- result = testdir.runpytest("-s")
- result.stdout.fnmatch_lines("""
- *setup*
- *test1 1*
- *test2 1*
- *teardown*
- """)
-
- def test_setup_exception(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.yield_fixture(scope="module")
- def arg1():
- pytest.fail("setup")
- yield 1
- def test_1(arg1):
- pass
- """)
- result = testdir.runpytest("-s")
- result.stdout.fnmatch_lines("""
- *pytest.fail*setup*
- *1 error*
- """)
-
- def test_teardown_exception(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.yield_fixture(scope="module")
- def arg1():
- yield 1
- pytest.fail("teardown")
- def test_1(arg1):
- pass
- """)
- result = testdir.runpytest("-s")
- result.stdout.fnmatch_lines("""
- *pytest.fail*teardown*
- *1 passed*1 error*
- """)
-
- def test_yields_more_than_one(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.yield_fixture(scope="module")
- def arg1():
- yield 1
- yield 2
- def test_1(arg1):
- pass
- """)
- result = testdir.runpytest("-s")
- result.stdout.fnmatch_lines("""
- *fixture function*
- *test_yields*:2*
- """)
-
-
- def test_no_yield(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.yield_fixture(scope="module")
- def arg1():
- return 1
- def test_1(arg1):
- pass
- """)
- result = testdir.runpytest("-s")
- result.stdout.fnmatch_lines("""
- *yield_fixture*requires*yield*
- *yield_fixture*
- *def arg1*
- """)
-
- def test_yield_not_allowed_in_non_yield(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope="module")
- def arg1():
- yield 1
- def test_1(arg1):
- pass
- """)
- result = testdir.runpytest("-s")
- result.stdout.fnmatch_lines("""
- *fixture*cannot use*yield*
- *def arg1*
- """)
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/python/integration.py b/tests/wpt/web-platform-tests/tools/pytest/testing/python/integration.py
deleted file mode 100644
index dea86f942fd..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/python/integration.py
+++ /dev/null
@@ -1,369 +0,0 @@
-import pytest
-from _pytest import python
-from _pytest import runner
-
-
-class TestOEJSKITSpecials:
- def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage
- testdir.makeconftest("""
- import pytest
- def pytest_pycollect_makeitem(collector, name, obj):
- if name == "MyClass":
- return MyCollector(name, parent=collector)
- class MyCollector(pytest.Collector):
- def reportinfo(self):
- return self.fspath, 3, "xyz"
- """)
- modcol = testdir.getmodulecol("""
- def pytest_funcarg__arg1(request):
- return 42
- class MyClass:
- pass
- """)
- # this hook finds funcarg factories
- rep = runner.collect_one_node(collector=modcol)
- clscol = rep.result[0]
- clscol.obj = lambda arg1: None
- clscol.funcargs = {}
- pytest._fillfuncargs(clscol)
- assert clscol.funcargs['arg1'] == 42
-
- def test_autouse_fixture(self, testdir): # rough jstests usage
- testdir.makeconftest("""
- import pytest
- def pytest_pycollect_makeitem(collector, name, obj):
- if name == "MyClass":
- return MyCollector(name, parent=collector)
- class MyCollector(pytest.Collector):
- def reportinfo(self):
- return self.fspath, 3, "xyz"
- """)
- modcol = testdir.getmodulecol("""
- import pytest
- @pytest.fixture(autouse=True)
- def hello():
- pass
- def pytest_funcarg__arg1(request):
- return 42
- class MyClass:
- pass
- """)
- # this hook finds funcarg factories
- rep = runner.collect_one_node(modcol)
- clscol = rep.result[0]
- clscol.obj = lambda: None
- clscol.funcargs = {}
- pytest._fillfuncargs(clscol)
- assert not clscol.funcargs
-
-
-def test_wrapped_getfslineno():
- def func():
- pass
- def wrap(f):
- func.__wrapped__ = f
- func.patchings = ["qwe"]
- return func
- @wrap
- def wrapped_func(x, y, z):
- pass
- fs, lineno = python.getfslineno(wrapped_func)
- fs2, lineno2 = python.getfslineno(wrap)
- assert lineno > lineno2, "getfslineno does not unwrap correctly"
-
-class TestMockDecoration:
- def test_wrapped_getfuncargnames(self):
- from _pytest.python import getfuncargnames
- def wrap(f):
- def func():
- pass
- func.__wrapped__ = f
- return func
- @wrap
- def f(x):
- pass
- l = getfuncargnames(f)
- assert l == ("x",)
-
- def test_wrapped_getfuncargnames_patching(self):
- from _pytest.python import getfuncargnames
- def wrap(f):
- def func():
- pass
- func.__wrapped__ = f
- func.patchings = ["qwe"]
- return func
- @wrap
- def f(x, y, z):
- pass
- l = getfuncargnames(f)
- assert l == ("y", "z")
-
- def test_unittest_mock(self, testdir):
- pytest.importorskip("unittest.mock")
- testdir.makepyfile("""
- import unittest.mock
- class T(unittest.TestCase):
- @unittest.mock.patch("os.path.abspath")
- def test_hello(self, abspath):
- import os
- os.path.abspath("hello")
- abspath.assert_any_call("hello")
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_unittest_mock_and_fixture(self, testdir):
- pytest.importorskip("unittest.mock")
- testdir.makepyfile("""
- import os.path
- import unittest.mock
- import pytest
-
- @pytest.fixture
- def inject_me():
- pass
-
- @unittest.mock.patch.object(os.path, "abspath",
- new=unittest.mock.MagicMock)
- def test_hello(inject_me):
- import os
- os.path.abspath("hello")
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_mock(self, testdir):
- pytest.importorskip("mock", "1.0.1")
- testdir.makepyfile("""
- import os
- import unittest
- import mock
-
- class T(unittest.TestCase):
- @mock.patch("os.path.abspath")
- def test_hello(self, abspath):
- os.path.abspath("hello")
- abspath.assert_any_call("hello")
- def mock_basename(path):
- return "mock_basename"
- @mock.patch("os.path.abspath")
- @mock.patch("os.path.normpath")
- @mock.patch("os.path.basename", new=mock_basename)
- def test_someting(normpath, abspath, tmpdir):
- abspath.return_value = "this"
- os.path.normpath(os.path.abspath("hello"))
- normpath.assert_any_call("this")
- assert os.path.basename("123") == "mock_basename"
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
- calls = reprec.getcalls("pytest_runtest_logreport")
- funcnames = [call.report.location[2] for call in calls
- if call.report.when == "call"]
- assert funcnames == ["T.test_hello", "test_someting"]
-
- def test_mock_sorting(self, testdir):
- pytest.importorskip("mock", "1.0.1")
- testdir.makepyfile("""
- import os
- import mock
-
- @mock.patch("os.path.abspath")
- def test_one(abspath):
- pass
- @mock.patch("os.path.abspath")
- def test_two(abspath):
- pass
- @mock.patch("os.path.abspath")
- def test_three(abspath):
- pass
- """)
- reprec = testdir.inline_run()
- calls = reprec.getreports("pytest_runtest_logreport")
- calls = [x for x in calls if x.when == "call"]
- names = [x.nodeid.split("::")[-1] for x in calls]
- assert names == ["test_one", "test_two", "test_three"]
-
- def test_mock_double_patch_issue473(self, testdir):
- pytest.importorskip("mock", "1.0.1")
- testdir.makepyfile("""
- from mock import patch
- from pytest import mark
-
- @patch('os.getcwd')
- @patch('os.path')
- @mark.slow
- class TestSimple:
- def test_simple_thing(self, mock_path, mock_getcwd):
- pass
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
-
-class TestReRunTests:
- def test_rerun(self, testdir):
- testdir.makeconftest("""
- from _pytest.runner import runtestprotocol
- def pytest_runtest_protocol(item, nextitem):
- runtestprotocol(item, log=False, nextitem=nextitem)
- runtestprotocol(item, log=True, nextitem=nextitem)
- """)
- testdir.makepyfile("""
- import pytest
- count = 0
- req = None
- @pytest.fixture
- def fix(request):
- global count, req
- assert request != req
- req = request
- print ("fix count %s" % count)
- count += 1
- def test_fix(fix):
- pass
- """)
- result = testdir.runpytest("-s")
- result.stdout.fnmatch_lines("""
- *fix count 0*
- *fix count 1*
- """)
- result.stdout.fnmatch_lines("""
- *2 passed*
- """)
-
-def test_pytestconfig_is_session_scoped():
- from _pytest.python import pytestconfig
- assert pytestconfig._pytestfixturefunction.scope == "session"
-
-
-class TestNoselikeTestAttribute:
- def test_module_with_global_test(self, testdir):
- testdir.makepyfile("""
- __test__ = False
- def test_hello():
- pass
- """)
- reprec = testdir.inline_run()
- assert not reprec.getfailedcollections()
- calls = reprec.getreports("pytest_runtest_logreport")
- assert not calls
-
- def test_class_and_method(self, testdir):
- testdir.makepyfile("""
- __test__ = True
- def test_func():
- pass
- test_func.__test__ = False
-
- class TestSome:
- __test__ = False
- def test_method(self):
- pass
- """)
- reprec = testdir.inline_run()
- assert not reprec.getfailedcollections()
- calls = reprec.getreports("pytest_runtest_logreport")
- assert not calls
-
- def test_unittest_class(self, testdir):
- testdir.makepyfile("""
- import unittest
- class TC(unittest.TestCase):
- def test_1(self):
- pass
- class TC2(unittest.TestCase):
- __test__ = False
- def test_2(self):
- pass
- """)
- reprec = testdir.inline_run()
- assert not reprec.getfailedcollections()
- call = reprec.getcalls("pytest_collection_modifyitems")[0]
- assert len(call.items) == 1
- assert call.items[0].cls.__name__ == "TC"
-
- def test_class_with_nasty_getattr(self, testdir):
- """Make sure we handle classes with a custom nasty __getattr__ right.
-
- With a custom __getattr__ which e.g. returns a function (like with a
- RPC wrapper), we shouldn't assume this meant "__test__ = True".
- """
- # https://github.com/pytest-dev/pytest/issues/1204
- testdir.makepyfile("""
- class MetaModel(type):
-
- def __getattr__(cls, key):
- return lambda: None
-
-
- BaseModel = MetaModel('Model', (), {})
-
-
- class Model(BaseModel):
-
- __metaclass__ = MetaModel
-
- def test_blah(self):
- pass
- """)
- reprec = testdir.inline_run()
- assert not reprec.getfailedcollections()
- call = reprec.getcalls("pytest_collection_modifyitems")[0]
- assert not call.items
-
-
-@pytest.mark.issue351
-class TestParameterize:
-
- def test_idfn_marker(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- def idfn(param):
- if param == 0:
- return 'spam'
- elif param == 1:
- return 'ham'
- else:
- return None
-
- @pytest.mark.parametrize('a,b', [(0, 2), (1, 2)], ids=idfn)
- def test_params(a, b):
- pass
- """)
- res = testdir.runpytest('--collect-only')
- res.stdout.fnmatch_lines([
- "*spam-2*",
- "*ham-2*",
- ])
-
- def test_idfn_fixture(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- def idfn(param):
- if param == 0:
- return 'spam'
- elif param == 1:
- return 'ham'
- else:
- return None
-
- @pytest.fixture(params=[0, 1], ids=idfn)
- def a(request):
- return request.param
-
- @pytest.fixture(params=[1, 2], ids=idfn)
- def b(request):
- return request.param
-
- def test_params(a, b):
- pass
- """)
- res = testdir.runpytest('--collect-only')
- res.stdout.fnmatch_lines([
- "*spam-2*",
- "*ham-2*",
- ])
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/python/metafunc.py b/tests/wpt/web-platform-tests/tools/pytest/testing/python/metafunc.py
deleted file mode 100644
index faa687f4056..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/python/metafunc.py
+++ /dev/null
@@ -1,1094 +0,0 @@
-# -*- coding: utf-8 -*-
-import re
-
-import _pytest._code
-import py
-import pytest
-from _pytest import python as funcargs
-
-class TestMetafunc:
- def Metafunc(self, func):
- # the unit tests of this class check if things work correctly
- # on the funcarg level, so we don't need a full blown
- # initiliazation
- class FixtureInfo:
- name2fixturedefs = None
- def __init__(self, names):
- self.names_closure = names
- names = funcargs.getfuncargnames(func)
- fixtureinfo = FixtureInfo(names)
- return funcargs.Metafunc(func, fixtureinfo, None)
-
- def test_no_funcargs(self, testdir):
- def function(): pass
- metafunc = self.Metafunc(function)
- assert not metafunc.fixturenames
- repr(metafunc._calls)
-
- def test_function_basic(self):
- def func(arg1, arg2="qwe"): pass
- metafunc = self.Metafunc(func)
- assert len(metafunc.fixturenames) == 1
- assert 'arg1' in metafunc.fixturenames
- assert metafunc.function is func
- assert metafunc.cls is None
-
- def test_addcall_no_args(self):
- def func(arg1): pass
- metafunc = self.Metafunc(func)
- metafunc.addcall()
- assert len(metafunc._calls) == 1
- call = metafunc._calls[0]
- assert call.id == "0"
- assert not hasattr(call, 'param')
-
- def test_addcall_id(self):
- def func(arg1): pass
- metafunc = self.Metafunc(func)
- pytest.raises(ValueError, "metafunc.addcall(id=None)")
-
- metafunc.addcall(id=1)
- pytest.raises(ValueError, "metafunc.addcall(id=1)")
- pytest.raises(ValueError, "metafunc.addcall(id='1')")
- metafunc.addcall(id=2)
- assert len(metafunc._calls) == 2
- assert metafunc._calls[0].id == "1"
- assert metafunc._calls[1].id == "2"
-
- def test_addcall_param(self):
- def func(arg1): pass
- metafunc = self.Metafunc(func)
- class obj: pass
- metafunc.addcall(param=obj)
- metafunc.addcall(param=obj)
- metafunc.addcall(param=1)
- assert len(metafunc._calls) == 3
- assert metafunc._calls[0].getparam("arg1") == obj
- assert metafunc._calls[1].getparam("arg1") == obj
- assert metafunc._calls[2].getparam("arg1") == 1
-
- def test_addcall_funcargs(self):
- def func(x): pass
- metafunc = self.Metafunc(func)
- class obj: pass
- metafunc.addcall(funcargs={"x": 2})
- metafunc.addcall(funcargs={"x": 3})
- pytest.raises(pytest.fail.Exception, "metafunc.addcall({'xyz': 0})")
- assert len(metafunc._calls) == 2
- assert metafunc._calls[0].funcargs == {'x': 2}
- assert metafunc._calls[1].funcargs == {'x': 3}
- assert not hasattr(metafunc._calls[1], 'param')
-
- def test_parametrize_error(self):
- def func(x, y): pass
- metafunc = self.Metafunc(func)
- metafunc.parametrize("x", [1,2])
- pytest.raises(ValueError, lambda: metafunc.parametrize("x", [5,6]))
- pytest.raises(ValueError, lambda: metafunc.parametrize("x", [5,6]))
- metafunc.parametrize("y", [1,2])
- pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5,6]))
- pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5,6]))
-
- def test_parametrize_and_id(self):
- def func(x, y): pass
- metafunc = self.Metafunc(func)
-
- metafunc.parametrize("x", [1,2], ids=['basic', 'advanced'])
- metafunc.parametrize("y", ["abc", "def"])
- ids = [x.id for x in metafunc._calls]
- assert ids == ["basic-abc", "basic-def", "advanced-abc", "advanced-def"]
-
- def test_parametrize_with_wrong_number_of_ids(self, testdir):
- def func(x, y): pass
- metafunc = self.Metafunc(func)
-
- pytest.raises(ValueError, lambda:
- metafunc.parametrize("x", [1,2], ids=['basic']))
-
- pytest.raises(ValueError, lambda:
- metafunc.parametrize(("x","y"), [("abc", "def"),
- ("ghi", "jkl")], ids=["one"]))
-
- def test_parametrize_with_userobjects(self):
- def func(x, y): pass
- metafunc = self.Metafunc(func)
- class A:
- pass
- metafunc.parametrize("x", [A(), A()])
- metafunc.parametrize("y", list("ab"))
- assert metafunc._calls[0].id == "x0-a"
- assert metafunc._calls[1].id == "x0-b"
- assert metafunc._calls[2].id == "x1-a"
- assert metafunc._calls[3].id == "x1-b"
-
- @pytest.mark.skipif('sys.version_info[0] >= 3')
- def test_unicode_idval_python2(self):
- """unittest for the expected behavior to obtain ids for parametrized
- unicode values in Python 2: if convertible to ascii, they should appear
- as ascii values, otherwise fallback to hide the value behind the name
- of the parametrized variable name. #1086
- """
- from _pytest.python import _idval
- values = [
- (u'', ''),
- (u'ascii', 'ascii'),
- (u'ação', 'a6'),
- (u'josé@blah.com', 'a6'),
- (u'δοκ.ιμή@παράδειγμα.δοκιμή', 'a6'),
- ]
- for val, expected in values:
- assert _idval(val, 'a', 6, None) == expected
-
- def test_bytes_idval(self):
- """unittest for the expected behavior to obtain ids for parametrized
- bytes values:
- - python2: non-ascii strings are considered bytes and formatted using
- "binary escape", where any byte < 127 is escaped into its hex form.
- - python3: bytes objects are always escaped using "binary escape".
- """
- from _pytest.python import _idval
- values = [
- (b'', ''),
- (b'\xc3\xb4\xff\xe4', '\\xc3\\xb4\\xff\\xe4'),
- (b'ascii', 'ascii'),
- (u'αρά'.encode('utf-8'), '\\xce\\xb1\\xcf\\x81\\xce\\xac'),
- ]
- for val, expected in values:
- assert _idval(val, 'a', 6, None) == expected
-
- @pytest.mark.issue250
- def test_idmaker_autoname(self):
- from _pytest.python import idmaker
- result = idmaker(("a", "b"), [("string", 1.0),
- ("st-ring", 2.0)])
- assert result == ["string-1.0", "st-ring-2.0"]
-
- result = idmaker(("a", "b"), [(object(), 1.0),
- (object(), object())])
- assert result == ["a0-1.0", "a1-b1"]
- # unicode mixing, issue250
- result = idmaker((py.builtin._totext("a"), "b"), [({}, b'\xc3\xb4')])
- assert result == ['a0-\\xc3\\xb4']
-
- def test_idmaker_with_bytes_regex(self):
- from _pytest.python import idmaker
- result = idmaker(("a"), [(re.compile(b'foo'), 1.0)])
- assert result == ["foo"]
-
- def test_idmaker_native_strings(self):
- from _pytest.python import idmaker
- totext = py.builtin._totext
- result = idmaker(("a", "b"), [(1.0, -1.1),
- (2, -202),
- ("three", "three hundred"),
- (True, False),
- (None, None),
- (re.compile('foo'), re.compile('bar')),
- (str, int),
- (list("six"), [66, 66]),
- (set([7]), set("seven")),
- (tuple("eight"), (8, -8, 8)),
- (b'\xc3\xb4', b"name"),
- (b'\xc3\xb4', totext("other")),
- ])
- assert result == ["1.0--1.1",
- "2--202",
- "three-three hundred",
- "True-False",
- "None-None",
- "foo-bar",
- "str-int",
- "a7-b7",
- "a8-b8",
- "a9-b9",
- "\\xc3\\xb4-name",
- "\\xc3\\xb4-other",
- ]
-
- def test_idmaker_enum(self):
- from _pytest.python import idmaker
- enum = pytest.importorskip("enum")
- e = enum.Enum("Foo", "one, two")
- result = idmaker(("a", "b"), [(e.one, e.two)])
- assert result == ["Foo.one-Foo.two"]
-
- @pytest.mark.issue351
- def test_idmaker_idfn(self):
- from _pytest.python import idmaker
- def ids(val):
- if isinstance(val, Exception):
- return repr(val)
-
- result = idmaker(("a", "b"), [(10.0, IndexError()),
- (20, KeyError()),
- ("three", [1, 2, 3]),
- ], idfn=ids)
- assert result == ["10.0-IndexError()",
- "20-KeyError()",
- "three-b2",
- ]
-
- @pytest.mark.issue351
- def test_idmaker_idfn_unique_names(self):
- from _pytest.python import idmaker
- def ids(val):
- return 'a'
-
- result = idmaker(("a", "b"), [(10.0, IndexError()),
- (20, KeyError()),
- ("three", [1, 2, 3]),
- ], idfn=ids)
- assert result == ["0a-a",
- "1a-a",
- "2a-a",
- ]
-
- @pytest.mark.issue351
- def test_idmaker_idfn_exception(self):
- from _pytest.python import idmaker
- def ids(val):
- raise Exception("bad code")
-
- result = idmaker(("a", "b"), [(10.0, IndexError()),
- (20, KeyError()),
- ("three", [1, 2, 3]),
- ], idfn=ids)
- assert result == ["10.0-b0",
- "20-b1",
- "three-b2",
- ]
-
- def test_addcall_and_parametrize(self):
- def func(x, y): pass
- metafunc = self.Metafunc(func)
- metafunc.addcall({'x': 1})
- metafunc.parametrize('y', [2,3])
- assert len(metafunc._calls) == 2
- assert metafunc._calls[0].funcargs == {'x': 1, 'y': 2}
- assert metafunc._calls[1].funcargs == {'x': 1, 'y': 3}
- assert metafunc._calls[0].id == "0-2"
- assert metafunc._calls[1].id == "0-3"
-
- @pytest.mark.issue714
- def test_parametrize_indirect(self):
- def func(x, y): pass
- metafunc = self.Metafunc(func)
- metafunc.parametrize('x', [1], indirect=True)
- metafunc.parametrize('y', [2,3], indirect=True)
- assert len(metafunc._calls) == 2
- assert metafunc._calls[0].funcargs == {}
- assert metafunc._calls[1].funcargs == {}
- assert metafunc._calls[0].params == dict(x=1,y=2)
- assert metafunc._calls[1].params == dict(x=1,y=3)
-
- @pytest.mark.issue714
- def test_parametrize_indirect_list(self):
- def func(x, y): pass
- metafunc = self.Metafunc(func)
- metafunc.parametrize('x, y', [('a', 'b')], indirect=['x'])
- assert metafunc._calls[0].funcargs == dict(y='b')
- assert metafunc._calls[0].params == dict(x='a')
-
- @pytest.mark.issue714
- def test_parametrize_indirect_list_all(self):
- def func(x, y): pass
- metafunc = self.Metafunc(func)
- metafunc.parametrize('x, y', [('a', 'b')], indirect=['x', 'y'])
- assert metafunc._calls[0].funcargs == {}
- assert metafunc._calls[0].params == dict(x='a', y='b')
-
- @pytest.mark.issue714
- def test_parametrize_indirect_list_empty(self):
- def func(x, y): pass
- metafunc = self.Metafunc(func)
- metafunc.parametrize('x, y', [('a', 'b')], indirect=[])
- assert metafunc._calls[0].funcargs == dict(x='a', y='b')
- assert metafunc._calls[0].params == {}
-
- @pytest.mark.issue714
- def test_parametrize_indirect_list_functional(self, testdir):
- """
- Test parametrization with 'indirect' parameter applied on
- particular arguments. As y is is direct, its value should
- be used directly rather than being passed to the fixture
- y.
-
- :param testdir: the instance of Testdir class, a temporary
- test directory.
- """
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope='function')
- def x(request):
- return request.param * 3
- @pytest.fixture(scope='function')
- def y(request):
- return request.param * 2
- @pytest.mark.parametrize('x, y', [('a', 'b')], indirect=['x'])
- def test_simple(x,y):
- assert len(x) == 3
- assert len(y) == 1
- """)
- result = testdir.runpytest("-v")
- result.stdout.fnmatch_lines([
- "*test_simple*a-b*",
- "*1 passed*",
- ])
-
- @pytest.mark.issue714
- def test_parametrize_indirect_list_error(self, testdir):
- def func(x, y): pass
- metafunc = self.Metafunc(func)
- with pytest.raises(ValueError):
- metafunc.parametrize('x, y', [('a', 'b')], indirect=['x', 'z'])
-
- @pytest.mark.issue714
- def test_parametrize_uses_no_fixture_error_indirect_false(self, testdir):
- """The 'uses no fixture' error tells the user at collection time
- that the parametrize data they've set up doesn't correspond to the
- fixtures in their test function, rather than silently ignoring this
- and letting the test potentially pass.
- """
- testdir.makepyfile("""
- import pytest
-
- @pytest.mark.parametrize('x, y', [('a', 'b')], indirect=False)
- def test_simple(x):
- assert len(x) == 3
- """)
- result = testdir.runpytest("--collect-only")
- result.stdout.fnmatch_lines([
- "*uses no fixture 'y'*",
- ])
-
- @pytest.mark.issue714
- def test_parametrize_uses_no_fixture_error_indirect_true(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope='function')
- def x(request):
- return request.param * 3
- @pytest.fixture(scope='function')
- def y(request):
- return request.param * 2
-
- @pytest.mark.parametrize('x, y', [('a', 'b')], indirect=True)
- def test_simple(x):
- assert len(x) == 3
- """)
- result = testdir.runpytest("--collect-only")
- result.stdout.fnmatch_lines([
- "*uses no fixture 'y'*",
- ])
-
- @pytest.mark.issue714
- def test_parametrize_indirect_uses_no_fixture_error_indirect_list(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope='function')
- def x(request):
- return request.param * 3
-
- @pytest.mark.parametrize('x, y', [('a', 'b')], indirect=['x'])
- def test_simple(x):
- assert len(x) == 3
- """)
- result = testdir.runpytest("--collect-only")
- result.stdout.fnmatch_lines([
- "*uses no fixture 'y'*",
- ])
-
- def test_addcalls_and_parametrize_indirect(self):
- def func(x, y): pass
- metafunc = self.Metafunc(func)
- metafunc.addcall(param="123")
- metafunc.parametrize('x', [1], indirect=True)
- metafunc.parametrize('y', [2,3], indirect=True)
- assert len(metafunc._calls) == 2
- assert metafunc._calls[0].funcargs == {}
- assert metafunc._calls[1].funcargs == {}
- assert metafunc._calls[0].params == dict(x=1,y=2)
- assert metafunc._calls[1].params == dict(x=1,y=3)
-
- def test_parametrize_functional(self, testdir):
- testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- metafunc.parametrize('x', [1,2], indirect=True)
- metafunc.parametrize('y', [2])
- def pytest_funcarg__x(request):
- return request.param * 10
- #def pytest_funcarg__y(request):
- # return request.param
-
- def test_simple(x,y):
- assert x in (10,20)
- assert y == 2
- """)
- result = testdir.runpytest("-v")
- result.stdout.fnmatch_lines([
- "*test_simple*1-2*",
- "*test_simple*2-2*",
- "*2 passed*",
- ])
-
- def test_parametrize_onearg(self):
- metafunc = self.Metafunc(lambda x: None)
- metafunc.parametrize("x", [1,2])
- assert len(metafunc._calls) == 2
- assert metafunc._calls[0].funcargs == dict(x=1)
- assert metafunc._calls[0].id == "1"
- assert metafunc._calls[1].funcargs == dict(x=2)
- assert metafunc._calls[1].id == "2"
-
- def test_parametrize_onearg_indirect(self):
- metafunc = self.Metafunc(lambda x: None)
- metafunc.parametrize("x", [1,2], indirect=True)
- assert metafunc._calls[0].params == dict(x=1)
- assert metafunc._calls[0].id == "1"
- assert metafunc._calls[1].params == dict(x=2)
- assert metafunc._calls[1].id == "2"
-
- def test_parametrize_twoargs(self):
- metafunc = self.Metafunc(lambda x,y: None)
- metafunc.parametrize(("x", "y"), [(1,2), (3,4)])
- assert len(metafunc._calls) == 2
- assert metafunc._calls[0].funcargs == dict(x=1, y=2)
- assert metafunc._calls[0].id == "1-2"
- assert metafunc._calls[1].funcargs == dict(x=3, y=4)
- assert metafunc._calls[1].id == "3-4"
-
- def test_parametrize_multiple_times(self, testdir):
- testdir.makepyfile("""
- import pytest
- pytestmark = pytest.mark.parametrize("x", [1,2])
- def test_func(x):
- assert 0, x
- class TestClass:
- pytestmark = pytest.mark.parametrize("y", [3,4])
- def test_meth(self, x, y):
- assert 0, x
- """)
- result = testdir.runpytest()
- assert result.ret == 1
- result.assert_outcomes(failed=6)
-
- def test_parametrize_CSV(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.parametrize("x, y,", [(1,2), (2,3)])
- def test_func(x, y):
- assert x+1 == y
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
- def test_parametrize_class_scenarios(self, testdir):
- testdir.makepyfile("""
- # same as doc/en/example/parametrize scenario example
- def pytest_generate_tests(metafunc):
- idlist = []
- argvalues = []
- for scenario in metafunc.cls.scenarios:
- idlist.append(scenario[0])
- items = scenario[1].items()
- argnames = [x[0] for x in items]
- argvalues.append(([x[1] for x in items]))
- metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class")
-
- class Test(object):
- scenarios = [['1', {'arg': {1: 2}, "arg2": "value2"}],
- ['2', {'arg':'value2', "arg2": "value2"}]]
-
- def test_1(self, arg, arg2):
- pass
-
- def test_2(self, arg2, arg):
- pass
-
- def test_3(self, arg, arg2):
- pass
- """)
- result = testdir.runpytest("-v")
- assert result.ret == 0
- result.stdout.fnmatch_lines("""
- *test_1*1*
- *test_2*1*
- *test_3*1*
- *test_1*2*
- *test_2*2*
- *test_3*2*
- *6 passed*
- """)
-
- def test_format_args(self):
- def function1(): pass
- assert funcargs._format_args(function1) == '()'
-
- def function2(arg1): pass
- assert funcargs._format_args(function2) == "(arg1)"
-
- def function3(arg1, arg2="qwe"): pass
- assert funcargs._format_args(function3) == "(arg1, arg2='qwe')"
-
- def function4(arg1, *args, **kwargs): pass
- assert funcargs._format_args(function4) == "(arg1, *args, **kwargs)"
-
-
-class TestMetafuncFunctional:
- def test_attributes(self, testdir):
- p = testdir.makepyfile("""
- # assumes that generate/provide runs in the same process
- import py, pytest
- def pytest_generate_tests(metafunc):
- metafunc.addcall(param=metafunc)
-
- def pytest_funcarg__metafunc(request):
- assert request._pyfuncitem._genid == "0"
- return request.param
-
- def test_function(metafunc, pytestconfig):
- assert metafunc.config == pytestconfig
- assert metafunc.module.__name__ == __name__
- assert metafunc.function == test_function
- assert metafunc.cls is None
-
- class TestClass:
- def test_method(self, metafunc, pytestconfig):
- assert metafunc.config == pytestconfig
- assert metafunc.module.__name__ == __name__
- if py.std.sys.version_info > (3, 0):
- unbound = TestClass.test_method
- else:
- unbound = TestClass.test_method.im_func
- # XXX actually have an unbound test function here?
- assert metafunc.function == unbound
- assert metafunc.cls == TestClass
- """)
- result = testdir.runpytest(p, "-v")
- result.assert_outcomes(passed=2)
-
- def test_addcall_with_two_funcargs_generators(self, testdir):
- testdir.makeconftest("""
- def pytest_generate_tests(metafunc):
- assert "arg1" in metafunc.fixturenames
- metafunc.addcall(funcargs=dict(arg1=1, arg2=2))
- """)
- p = testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- metafunc.addcall(funcargs=dict(arg1=1, arg2=1))
-
- class TestClass:
- def test_myfunc(self, arg1, arg2):
- assert arg1 == arg2
- """)
- result = testdir.runpytest("-v", p)
- result.stdout.fnmatch_lines([
- "*test_myfunc*0*PASS*",
- "*test_myfunc*1*FAIL*",
- "*1 failed, 1 passed*"
- ])
-
- def test_two_functions(self, testdir):
- p = testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- metafunc.addcall(param=10)
- metafunc.addcall(param=20)
-
- def pytest_funcarg__arg1(request):
- return request.param
-
- def test_func1(arg1):
- assert arg1 == 10
- def test_func2(arg1):
- assert arg1 in (10, 20)
- """)
- result = testdir.runpytest("-v", p)
- result.stdout.fnmatch_lines([
- "*test_func1*0*PASS*",
- "*test_func1*1*FAIL*",
- "*test_func2*PASS*",
- "*1 failed, 3 passed*"
- ])
-
- def test_noself_in_method(self, testdir):
- p = testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- assert 'xyz' not in metafunc.fixturenames
-
- class TestHello:
- def test_hello(xyz):
- pass
- """)
- result = testdir.runpytest(p)
- result.assert_outcomes(passed=1)
-
-
- def test_generate_plugin_and_module(self, testdir):
- testdir.makeconftest("""
- def pytest_generate_tests(metafunc):
- assert "arg1" in metafunc.fixturenames
- metafunc.addcall(id="world", param=(2,100))
- """)
- p = testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- metafunc.addcall(param=(1,1), id="hello")
-
- def pytest_funcarg__arg1(request):
- return request.param[0]
- def pytest_funcarg__arg2(request):
- return request.param[1]
-
- class TestClass:
- def test_myfunc(self, arg1, arg2):
- assert arg1 == arg2
- """)
- result = testdir.runpytest("-v", p)
- result.stdout.fnmatch_lines([
- "*test_myfunc*hello*PASS*",
- "*test_myfunc*world*FAIL*",
- "*1 failed, 1 passed*"
- ])
-
- def test_generate_tests_in_class(self, testdir):
- p = testdir.makepyfile("""
- class TestClass:
- def pytest_generate_tests(self, metafunc):
- metafunc.addcall(funcargs={'hello': 'world'}, id="hello")
-
- def test_myfunc(self, hello):
- assert hello == "world"
- """)
- result = testdir.runpytest("-v", p)
- result.stdout.fnmatch_lines([
- "*test_myfunc*hello*PASS*",
- "*1 passed*"
- ])
-
- def test_two_functions_not_same_instance(self, testdir):
- p = testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- metafunc.addcall({'arg1': 10})
- metafunc.addcall({'arg1': 20})
-
- class TestClass:
- def test_func(self, arg1):
- assert not hasattr(self, 'x')
- self.x = 1
- """)
- result = testdir.runpytest("-v", p)
- result.stdout.fnmatch_lines([
- "*test_func*0*PASS*",
- "*test_func*1*PASS*",
- "*2 pass*",
- ])
-
- def test_issue28_setup_method_in_generate_tests(self, testdir):
- p = testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- metafunc.addcall({'arg1': 1})
-
- class TestClass:
- def test_method(self, arg1):
- assert arg1 == self.val
- def setup_method(self, func):
- self.val = 1
- """)
- result = testdir.runpytest(p)
- result.assert_outcomes(passed=1)
-
- def test_parametrize_functional2(self, testdir):
- testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- metafunc.parametrize("arg1", [1,2])
- metafunc.parametrize("arg2", [4,5])
- def test_hello(arg1, arg2):
- assert 0, (arg1, arg2)
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*(1, 4)*",
- "*(1, 5)*",
- "*(2, 4)*",
- "*(2, 5)*",
- "*4 failed*",
- ])
-
- def test_parametrize_and_inner_getfuncargvalue(self, testdir):
- p = testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- metafunc.parametrize("arg1", [1], indirect=True)
- metafunc.parametrize("arg2", [10], indirect=True)
-
- def pytest_funcarg__arg1(request):
- x = request.getfuncargvalue("arg2")
- return x + request.param
-
- def pytest_funcarg__arg2(request):
- return request.param
-
- def test_func1(arg1, arg2):
- assert arg1 == 11
- """)
- result = testdir.runpytest("-v", p)
- result.stdout.fnmatch_lines([
- "*test_func1*1*PASS*",
- "*1 passed*"
- ])
-
- def test_parametrize_on_setup_arg(self, testdir):
- p = testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- assert "arg1" in metafunc.fixturenames
- metafunc.parametrize("arg1", [1], indirect=True)
-
- def pytest_funcarg__arg1(request):
- return request.param
-
- def pytest_funcarg__arg2(request, arg1):
- return 10 * arg1
-
- def test_func(arg2):
- assert arg2 == 10
- """)
- result = testdir.runpytest("-v", p)
- result.stdout.fnmatch_lines([
- "*test_func*1*PASS*",
- "*1 passed*"
- ])
-
- def test_parametrize_with_ids(self, testdir):
- testdir.makepyfile("""
- import pytest
- def pytest_generate_tests(metafunc):
- metafunc.parametrize(("a", "b"), [(1,1), (1,2)],
- ids=["basic", "advanced"])
-
- def test_function(a, b):
- assert a == b
- """)
- result = testdir.runpytest("-v")
- assert result.ret == 1
- result.stdout.fnmatch_lines_random([
- "*test_function*basic*PASSED",
- "*test_function*advanced*FAILED",
- ])
-
- def test_parametrize_without_ids(self, testdir):
- testdir.makepyfile("""
- import pytest
- def pytest_generate_tests(metafunc):
- metafunc.parametrize(("a", "b"),
- [(1,object()), (1.3,object())])
-
- def test_function(a, b):
- assert 1
- """)
- result = testdir.runpytest("-v")
- result.stdout.fnmatch_lines("""
- *test_function*1-b0*
- *test_function*1.3-b1*
- """)
-
- @pytest.mark.parametrize(("scope", "length"),
- [("module", 2), ("function", 4)])
- def test_parametrize_scope_overrides(self, testdir, scope, length):
- testdir.makepyfile("""
- import pytest
- l = []
- def pytest_generate_tests(metafunc):
- if "arg" in metafunc.funcargnames:
- metafunc.parametrize("arg", [1,2], indirect=True,
- scope=%r)
- def pytest_funcarg__arg(request):
- l.append(request.param)
- return request.param
- def test_hello(arg):
- assert arg in (1,2)
- def test_world(arg):
- assert arg in (1,2)
- def test_checklength():
- assert len(l) == %d
- """ % (scope, length))
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=5)
-
- def test_parametrize_issue323(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture(scope='module', params=range(966))
- def foo(request):
- return request.param
-
- def test_it(foo):
- pass
- def test_it2(foo):
- pass
- """)
- reprec = testdir.inline_run("--collect-only")
- assert not reprec.getcalls("pytest_internalerror")
-
- def test_usefixtures_seen_in_generate_tests(self, testdir):
- testdir.makepyfile("""
- import pytest
- def pytest_generate_tests(metafunc):
- assert "abc" in metafunc.fixturenames
- metafunc.parametrize("abc", [1])
-
- @pytest.mark.usefixtures("abc")
- def test_function():
- pass
- """)
- reprec = testdir.runpytest()
- reprec.assert_outcomes(passed=1)
-
- def test_generate_tests_only_done_in_subdir(self, testdir):
- sub1 = testdir.mkpydir("sub1")
- sub2 = testdir.mkpydir("sub2")
- sub1.join("conftest.py").write(_pytest._code.Source("""
- def pytest_generate_tests(metafunc):
- assert metafunc.function.__name__ == "test_1"
- """))
- sub2.join("conftest.py").write(_pytest._code.Source("""
- def pytest_generate_tests(metafunc):
- assert metafunc.function.__name__ == "test_2"
- """))
- sub1.join("test_in_sub1.py").write("def test_1(): pass")
- sub2.join("test_in_sub2.py").write("def test_2(): pass")
- result = testdir.runpytest("-v", "-s", sub1, sub2, sub1)
- result.assert_outcomes(passed=3)
-
- def test_generate_same_function_names_issue403(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- def make_tests():
- @pytest.mark.parametrize("x", range(2))
- def test_foo(x):
- pass
- return test_foo
-
- test_x = make_tests()
- test_y = make_tests()
- """)
- reprec = testdir.runpytest()
- reprec.assert_outcomes(passed=4)
-
- @pytest.mark.issue463
- @pytest.mark.parametrize('attr', ['parametrise', 'parameterize',
- 'parameterise'])
- def test_parametrize_misspelling(self, testdir, attr):
- testdir.makepyfile("""
- import pytest
-
- @pytest.mark.{0}("x", range(2))
- def test_foo(x):
- pass
- """.format(attr))
- reprec = testdir.inline_run('--collectonly')
- failures = reprec.getfailures()
- assert len(failures) == 1
- expectederror = "MarkerError: test_foo has '{0}', spelling should be 'parametrize'".format(attr)
- assert expectederror in failures[0].longrepr.reprcrash.message
-
-
-class TestMarkersWithParametrization:
- pytestmark = pytest.mark.issue308
- def test_simple_mark(self, testdir):
- s = """
- import pytest
-
- @pytest.mark.foo
- @pytest.mark.parametrize(("n", "expected"), [
- (1, 2),
- pytest.mark.bar((1, 3)),
- (2, 3),
- ])
- def test_increment(n, expected):
- assert n + 1 == expected
- """
- items = testdir.getitems(s)
- assert len(items) == 3
- for item in items:
- assert 'foo' in item.keywords
- assert 'bar' not in items[0].keywords
- assert 'bar' in items[1].keywords
- assert 'bar' not in items[2].keywords
-
- def test_select_based_on_mark(self, testdir):
- s = """
- import pytest
-
- @pytest.mark.parametrize(("n", "expected"), [
- (1, 2),
- pytest.mark.foo((2, 3)),
- (3, 4),
- ])
- def test_increment(n, expected):
- assert n + 1 == expected
- """
- testdir.makepyfile(s)
- rec = testdir.inline_run("-m", 'foo')
- passed, skipped, fail = rec.listoutcomes()
- assert len(passed) == 1
- assert len(skipped) == 0
- assert len(fail) == 0
-
- @pytest.mark.xfail(reason="is this important to support??")
- def test_nested_marks(self, testdir):
- s = """
- import pytest
- mastermark = pytest.mark.foo(pytest.mark.bar)
-
- @pytest.mark.parametrize(("n", "expected"), [
- (1, 2),
- mastermark((1, 3)),
- (2, 3),
- ])
- def test_increment(n, expected):
- assert n + 1 == expected
- """
- items = testdir.getitems(s)
- assert len(items) == 3
- for mark in ['foo', 'bar']:
- assert mark not in items[0].keywords
- assert mark in items[1].keywords
- assert mark not in items[2].keywords
-
- def test_simple_xfail(self, testdir):
- s = """
- import pytest
-
- @pytest.mark.parametrize(("n", "expected"), [
- (1, 2),
- pytest.mark.xfail((1, 3)),
- (2, 3),
- ])
- def test_increment(n, expected):
- assert n + 1 == expected
- """
- testdir.makepyfile(s)
- reprec = testdir.inline_run()
- # xfail is skip??
- reprec.assertoutcome(passed=2, skipped=1)
-
- def test_simple_xfail_single_argname(self, testdir):
- s = """
- import pytest
-
- @pytest.mark.parametrize("n", [
- 2,
- pytest.mark.xfail(3),
- 4,
- ])
- def test_isEven(n):
- assert n % 2 == 0
- """
- testdir.makepyfile(s)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2, skipped=1)
-
- def test_xfail_with_arg(self, testdir):
- s = """
- import pytest
-
- @pytest.mark.parametrize(("n", "expected"), [
- (1, 2),
- pytest.mark.xfail("True")((1, 3)),
- (2, 3),
- ])
- def test_increment(n, expected):
- assert n + 1 == expected
- """
- testdir.makepyfile(s)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2, skipped=1)
-
- def test_xfail_with_kwarg(self, testdir):
- s = """
- import pytest
-
- @pytest.mark.parametrize(("n", "expected"), [
- (1, 2),
- pytest.mark.xfail(reason="some bug")((1, 3)),
- (2, 3),
- ])
- def test_increment(n, expected):
- assert n + 1 == expected
- """
- testdir.makepyfile(s)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2, skipped=1)
-
- def test_xfail_with_arg_and_kwarg(self, testdir):
- s = """
- import pytest
-
- @pytest.mark.parametrize(("n", "expected"), [
- (1, 2),
- pytest.mark.xfail("True", reason="some bug")((1, 3)),
- (2, 3),
- ])
- def test_increment(n, expected):
- assert n + 1 == expected
- """
- testdir.makepyfile(s)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2, skipped=1)
-
- def test_xfail_passing_is_xpass(self, testdir):
- s = """
- import pytest
-
- @pytest.mark.parametrize(("n", "expected"), [
- (1, 2),
- pytest.mark.xfail("sys.version > 0", reason="some bug")((2, 3)),
- (3, 4),
- ])
- def test_increment(n, expected):
- assert n + 1 == expected
- """
- testdir.makepyfile(s)
- reprec = testdir.inline_run()
- # xpass is fail, obviously :)
- reprec.assertoutcome(passed=2, failed=1)
-
- def test_parametrize_called_in_generate_tests(self, testdir):
- s = """
- import pytest
-
-
- def pytest_generate_tests(metafunc):
- passingTestData = [(1, 2),
- (2, 3)]
- failingTestData = [(1, 3),
- (2, 2)]
-
- testData = passingTestData + [pytest.mark.xfail(d)
- for d in failingTestData]
- metafunc.parametrize(("n", "expected"), testData)
-
-
- def test_increment(n, expected):
- assert n + 1 == expected
- """
- testdir.makepyfile(s)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2, skipped=2)
-
-
- @pytest.mark.issue290
- def test_parametrize_ID_generation_string_int_works(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture
- def myfixture():
- return 'example'
- @pytest.mark.parametrize(
- 'limit', (0, '0'))
- def test_limit(limit, myfixture):
- return
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/python/raises.py b/tests/wpt/web-platform-tests/tools/pytest/testing/python/raises.py
deleted file mode 100644
index 0ea7f9bee3e..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/python/raises.py
+++ /dev/null
@@ -1,78 +0,0 @@
-import pytest
-
-class TestRaises:
- def test_raises(self):
- source = "int('qwe')"
- excinfo = pytest.raises(ValueError, source)
- code = excinfo.traceback[-1].frame.code
- s = str(code.fullsource)
- assert s == source
-
- def test_raises_exec(self):
- pytest.raises(ValueError, "a,x = []")
-
- def test_raises_syntax_error(self):
- pytest.raises(SyntaxError, "qwe qwe qwe")
-
- def test_raises_function(self):
- pytest.raises(ValueError, int, 'hello')
-
- def test_raises_callable_no_exception(self):
- class A:
- def __call__(self):
- pass
- try:
- pytest.raises(ValueError, A())
- except pytest.raises.Exception:
- pass
-
- def test_raises_flip_builtin_AssertionError(self):
- # we replace AssertionError on python level
- # however c code might still raise the builtin one
- from _pytest.assertion.util import BuiltinAssertionError # noqa
- pytest.raises(AssertionError,"""
- raise BuiltinAssertionError
- """)
-
- def test_raises_as_contextmanager(self, testdir):
- testdir.makepyfile("""
- from __future__ import with_statement
- import py, pytest
- import _pytest._code
-
- def test_simple():
- with pytest.raises(ZeroDivisionError) as excinfo:
- assert isinstance(excinfo, _pytest._code.ExceptionInfo)
- 1/0
- print (excinfo)
- assert excinfo.type == ZeroDivisionError
- assert isinstance(excinfo.value, ZeroDivisionError)
-
- def test_noraise():
- with pytest.raises(pytest.raises.Exception):
- with pytest.raises(ValueError):
- int()
-
- def test_raise_wrong_exception_passes_by():
- with pytest.raises(ZeroDivisionError):
- with pytest.raises(ValueError):
- 1/0
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- '*3 passed*',
- ])
-
- def test_noclass(self):
- with pytest.raises(TypeError):
- pytest.raises('wrong', lambda: None)
-
- def test_tuple(self):
- with pytest.raises((KeyError, ValueError)):
- raise KeyError('oops')
-
- def test_no_raise_message(self):
- try:
- pytest.raises(ValueError, int, '0')
- except pytest.raises.Exception as e:
- assert e.msg == "DID NOT RAISE {0}".format(repr(ValueError))
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_argcomplete.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_argcomplete.py
deleted file mode 100644
index ace7d8ceb43..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_argcomplete.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from __future__ import with_statement
-import py, pytest
-
-# test for _argcomplete but not specific for any application
-
-def equal_with_bash(prefix, ffc, fc, out=None):
- res = ffc(prefix)
- res_bash = set(fc(prefix))
- retval = set(res) == res_bash
- if out:
- out.write('equal_with_bash %s %s\n' % (retval, res))
- if not retval:
- out.write(' python - bash: %s\n' % (set(res) - res_bash))
- out.write(' bash - python: %s\n' % (res_bash - set(res)))
- return retval
-
-# copied from argcomplete.completers as import from there
-# also pulls in argcomplete.__init__ which opens filedescriptor 9
-# this gives an IOError at the end of testrun
-def _wrapcall(*args, **kargs):
- try:
- if py.std.sys.version_info > (2,7):
- return py.std.subprocess.check_output(*args,**kargs).decode().splitlines()
- if 'stdout' in kargs:
- raise ValueError('stdout argument not allowed, it will be overridden.')
- process = py.std.subprocess.Popen(
- stdout=py.std.subprocess.PIPE, *args, **kargs)
- output, unused_err = process.communicate()
- retcode = process.poll()
- if retcode:
- cmd = kargs.get("args")
- if cmd is None:
- cmd = args[0]
- raise py.std.subprocess.CalledProcessError(retcode, cmd)
- return output.decode().splitlines()
- except py.std.subprocess.CalledProcessError:
- return []
-
-class FilesCompleter(object):
- 'File completer class, optionally takes a list of allowed extensions'
- def __init__(self,allowednames=(),directories=True):
- # Fix if someone passes in a string instead of a list
- if type(allowednames) is str:
- allowednames = [allowednames]
-
- self.allowednames = [x.lstrip('*').lstrip('.') for x in allowednames]
- self.directories = directories
-
- def __call__(self, prefix, **kwargs):
- completion = []
- if self.allowednames:
- if self.directories:
- files = _wrapcall(['bash','-c',
- "compgen -A directory -- '{p}'".format(p=prefix)])
- completion += [ f + '/' for f in files]
- for x in self.allowednames:
- completion += _wrapcall(['bash', '-c',
- "compgen -A file -X '!*.{0}' -- '{p}'".format(x,p=prefix)])
- else:
- completion += _wrapcall(['bash', '-c',
- "compgen -A file -- '{p}'".format(p=prefix)])
-
- anticomp = _wrapcall(['bash', '-c',
- "compgen -A directory -- '{p}'".format(p=prefix)])
-
- completion = list( set(completion) - set(anticomp))
-
- if self.directories:
- completion += [f + '/' for f in anticomp]
- return completion
-
-class TestArgComplete:
- @pytest.mark.skipif("sys.platform in ('win32', 'darwin')")
- def test_compare_with_compgen(self):
- from _pytest._argcomplete import FastFilesCompleter
- ffc = FastFilesCompleter()
- fc = FilesCompleter()
- for x in '/ /d /data qqq'.split():
- assert equal_with_bash(x, ffc, fc, out=py.std.sys.stdout)
-
- @pytest.mark.skipif("sys.platform in ('win32', 'darwin')")
- def test_remove_dir_prefix(self):
- """this is not compatible with compgen but it is with bash itself:
- ls /usr/<TAB>
- """
- from _pytest._argcomplete import FastFilesCompleter
- ffc = FastFilesCompleter()
- fc = FilesCompleter()
- for x in '/usr/'.split():
- assert not equal_with_bash(x, ffc, fc, out=py.std.sys.stdout)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_assertinterpret.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_assertinterpret.py
deleted file mode 100644
index 67a352ce7f5..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_assertinterpret.py
+++ /dev/null
@@ -1,274 +0,0 @@
-"PYTEST_DONT_REWRITE"
-import py
-import pytest
-from _pytest.assertion import util
-
-
-def exvalue():
- return py.std.sys.exc_info()[1]
-
-def f():
- return 2
-
-def test_not_being_rewritten():
- assert "@py_builtins" not in globals()
-
-def test_assert():
- try:
- assert f() == 3
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith('assert 2 == 3\n')
-
-def test_assert_with_explicit_message():
- try:
- assert f() == 3, "hello"
- except AssertionError:
- e = exvalue()
- assert e.msg == 'hello'
-
-def test_assert_within_finally():
- excinfo = pytest.raises(ZeroDivisionError, """
- try:
- 1/0
- finally:
- i = 42
- """)
- s = excinfo.exconly()
- assert py.std.re.search("division.+by zero", s) is not None
-
- #def g():
- # A.f()
- #excinfo = getexcinfo(TypeError, g)
- #msg = getmsg(excinfo)
- #assert msg.find("must be called with A") != -1
-
-
-def test_assert_multiline_1():
- try:
- assert (f() ==
- 3)
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith('assert 2 == 3\n')
-
-def test_assert_multiline_2():
- try:
- assert (f() == (4,
- 3)[-1])
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith('assert 2 ==')
-
-def test_in():
- try:
- assert "hi" in [1, 2]
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith("assert 'hi' in")
-
-def test_is():
- try:
- assert 1 is 2
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith("assert 1 is 2")
-
-
-def test_attrib():
- class Foo(object):
- b = 1
- i = Foo()
- try:
- assert i.b == 2
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith("assert 1 == 2")
-
-def test_attrib_inst():
- class Foo(object):
- b = 1
- try:
- assert Foo().b == 2
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith("assert 1 == 2")
-
-def test_len():
- l = list(range(42))
- try:
- assert len(l) == 100
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert s.startswith("assert 42 == 100")
- assert "where 42 = len([" in s
-
-def test_assert_non_string_message():
- class A:
- def __str__(self):
- return "hello"
- try:
- assert 0 == 1, A()
- except AssertionError:
- e = exvalue()
- assert e.msg == "hello"
-
-def test_assert_keyword_arg():
- def f(x=3):
- return False
- try:
- assert f(x=5)
- except AssertionError:
- e = exvalue()
- assert "x=5" in e.msg
-
-def test_private_class_variable():
- class X:
- def __init__(self):
- self.__v = 41
- def m(self):
- assert self.__v == 42
- try:
- X().m()
- except AssertionError:
- e = exvalue()
- assert "== 42" in e.msg
-
-# These tests should both fail, but should fail nicely...
-class WeirdRepr:
- def __repr__(self):
- return '<WeirdRepr\nsecond line>'
-
-def bug_test_assert_repr():
- v = WeirdRepr()
- try:
- assert v == 1
- except AssertionError:
- e = exvalue()
- assert e.msg.find('WeirdRepr') != -1
- assert e.msg.find('second line') != -1
- assert 0
-
-def test_assert_non_string():
- try:
- assert 0, ['list']
- except AssertionError:
- e = exvalue()
- assert e.msg.find("list") != -1
-
-def test_assert_implicit_multiline():
- try:
- x = [1,2,3]
- assert x != [1,
- 2, 3]
- except AssertionError:
- e = exvalue()
- assert e.msg.find('assert [1, 2, 3] !=') != -1
-
-
-def test_assert_with_brokenrepr_arg():
- class BrokenRepr:
- def __repr__(self): 0 / 0
- e = AssertionError(BrokenRepr())
- if e.msg.find("broken __repr__") == -1:
- pytest.fail("broken __repr__ not handle correctly")
-
-def test_multiple_statements_per_line():
- try:
- a = 1; assert a == 2
- except AssertionError:
- e = exvalue()
- assert "assert 1 == 2" in e.msg
-
-def test_power():
- try:
- assert 2**3 == 7
- except AssertionError:
- e = exvalue()
- assert "assert (2 ** 3) == 7" in e.msg
-
-
-def test_assert_customizable_reprcompare(monkeypatch):
- monkeypatch.setattr(util, '_reprcompare', lambda *args: 'hello')
- try:
- assert 3 == 4
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert "hello" in s
-
-def test_assert_long_source_1():
- try:
- assert len == [
- (None, ['somet text', 'more text']),
- ]
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert 're-run' not in s
- assert 'somet text' in s
-
-def test_assert_long_source_2():
- try:
- assert(len == [
- (None, ['somet text', 'more text']),
- ])
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert 're-run' not in s
- assert 'somet text' in s
-
-def test_assert_raise_alias(testdir):
- testdir.makepyfile("""
- "PYTEST_DONT_REWRITE"
- import sys
- EX = AssertionError
- def test_hello():
- raise EX("hello"
- "multi"
- "line")
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*def test_hello*",
- "*raise EX*",
- "*1 failed*",
- ])
-
-
-def test_assert_raise_subclass():
- class SomeEx(AssertionError):
- def __init__(self, *args):
- super(SomeEx, self).__init__()
- try:
- raise SomeEx("hello")
- except AssertionError:
- s = str(exvalue())
- assert 're-run' not in s
- assert 'could not determine' in s
-
-def test_assert_raises_in_nonzero_of_object_pytest_issue10():
- class A(object):
- def __nonzero__(self):
- raise ValueError(42)
- def __lt__(self, other):
- return A()
- def __repr__(self):
- return "<MY42 object>"
- def myany(x):
- return True
- try:
- assert not(myany(A() < 0))
- except AssertionError:
- e = exvalue()
- s = str(e)
- assert "<MY42 object> < 0" in s
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_assertion.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_assertion.py
deleted file mode 100644
index 347278e1904..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_assertion.py
+++ /dev/null
@@ -1,628 +0,0 @@
-# -*- coding: utf-8 -*-
-import sys
-import textwrap
-
-import _pytest.assertion as plugin
-import _pytest._code
-import py
-import pytest
-from _pytest.assertion import reinterpret
-from _pytest.assertion import util
-
-PY3 = sys.version_info >= (3, 0)
-
-
-@pytest.fixture
-def mock_config():
- class Config(object):
- verbose = False
- def getoption(self, name):
- if name == 'verbose':
- return self.verbose
- raise KeyError('Not mocked out: %s' % name)
- return Config()
-
-
-def interpret(expr):
- return reinterpret.reinterpret(expr, _pytest._code.Frame(sys._getframe(1)))
-
-class TestBinReprIntegration:
-
- def test_pytest_assertrepr_compare_called(self, testdir):
- testdir.makeconftest("""
- l = []
- def pytest_assertrepr_compare(op, left, right):
- l.append((op, left, right))
- def pytest_funcarg__l(request):
- return l
- """)
- testdir.makepyfile("""
- def test_hello():
- assert 0 == 1
- def test_check(l):
- assert l == [("==", 0, 1)]
- """)
- result = testdir.runpytest("-v")
- result.stdout.fnmatch_lines([
- "*test_hello*FAIL*",
- "*test_check*PASS*",
- ])
-
-def callequal(left, right, verbose=False):
- config = mock_config()
- config.verbose = verbose
- return plugin.pytest_assertrepr_compare(config, '==', left, right)
-
-
-class TestAssert_reprcompare:
- def test_different_types(self):
- assert callequal([0, 1], 'foo') is None
-
- def test_summary(self):
- summary = callequal([0, 1], [0, 2])[0]
- assert len(summary) < 65
-
- def test_text_diff(self):
- diff = callequal('spam', 'eggs')[1:]
- assert '- spam' in diff
- assert '+ eggs' in diff
-
- def test_text_skipping(self):
- lines = callequal('a'*50 + 'spam', 'a'*50 + 'eggs')
- assert 'Skipping' in lines[1]
- for line in lines:
- assert 'a'*50 not in line
-
- def test_text_skipping_verbose(self):
- lines = callequal('a'*50 + 'spam', 'a'*50 + 'eggs', verbose=True)
- assert '- ' + 'a'*50 + 'spam' in lines
- assert '+ ' + 'a'*50 + 'eggs' in lines
-
- def test_multiline_text_diff(self):
- left = 'foo\nspam\nbar'
- right = 'foo\neggs\nbar'
- diff = callequal(left, right)
- assert '- spam' in diff
- assert '+ eggs' in diff
-
- def test_list(self):
- expl = callequal([0, 1], [0, 2])
- assert len(expl) > 1
-
- @pytest.mark.parametrize(
- ['left', 'right', 'expected'], [
- ([0, 1], [0, 2], """
- Full diff:
- - [0, 1]
- ? ^
- + [0, 2]
- ? ^
- """),
- ({0: 1}, {0: 2}, """
- Full diff:
- - {0: 1}
- ? ^
- + {0: 2}
- ? ^
- """),
- (set([0, 1]), set([0, 2]), """
- Full diff:
- - set([0, 1])
- ? ^
- + set([0, 2])
- ? ^
- """ if not PY3 else """
- Full diff:
- - {0, 1}
- ? ^
- + {0, 2}
- ? ^
- """)
- ]
- )
- def test_iterable_full_diff(self, left, right, expected):
- """Test the full diff assertion failure explanation.
-
- When verbose is False, then just a -v notice to get the diff is rendered,
- when verbose is True, then ndiff of the pprint is returned.
- """
- expl = callequal(left, right, verbose=False)
- assert expl[-1] == 'Use -v to get the full diff'
- expl = '\n'.join(callequal(left, right, verbose=True))
- assert expl.endswith(textwrap.dedent(expected).strip())
-
- def test_list_different_lenghts(self):
- expl = callequal([0, 1], [0, 1, 2])
- assert len(expl) > 1
- expl = callequal([0, 1, 2], [0, 1])
- assert len(expl) > 1
-
- def test_dict(self):
- expl = callequal({'a': 0}, {'a': 1})
- assert len(expl) > 1
-
- def test_dict_omitting(self):
- lines = callequal({'a': 0, 'b': 1}, {'a': 1, 'b': 1})
- assert lines[1].startswith('Omitting 1 identical item')
- assert 'Common items' not in lines
- for line in lines[1:]:
- assert 'b' not in line
-
- def test_dict_omitting_verbose(self):
- lines = callequal({'a': 0, 'b': 1}, {'a': 1, 'b': 1}, verbose=True)
- assert lines[1].startswith('Common items:')
- assert 'Omitting' not in lines[1]
- assert lines[2] == "{'b': 1}"
-
- def test_set(self):
- expl = callequal(set([0, 1]), set([0, 2]))
- assert len(expl) > 1
-
- def test_frozenzet(self):
- expl = callequal(frozenset([0, 1]), set([0, 2]))
- assert len(expl) > 1
-
- def test_Sequence(self):
- col = py.builtin._tryimport(
- "collections.abc",
- "collections",
- "sys")
- if not hasattr(col, "MutableSequence"):
- pytest.skip("cannot import MutableSequence")
- MutableSequence = col.MutableSequence
-
- class TestSequence(MutableSequence): # works with a Sequence subclass
- def __init__(self, iterable):
- self.elements = list(iterable)
-
- def __getitem__(self, item):
- return self.elements[item]
-
- def __len__(self):
- return len(self.elements)
-
- def __setitem__(self, item, value):
- pass
-
- def __delitem__(self, item):
- pass
-
- def insert(self, item, index):
- pass
-
- expl = callequal(TestSequence([0, 1]), list([0, 2]))
- assert len(expl) > 1
-
- def test_list_tuples(self):
- expl = callequal([], [(1,2)])
- assert len(expl) > 1
- expl = callequal([(1,2)], [])
- assert len(expl) > 1
-
- def test_list_bad_repr(self):
- class A:
- def __repr__(self):
- raise ValueError(42)
- expl = callequal([], [A()])
- assert 'ValueError' in "".join(expl)
- expl = callequal({}, {'1': A()})
- assert 'faulty' in "".join(expl)
-
- def test_one_repr_empty(self):
- """
- the faulty empty string repr did trigger
- a unbound local error in _diff_text
- """
- class A(str):
- def __repr__(self):
- return ''
- expl = callequal(A(), '')
- assert not expl
-
- def test_repr_no_exc(self):
- expl = ' '.join(callequal('foo', 'bar'))
- assert 'raised in repr()' not in expl
-
- def test_unicode(self):
- left = py.builtin._totext('£€', 'utf-8')
- right = py.builtin._totext('£', 'utf-8')
- expl = callequal(left, right)
- assert expl[0] == py.builtin._totext("'£€' == '£'", 'utf-8')
- assert expl[1] == py.builtin._totext('- £€', 'utf-8')
- assert expl[2] == py.builtin._totext('+ £', 'utf-8')
-
- def test_nonascii_text(self):
- """
- :issue: 877
- non ascii python2 str caused a UnicodeDecodeError
- """
- class A(str):
- def __repr__(self):
- return '\xff'
- expl = callequal(A(), '1')
- assert expl
-
- def test_format_nonascii_explanation(self):
- assert util.format_explanation('λ')
-
- def test_mojibake(self):
- # issue 429
- left = 'e'
- right = '\xc3\xa9'
- if not isinstance(left, py.builtin.bytes):
- left = py.builtin.bytes(left, 'utf-8')
- right = py.builtin.bytes(right, 'utf-8')
- expl = callequal(left, right)
- for line in expl:
- assert isinstance(line, py.builtin.text)
- msg = py.builtin._totext('\n').join(expl)
- assert msg
-
-
-class TestFormatExplanation:
-
- def test_special_chars_full(self, testdir):
- # Issue 453, for the bug this would raise IndexError
- testdir.makepyfile("""
- def test_foo():
- assert '\\n}' == ''
- """)
- result = testdir.runpytest()
- assert result.ret == 1
- result.stdout.fnmatch_lines([
- "*AssertionError*",
- ])
-
- def test_fmt_simple(self):
- expl = 'assert foo'
- assert util.format_explanation(expl) == 'assert foo'
-
- def test_fmt_where(self):
- expl = '\n'.join(['assert 1',
- '{1 = foo',
- '} == 2'])
- res = '\n'.join(['assert 1 == 2',
- ' + where 1 = foo'])
- assert util.format_explanation(expl) == res
-
- def test_fmt_and(self):
- expl = '\n'.join(['assert 1',
- '{1 = foo',
- '} == 2',
- '{2 = bar',
- '}'])
- res = '\n'.join(['assert 1 == 2',
- ' + where 1 = foo',
- ' + and 2 = bar'])
- assert util.format_explanation(expl) == res
-
- def test_fmt_where_nested(self):
- expl = '\n'.join(['assert 1',
- '{1 = foo',
- '{foo = bar',
- '}',
- '} == 2'])
- res = '\n'.join(['assert 1 == 2',
- ' + where 1 = foo',
- ' + where foo = bar'])
- assert util.format_explanation(expl) == res
-
- def test_fmt_newline(self):
- expl = '\n'.join(['assert "foo" == "bar"',
- '~- foo',
- '~+ bar'])
- res = '\n'.join(['assert "foo" == "bar"',
- ' - foo',
- ' + bar'])
- assert util.format_explanation(expl) == res
-
- def test_fmt_newline_escaped(self):
- expl = '\n'.join(['assert foo == bar',
- 'baz'])
- res = 'assert foo == bar\\nbaz'
- assert util.format_explanation(expl) == res
-
- def test_fmt_newline_before_where(self):
- expl = '\n'.join(['the assertion message here',
- '>assert 1',
- '{1 = foo',
- '} == 2',
- '{2 = bar',
- '}'])
- res = '\n'.join(['the assertion message here',
- 'assert 1 == 2',
- ' + where 1 = foo',
- ' + and 2 = bar'])
- assert util.format_explanation(expl) == res
-
- def test_fmt_multi_newline_before_where(self):
- expl = '\n'.join(['the assertion',
- '~message here',
- '>assert 1',
- '{1 = foo',
- '} == 2',
- '{2 = bar',
- '}'])
- res = '\n'.join(['the assertion',
- ' message here',
- 'assert 1 == 2',
- ' + where 1 = foo',
- ' + and 2 = bar'])
- assert util.format_explanation(expl) == res
-
-
-def test_python25_compile_issue257(testdir):
- testdir.makepyfile("""
- def test_rewritten():
- assert 1 == 2
- # some comment
- """)
- result = testdir.runpytest()
- assert result.ret == 1
- result.stdout.fnmatch_lines("""
- *E*assert 1 == 2*
- *1 failed*
- """)
-
-def test_rewritten(testdir):
- testdir.makepyfile("""
- def test_rewritten():
- assert "@py_builtins" in globals()
- """)
- assert testdir.runpytest().ret == 0
-
-def test_reprcompare_notin(mock_config):
- detail = plugin.pytest_assertrepr_compare(
- mock_config, 'not in', 'foo', 'aaafoobbb')[1:]
- assert detail == ["'foo' is contained here:", ' aaafoobbb', '? +++']
-
-def test_pytest_assertrepr_compare_integration(testdir):
- testdir.makepyfile("""
- def test_hello():
- x = set(range(100))
- y = x.copy()
- y.remove(50)
- assert x == y
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*def test_hello():*",
- "*assert x == y*",
- "*E*Extra items*left*",
- "*E*50*",
- ])
-
-def test_sequence_comparison_uses_repr(testdir):
- testdir.makepyfile("""
- def test_hello():
- x = set("hello x")
- y = set("hello y")
- assert x == y
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*def test_hello():*",
- "*assert x == y*",
- "*E*Extra items*left*",
- "*E*'x'*",
- "*E*Extra items*right*",
- "*E*'y'*",
- ])
-
-
-def test_assert_compare_truncate_longmessage(monkeypatch, testdir):
- testdir.makepyfile(r"""
- def test_long():
- a = list(range(200))
- b = a[::2]
- a = '\n'.join(map(str, a))
- b = '\n'.join(map(str, b))
- assert a == b
- """)
- monkeypatch.delenv('CI', raising=False)
-
- result = testdir.runpytest()
- # without -vv, truncate the message showing a few diff lines only
- result.stdout.fnmatch_lines([
- "*- 1",
- "*- 3",
- "*- 5",
- "*- 7",
- "*truncated (191 more lines)*use*-vv*",
- ])
-
-
- result = testdir.runpytest('-vv')
- result.stdout.fnmatch_lines([
- "*- 197",
- ])
-
- monkeypatch.setenv('CI', '1')
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*- 197",
- ])
-
-
-def test_assertrepr_loaded_per_dir(testdir):
- testdir.makepyfile(test_base=['def test_base(): assert 1 == 2'])
- a = testdir.mkdir('a')
- a_test = a.join('test_a.py')
- a_test.write('def test_a(): assert 1 == 2')
- a_conftest = a.join('conftest.py')
- a_conftest.write('def pytest_assertrepr_compare(): return ["summary a"]')
- b = testdir.mkdir('b')
- b_test = b.join('test_b.py')
- b_test.write('def test_b(): assert 1 == 2')
- b_conftest = b.join('conftest.py')
- b_conftest.write('def pytest_assertrepr_compare(): return ["summary b"]')
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- '*def test_base():*',
- '*E*assert 1 == 2*',
- '*def test_a():*',
- '*E*assert summary a*',
- '*def test_b():*',
- '*E*assert summary b*'])
-
-
-def test_assertion_options(testdir):
- testdir.makepyfile("""
- def test_hello():
- x = 3
- assert x == 4
- """)
- result = testdir.runpytest()
- assert "3 == 4" in result.stdout.str()
- off_options = (("--no-assert",),
- ("--nomagic",),
- ("--no-assert", "--nomagic"),
- ("--assert=plain",),
- ("--assert=plain", "--no-assert"),
- ("--assert=plain", "--nomagic"),
- ("--assert=plain", "--no-assert", "--nomagic"))
- for opt in off_options:
- result = testdir.runpytest_subprocess(*opt)
- assert "3 == 4" not in result.stdout.str()
-
-def test_old_assert_mode(testdir):
- testdir.makepyfile("""
- def test_in_old_mode():
- assert "@py_builtins" not in globals()
- """)
- result = testdir.runpytest_subprocess("--assert=reinterp")
- assert result.ret == 0
-
-def test_triple_quoted_string_issue113(testdir):
- testdir.makepyfile("""
- def test_hello():
- assert "" == '''
- '''""")
- result = testdir.runpytest("--fulltrace")
- result.stdout.fnmatch_lines([
- "*1 failed*",
- ])
- assert 'SyntaxError' not in result.stdout.str()
-
-def test_traceback_failure(testdir):
- p1 = testdir.makepyfile("""
- def g():
- return 2
- def f(x):
- assert x == g()
- def test_onefails():
- f(3)
- """)
- result = testdir.runpytest(p1, "--tb=long")
- result.stdout.fnmatch_lines([
- "*test_traceback_failure.py F",
- "====* FAILURES *====",
- "____*____",
- "",
- " def test_onefails():",
- "> f(3)",
- "",
- "*test_*.py:6: ",
- "_ _ _ *",
- #"",
- " def f(x):",
- "> assert x == g()",
- "E assert 3 == 2",
- "E + where 2 = g()",
- "",
- "*test_traceback_failure.py:4: AssertionError"
- ])
-
- result = testdir.runpytest(p1) # "auto"
- result.stdout.fnmatch_lines([
- "*test_traceback_failure.py F",
- "====* FAILURES *====",
- "____*____",
- "",
- " def test_onefails():",
- "> f(3)",
- "",
- "*test_*.py:6: ",
- "",
- " def f(x):",
- "> assert x == g()",
- "E assert 3 == 2",
- "E + where 2 = g()",
- "",
- "*test_traceback_failure.py:4: AssertionError"
- ])
-
-@pytest.mark.skipif("'__pypy__' in sys.builtin_module_names or sys.platform.startswith('java')" )
-def test_warn_missing(testdir):
- testdir.makepyfile("")
- result = testdir.run(sys.executable, "-OO", "-m", "pytest", "-h")
- result.stderr.fnmatch_lines([
- "*WARNING*assert statements are not executed*",
- ])
- result = testdir.run(sys.executable, "-OO", "-m", "pytest", "--no-assert")
- result.stderr.fnmatch_lines([
- "*WARNING*assert statements are not executed*",
- ])
-
-def test_recursion_source_decode(testdir):
- testdir.makepyfile("""
- def test_something():
- pass
- """)
- testdir.makeini("""
- [pytest]
- python_files = *.py
- """)
- result = testdir.runpytest("--collect-only")
- result.stdout.fnmatch_lines("""
- <Module*>
- """)
-
-def test_AssertionError_message(testdir):
- testdir.makepyfile("""
- def test_hello():
- x,y = 1,2
- assert 0, (x,y)
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines("""
- *def test_hello*
- *assert 0, (x,y)*
- *AssertionError: (1, 2)*
- """)
-
-@pytest.mark.skipif(PY3, reason='This bug does not exist on PY3')
-def test_set_with_unsortable_elements():
- # issue #718
- class UnsortableKey(object):
- def __init__(self, name):
- self.name = name
-
- def __lt__(self, other):
- raise RuntimeError()
-
- def __repr__(self):
- return 'repr({0})'.format(self.name)
-
- def __eq__(self, other):
- return self.name == other.name
-
- def __hash__(self):
- return hash(self.name)
-
- left_set = set(UnsortableKey(str(i)) for i in range(1, 3))
- right_set = set(UnsortableKey(str(i)) for i in range(2, 4))
- expl = callequal(left_set, right_set, verbose=True)
- # skip first line because it contains the "construction" of the set, which does not have a guaranteed order
- expl = expl[1:]
- dedent = textwrap.dedent("""
- Extra items in the left set:
- repr(1)
- Extra items in the right set:
- repr(3)
- Full diff (fallback to calling repr on each item):
- - repr(1)
- repr(2)
- + repr(3)
- """).strip()
- assert '\n'.join(expl) == dedent
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_assertrewrite.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_assertrewrite.py
deleted file mode 100644
index f43c424ca94..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_assertrewrite.py
+++ /dev/null
@@ -1,716 +0,0 @@
-import os
-import stat
-import sys
-import zipfile
-import py
-import pytest
-
-ast = pytest.importorskip("ast")
-if sys.platform.startswith("java"):
- # XXX should be xfail
- pytest.skip("assert rewrite does currently not work on jython")
-
-import _pytest._code
-from _pytest.assertion import util
-from _pytest.assertion.rewrite import rewrite_asserts, PYTEST_TAG
-from _pytest.main import EXIT_NOTESTSCOLLECTED
-
-
-def setup_module(mod):
- mod._old_reprcompare = util._reprcompare
- _pytest._code._reprcompare = None
-
-def teardown_module(mod):
- util._reprcompare = mod._old_reprcompare
- del mod._old_reprcompare
-
-
-def rewrite(src):
- tree = ast.parse(src)
- rewrite_asserts(tree)
- return tree
-
-def getmsg(f, extra_ns=None, must_pass=False):
- """Rewrite the assertions in f, run it, and get the failure message."""
- src = '\n'.join(_pytest._code.Code(f).source().lines)
- mod = rewrite(src)
- code = compile(mod, "<test>", "exec")
- ns = {}
- if extra_ns is not None:
- ns.update(extra_ns)
- py.builtin.exec_(code, ns)
- func = ns[f.__name__]
- try:
- func()
- except AssertionError:
- if must_pass:
- pytest.fail("shouldn't have raised")
- s = str(sys.exc_info()[1])
- if not s.startswith("assert"):
- return "AssertionError: " + s
- return s
- else:
- if not must_pass:
- pytest.fail("function didn't raise at all")
-
-
-class TestAssertionRewrite:
-
- def test_place_initial_imports(self):
- s = """'Doc string'\nother = stuff"""
- m = rewrite(s)
- assert isinstance(m.body[0], ast.Expr)
- assert isinstance(m.body[0].value, ast.Str)
- for imp in m.body[1:3]:
- assert isinstance(imp, ast.Import)
- assert imp.lineno == 2
- assert imp.col_offset == 0
- assert isinstance(m.body[3], ast.Assign)
- s = """from __future__ import with_statement\nother_stuff"""
- m = rewrite(s)
- assert isinstance(m.body[0], ast.ImportFrom)
- for imp in m.body[1:3]:
- assert isinstance(imp, ast.Import)
- assert imp.lineno == 2
- assert imp.col_offset == 0
- assert isinstance(m.body[3], ast.Expr)
- s = """'doc string'\nfrom __future__ import with_statement\nother"""
- m = rewrite(s)
- assert isinstance(m.body[0], ast.Expr)
- assert isinstance(m.body[0].value, ast.Str)
- assert isinstance(m.body[1], ast.ImportFrom)
- for imp in m.body[2:4]:
- assert isinstance(imp, ast.Import)
- assert imp.lineno == 3
- assert imp.col_offset == 0
- assert isinstance(m.body[4], ast.Expr)
- s = """from . import relative\nother_stuff"""
- m = rewrite(s)
- for imp in m.body[0:2]:
- assert isinstance(imp, ast.Import)
- assert imp.lineno == 1
- assert imp.col_offset == 0
- assert isinstance(m.body[3], ast.Expr)
-
- def test_dont_rewrite(self):
- s = """'PYTEST_DONT_REWRITE'\nassert 14"""
- m = rewrite(s)
- assert len(m.body) == 2
- assert isinstance(m.body[0].value, ast.Str)
- assert isinstance(m.body[1], ast.Assert)
- assert m.body[1].msg is None
-
- def test_name(self):
- def f():
- assert False
- assert getmsg(f) == "assert False"
- def f():
- f = False
- assert f
- assert getmsg(f) == "assert False"
- def f():
- assert a_global # noqa
- assert getmsg(f, {"a_global" : False}) == "assert False"
- def f():
- assert sys == 42
- assert getmsg(f, {"sys" : sys}) == "assert sys == 42"
- def f():
- assert cls == 42 # noqa
- class X(object):
- pass
- assert getmsg(f, {"cls" : X}) == "assert cls == 42"
-
- def test_assert_already_has_message(self):
- def f():
- assert False, "something bad!"
- assert getmsg(f) == "AssertionError: something bad!\nassert False"
-
- def test_assertion_message(self, testdir):
- testdir.makepyfile("""
- def test_foo():
- assert 1 == 2, "The failure message"
- """)
- result = testdir.runpytest()
- assert result.ret == 1
- result.stdout.fnmatch_lines([
- "*AssertionError*The failure message*",
- "*assert 1 == 2*",
- ])
-
- def test_assertion_message_multiline(self, testdir):
- testdir.makepyfile("""
- def test_foo():
- assert 1 == 2, "A multiline\\nfailure message"
- """)
- result = testdir.runpytest()
- assert result.ret == 1
- result.stdout.fnmatch_lines([
- "*AssertionError*A multiline*",
- "*failure message*",
- "*assert 1 == 2*",
- ])
-
- def test_assertion_message_tuple(self, testdir):
- testdir.makepyfile("""
- def test_foo():
- assert 1 == 2, (1, 2)
- """)
- result = testdir.runpytest()
- assert result.ret == 1
- result.stdout.fnmatch_lines([
- "*AssertionError*%s*" % repr((1, 2)),
- "*assert 1 == 2*",
- ])
-
- def test_assertion_message_expr(self, testdir):
- testdir.makepyfile("""
- def test_foo():
- assert 1 == 2, 1 + 2
- """)
- result = testdir.runpytest()
- assert result.ret == 1
- result.stdout.fnmatch_lines([
- "*AssertionError*3*",
- "*assert 1 == 2*",
- ])
-
- def test_assertion_message_escape(self, testdir):
- testdir.makepyfile("""
- def test_foo():
- assert 1 == 2, 'To be escaped: %'
- """)
- result = testdir.runpytest()
- assert result.ret == 1
- result.stdout.fnmatch_lines([
- "*AssertionError: To be escaped: %",
- "*assert 1 == 2",
- ])
-
- def test_boolop(self):
- def f():
- f = g = False
- assert f and g
- assert getmsg(f) == "assert (False)"
- def f():
- f = True
- g = False
- assert f and g
- assert getmsg(f) == "assert (True and False)"
- def f():
- f = False
- g = True
- assert f and g
- assert getmsg(f) == "assert (False)"
- def f():
- f = g = False
- assert f or g
- assert getmsg(f) == "assert (False or False)"
- def f():
- f = g = False
- assert not f and not g
- getmsg(f, must_pass=True)
- def x():
- return False
- def f():
- assert x() and x()
- assert getmsg(f, {"x" : x}) == "assert (x())"
- def f():
- assert False or x()
- assert getmsg(f, {"x" : x}) == "assert (False or x())"
- def f():
- assert 1 in {} and 2 in {}
- assert getmsg(f) == "assert (1 in {})"
- def f():
- x = 1
- y = 2
- assert x in {1 : None} and y in {}
- assert getmsg(f) == "assert (1 in {1: None} and 2 in {})"
- def f():
- f = True
- g = False
- assert f or g
- getmsg(f, must_pass=True)
- def f():
- f = g = h = lambda: True
- assert f() and g() and h()
- getmsg(f, must_pass=True)
-
- def test_short_circut_evaluation(self):
- def f():
- assert True or explode # noqa
- getmsg(f, must_pass=True)
- def f():
- x = 1
- assert x == 1 or x == 2
- getmsg(f, must_pass=True)
-
- def test_unary_op(self):
- def f():
- x = True
- assert not x
- assert getmsg(f) == "assert not True"
- def f():
- x = 0
- assert ~x + 1
- assert getmsg(f) == "assert (~0 + 1)"
- def f():
- x = 3
- assert -x + x
- assert getmsg(f) == "assert (-3 + 3)"
- def f():
- x = 0
- assert +x + x
- assert getmsg(f) == "assert (+0 + 0)"
-
- def test_binary_op(self):
- def f():
- x = 1
- y = -1
- assert x + y
- assert getmsg(f) == "assert (1 + -1)"
- def f():
- assert not 5 % 4
- assert getmsg(f) == "assert not (5 % 4)"
-
- def test_boolop_percent(self):
- def f():
- assert 3 % 2 and False
- assert getmsg(f) == "assert ((3 % 2) and False)"
- def f():
- assert False or 4 % 2
- assert getmsg(f) == "assert (False or (4 % 2))"
-
- @pytest.mark.skipif("sys.version_info < (3,5)")
- def test_at_operator_issue1290(self, testdir):
- testdir.makepyfile("""
- class Matrix:
- def __init__(self, num):
- self.num = num
- def __matmul__(self, other):
- return self.num * other.num
-
- def test_multmat_operator():
- assert Matrix(2) @ Matrix(3) == 6""")
- testdir.runpytest().assert_outcomes(passed=1)
-
- def test_call(self):
- def g(a=42, *args, **kwargs):
- return False
- ns = {"g" : g}
- def f():
- assert g()
- assert getmsg(f, ns) == """assert g()"""
- def f():
- assert g(1)
- assert getmsg(f, ns) == """assert g(1)"""
- def f():
- assert g(1, 2)
- assert getmsg(f, ns) == """assert g(1, 2)"""
- def f():
- assert g(1, g=42)
- assert getmsg(f, ns) == """assert g(1, g=42)"""
- def f():
- assert g(1, 3, g=23)
- assert getmsg(f, ns) == """assert g(1, 3, g=23)"""
- def f():
- seq = [1, 2, 3]
- assert g(*seq)
- assert getmsg(f, ns) == """assert g(*[1, 2, 3])"""
- def f():
- x = "a"
- assert g(**{x : 2})
- assert getmsg(f, ns) == """assert g(**{'a': 2})"""
-
- def test_attribute(self):
- class X(object):
- g = 3
- ns = {"x" : X}
- def f():
- assert not x.g # noqa
- assert getmsg(f, ns) == """assert not 3
- + where 3 = x.g"""
- def f():
- x.a = False # noqa
- assert x.a # noqa
- assert getmsg(f, ns) == """assert x.a"""
-
- def test_comparisons(self):
- def f():
- a, b = range(2)
- assert b < a
- assert getmsg(f) == """assert 1 < 0"""
- def f():
- a, b, c = range(3)
- assert a > b > c
- assert getmsg(f) == """assert 0 > 1"""
- def f():
- a, b, c = range(3)
- assert a < b > c
- assert getmsg(f) == """assert 1 > 2"""
- def f():
- a, b, c = range(3)
- assert a < b <= c
- getmsg(f, must_pass=True)
- def f():
- a, b, c = range(3)
- assert a < b
- assert b < c
- getmsg(f, must_pass=True)
-
- def test_len(self):
- def f():
- l = list(range(10))
- assert len(l) == 11
- assert getmsg(f).startswith("""assert 10 == 11
- + where 10 = len([""")
-
- def test_custom_reprcompare(self, monkeypatch):
- def my_reprcompare(op, left, right):
- return "42"
- monkeypatch.setattr(util, "_reprcompare", my_reprcompare)
- def f():
- assert 42 < 3
- assert getmsg(f) == "assert 42"
- def my_reprcompare(op, left, right):
- return "%s %s %s" % (left, op, right)
- monkeypatch.setattr(util, "_reprcompare", my_reprcompare)
- def f():
- assert 1 < 3 < 5 <= 4 < 7
- assert getmsg(f) == "assert 5 <= 4"
-
- def test_assert_raising_nonzero_in_comparison(self):
- def f():
- class A(object):
- def __nonzero__(self):
- raise ValueError(42)
- def __lt__(self, other):
- return A()
- def __repr__(self):
- return "<MY42 object>"
- def myany(x):
- return False
- assert myany(A() < 0)
- assert "<MY42 object> < 0" in getmsg(f)
-
- def test_formatchar(self):
- def f():
- assert "%test" == "test"
- assert getmsg(f).startswith("assert '%test' == 'test'")
-
- def test_custom_repr(self):
- def f():
- class Foo(object):
- a = 1
-
- def __repr__(self):
- return "\n{ \n~ \n}"
- f = Foo()
- assert 0 == f.a
- assert r"where 1 = \n{ \n~ \n}.a" in util._format_lines([getmsg(f)])[0]
-
-
-class TestRewriteOnImport:
-
- def test_pycache_is_a_file(self, testdir):
- testdir.tmpdir.join("__pycache__").write("Hello")
- testdir.makepyfile("""
- def test_rewritten():
- assert "@py_builtins" in globals()""")
- assert testdir.runpytest().ret == 0
-
- def test_pycache_is_readonly(self, testdir):
- cache = testdir.tmpdir.mkdir("__pycache__")
- old_mode = cache.stat().mode
- cache.chmod(old_mode ^ stat.S_IWRITE)
- testdir.makepyfile("""
- def test_rewritten():
- assert "@py_builtins" in globals()""")
- try:
- assert testdir.runpytest().ret == 0
- finally:
- cache.chmod(old_mode)
-
- def test_zipfile(self, testdir):
- z = testdir.tmpdir.join("myzip.zip")
- z_fn = str(z)
- f = zipfile.ZipFile(z_fn, "w")
- try:
- f.writestr("test_gum/__init__.py", "")
- f.writestr("test_gum/test_lizard.py", "")
- finally:
- f.close()
- z.chmod(256)
- testdir.makepyfile("""
- import sys
- sys.path.append(%r)
- import test_gum.test_lizard""" % (z_fn,))
- assert testdir.runpytest().ret == EXIT_NOTESTSCOLLECTED
-
- def test_readonly(self, testdir):
- sub = testdir.mkdir("testing")
- sub.join("test_readonly.py").write(
- py.builtin._totext("""
-def test_rewritten():
- assert "@py_builtins" in globals()
- """).encode("utf-8"), "wb")
- old_mode = sub.stat().mode
- sub.chmod(320)
- try:
- assert testdir.runpytest().ret == 0
- finally:
- sub.chmod(old_mode)
-
- def test_dont_write_bytecode(self, testdir, monkeypatch):
- testdir.makepyfile("""
- import os
- def test_no_bytecode():
- assert "__pycache__" in __cached__
- assert not os.path.exists(__cached__)
- assert not os.path.exists(os.path.dirname(__cached__))""")
- monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
- assert testdir.runpytest_subprocess().ret == 0
-
- @pytest.mark.skipif('"__pypy__" in sys.modules')
- def test_pyc_vs_pyo(self, testdir, monkeypatch):
- testdir.makepyfile("""
- import pytest
- def test_optimized():
- "hello"
- assert test_optimized.__doc__ is None"""
- )
- p = py.path.local.make_numbered_dir(prefix="runpytest-", keep=None,
- rootdir=testdir.tmpdir)
- tmp = "--basetemp=%s" % p
- monkeypatch.setenv("PYTHONOPTIMIZE", "2")
- monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
- assert testdir.runpytest_subprocess(tmp).ret == 0
- tagged = "test_pyc_vs_pyo." + PYTEST_TAG
- assert tagged + ".pyo" in os.listdir("__pycache__")
- monkeypatch.undo()
- monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
- assert testdir.runpytest_subprocess(tmp).ret == 1
- assert tagged + ".pyc" in os.listdir("__pycache__")
-
- def test_package(self, testdir):
- pkg = testdir.tmpdir.join("pkg")
- pkg.mkdir()
- pkg.join("__init__.py").ensure()
- pkg.join("test_blah.py").write("""
-def test_rewritten():
- assert "@py_builtins" in globals()""")
- assert testdir.runpytest().ret == 0
-
- def test_translate_newlines(self, testdir):
- content = "def test_rewritten():\r\n assert '@py_builtins' in globals()"
- b = content.encode("utf-8")
- testdir.tmpdir.join("test_newlines.py").write(b, "wb")
- assert testdir.runpytest().ret == 0
-
- @pytest.mark.skipif(sys.version_info < (3,3),
- reason='packages without __init__.py not supported on python 2')
- def test_package_without__init__py(self, testdir):
- pkg = testdir.mkdir('a_package_without_init_py')
- pkg.join('module.py').ensure()
- testdir.makepyfile("import a_package_without_init_py.module")
- assert testdir.runpytest().ret == EXIT_NOTESTSCOLLECTED
-
-class TestAssertionRewriteHookDetails(object):
- def test_loader_is_package_false_for_module(self, testdir):
- testdir.makepyfile(test_fun="""
- def test_loader():
- assert not __loader__.is_package(__name__)
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "* 1 passed*",
- ])
-
- def test_loader_is_package_true_for_package(self, testdir):
- testdir.makepyfile(test_fun="""
- def test_loader():
- assert not __loader__.is_package(__name__)
-
- def test_fun():
- assert __loader__.is_package('fun')
-
- def test_missing():
- assert not __loader__.is_package('pytest_not_there')
- """)
- testdir.mkpydir('fun')
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- '* 3 passed*',
- ])
-
- @pytest.mark.skipif("sys.version_info[0] >= 3")
- @pytest.mark.xfail("hasattr(sys, 'pypy_translation_info')")
- def test_assume_ascii(self, testdir):
- content = "u'\xe2\x99\xa5\x01\xfe'"
- testdir.tmpdir.join("test_encoding.py").write(content, "wb")
- res = testdir.runpytest()
- assert res.ret != 0
- assert "SyntaxError: Non-ASCII character" in res.stdout.str()
-
- @pytest.mark.skipif("sys.version_info[0] >= 3")
- def test_detect_coding_cookie(self, testdir):
- testdir.makepyfile(test_cookie="""
- # -*- coding: utf-8 -*-
- u"St\xc3\xa4d"
- def test_rewritten():
- assert "@py_builtins" in globals()""")
- assert testdir.runpytest().ret == 0
-
- @pytest.mark.skipif("sys.version_info[0] >= 3")
- def test_detect_coding_cookie_second_line(self, testdir):
- testdir.makepyfile(test_cookie="""
- # -*- coding: utf-8 -*-
- u"St\xc3\xa4d"
- def test_rewritten():
- assert "@py_builtins" in globals()""")
- assert testdir.runpytest().ret == 0
-
- @pytest.mark.skipif("sys.version_info[0] >= 3")
- def test_detect_coding_cookie_crlf(self, testdir):
- testdir.makepyfile(test_cookie="""
- # -*- coding: utf-8 -*-
- u"St\xc3\xa4d"
- def test_rewritten():
- assert "@py_builtins" in globals()""")
- assert testdir.runpytest().ret == 0
-
- def test_sys_meta_path_munged(self, testdir):
- testdir.makepyfile("""
- def test_meta_path():
- import sys; sys.meta_path = []""")
- assert testdir.runpytest().ret == 0
-
- def test_write_pyc(self, testdir, tmpdir, monkeypatch):
- from _pytest.assertion.rewrite import _write_pyc
- from _pytest.assertion import AssertionState
- try:
- import __builtin__ as b
- except ImportError:
- import builtins as b
- config = testdir.parseconfig([])
- state = AssertionState(config, "rewrite")
- source_path = tmpdir.ensure("source.py")
- pycpath = tmpdir.join("pyc").strpath
- assert _write_pyc(state, [1], source_path.stat(), pycpath)
- def open(*args):
- e = IOError()
- e.errno = 10
- raise e
- monkeypatch.setattr(b, "open", open)
- assert not _write_pyc(state, [1], source_path.stat(), pycpath)
-
- def test_resources_provider_for_loader(self, testdir):
- """
- Attempts to load resources from a package should succeed normally,
- even when the AssertionRewriteHook is used to load the modules.
-
- See #366 for details.
- """
- pytest.importorskip("pkg_resources")
-
- testdir.mkpydir('testpkg')
- contents = {
- 'testpkg/test_pkg': """
- import pkg_resources
-
- import pytest
- from _pytest.assertion.rewrite import AssertionRewritingHook
-
- def test_load_resource():
- assert isinstance(__loader__, AssertionRewritingHook)
- res = pkg_resources.resource_string(__name__, 'resource.txt')
- res = res.decode('ascii')
- assert res == 'Load me please.'
- """,
- }
- testdir.makepyfile(**contents)
- testdir.maketxtfile(**{'testpkg/resource': "Load me please."})
-
- result = testdir.runpytest_subprocess()
- result.assert_outcomes(passed=1)
-
- def test_read_pyc(self, tmpdir):
- """
- Ensure that the `_read_pyc` can properly deal with corrupted pyc files.
- In those circumstances it should just give up instead of generating
- an exception that is propagated to the caller.
- """
- import py_compile
- from _pytest.assertion.rewrite import _read_pyc
-
- source = tmpdir.join('source.py')
- pyc = source + 'c'
-
- source.write('def test(): pass')
- py_compile.compile(str(source), str(pyc))
-
- contents = pyc.read(mode='rb')
- strip_bytes = 20 # header is around 8 bytes, strip a little more
- assert len(contents) > strip_bytes
- pyc.write(contents[:strip_bytes], mode='wb')
-
- assert _read_pyc(source, str(pyc)) is None # no error
-
- def test_reload_is_same(self, testdir):
- # A file that will be picked up during collecting.
- testdir.tmpdir.join("file.py").ensure()
- testdir.tmpdir.join("pytest.ini").write(py.std.textwrap.dedent("""
- [pytest]
- python_files = *.py
- """))
-
- testdir.makepyfile(test_fun="""
- import sys
- try:
- from imp import reload
- except ImportError:
- pass
-
- def test_loader():
- import file
- assert sys.modules["file"] is reload(file)
- """)
- result = testdir.runpytest('-s')
- result.stdout.fnmatch_lines([
- "* 1 passed*",
- ])
-
- def test_get_data_support(self, testdir):
- """Implement optional PEP302 api (#808).
- """
- path = testdir.mkpydir("foo")
- path.join("test_foo.py").write(_pytest._code.Source("""
- class Test:
- def test_foo(self):
- import pkgutil
- data = pkgutil.get_data('foo.test_foo', 'data.txt')
- assert data == b'Hey'
- """))
- path.join('data.txt').write('Hey')
- result = testdir.runpytest()
- result.stdout.fnmatch_lines('*1 passed*')
-
-
-def test_issue731(testdir):
- testdir.makepyfile("""
- class LongReprWithBraces(object):
- def __repr__(self):
- return 'LongReprWithBraces({' + ('a' * 80) + '}' + ('a' * 120) + ')'
-
- def some_method(self):
- return False
-
- def test_long_repr():
- obj = LongReprWithBraces()
- assert obj.some_method()
- """)
- result = testdir.runpytest()
- assert 'unbalanced braces' not in result.stdout.str()
-
-
-def test_collapse_false_unbalanced_braces():
- util._collapse_false('some text{ False\n{False = some more text\n}')
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_cache.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_cache.py
deleted file mode 100755
index 98053f86947..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_cache.py
+++ /dev/null
@@ -1,386 +0,0 @@
-import sys
-
-import _pytest
-import pytest
-import os
-import shutil
-
-pytest_plugins = "pytester",
-
-class TestNewAPI:
- def test_config_cache_makedir(self, testdir):
- testdir.makeini("[pytest]")
- config = testdir.parseconfigure()
- with pytest.raises(ValueError):
- config.cache.makedir("key/name")
-
- p = config.cache.makedir("name")
- assert p.check()
-
- def test_config_cache_dataerror(self, testdir):
- testdir.makeini("[pytest]")
- config = testdir.parseconfigure()
- cache = config.cache
- pytest.raises(TypeError, lambda: cache.set("key/name", cache))
- config.cache.set("key/name", 0)
- config.cache._getvaluepath("key/name").write("123invalid")
- val = config.cache.get("key/name", -2)
- assert val == -2
-
- def test_cache_writefail_cachfile_silent(self, testdir):
- testdir.makeini("[pytest]")
- testdir.tmpdir.join('.cache').write('gone wrong')
- config = testdir.parseconfigure()
- cache = config.cache
- cache.set('test/broken', [])
-
- @pytest.mark.skipif(sys.platform.startswith('win'), reason='no chmod on windows')
- def test_cache_writefail_permissions(self, testdir):
- testdir.makeini("[pytest]")
- testdir.tmpdir.ensure_dir('.cache').chmod(0)
- config = testdir.parseconfigure()
- cache = config.cache
- cache.set('test/broken', [])
-
- @pytest.mark.skipif(sys.platform.startswith('win'), reason='no chmod on windows')
- def test_cache_failure_warns(self, testdir):
- testdir.tmpdir.ensure_dir('.cache').chmod(0)
- testdir.makepyfile("""
- def test_error():
- raise Exception
-
- """)
- result = testdir.runpytest('-rw')
- assert result.ret == 1
- result.stdout.fnmatch_lines([
- "*could not create cache path*",
- "*1 pytest-warnings*",
- ])
-
- def test_config_cache(self, testdir):
- testdir.makeconftest("""
- def pytest_configure(config):
- # see that we get cache information early on
- assert hasattr(config, "cache")
- """)
- testdir.makepyfile("""
- def test_session(pytestconfig):
- assert hasattr(pytestconfig, "cache")
- """)
- result = testdir.runpytest()
- assert result.ret == 0
- result.stdout.fnmatch_lines(["*1 passed*"])
-
- def test_cachefuncarg(self, testdir):
- testdir.makepyfile("""
- import pytest
- def test_cachefuncarg(cache):
- val = cache.get("some/thing", None)
- assert val is None
- cache.set("some/thing", [1])
- pytest.raises(TypeError, lambda: cache.get("some/thing"))
- val = cache.get("some/thing", [])
- assert val == [1]
- """)
- result = testdir.runpytest()
- assert result.ret == 0
- result.stdout.fnmatch_lines(["*1 passed*"])
-
-
-
-def test_cache_reportheader(testdir):
- testdir.makepyfile("""
- def test_hello():
- pass
- """)
- result = testdir.runpytest("-v")
- result.stdout.fnmatch_lines([
- "cachedir: .cache"
- ])
-
-
-def test_cache_show(testdir):
- result = testdir.runpytest("--cache-show")
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*cache is empty*"
- ])
- testdir.makeconftest("""
- def pytest_configure(config):
- config.cache.set("my/name", [1,2,3])
- config.cache.set("other/some", {1:2})
- dp = config.cache.makedir("mydb")
- dp.ensure("hello")
- dp.ensure("world")
- """)
- result = testdir.runpytest()
- assert result.ret == 5 # no tests executed
- result = testdir.runpytest("--cache-show")
- result.stdout.fnmatch_lines_random([
- "*cachedir:*",
- "-*cache values*-",
- "*my/name contains:",
- " [1, 2, 3]",
- "*other/some contains*",
- " {*1*: 2}",
- "-*cache directories*-",
- "*mydb/hello*length 0*",
- "*mydb/world*length 0*",
- ])
-
-
-class TestLastFailed:
-
- def test_lastfailed_usecase(self, testdir, monkeypatch):
- monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
- p = testdir.makepyfile("""
- def test_1():
- assert 0
- def test_2():
- assert 0
- def test_3():
- assert 1
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*2 failed*",
- ])
- p.write(_pytest._code.Source("""
- def test_1():
- assert 1
-
- def test_2():
- assert 1
-
- def test_3():
- assert 0
- """))
- result = testdir.runpytest("--lf")
- result.stdout.fnmatch_lines([
- "*2 passed*1 desel*",
- ])
- result = testdir.runpytest("--lf")
- result.stdout.fnmatch_lines([
- "*1 failed*2 passed*",
- ])
- result = testdir.runpytest("--lf", "--cache-clear")
- result.stdout.fnmatch_lines([
- "*1 failed*2 passed*",
- ])
-
- # Run this again to make sure clear-cache is robust
- if os.path.isdir('.cache'):
- shutil.rmtree('.cache')
- result = testdir.runpytest("--lf", "--cache-clear")
- result.stdout.fnmatch_lines([
- "*1 failed*2 passed*",
- ])
-
- def test_failedfirst_order(self, testdir):
- testdir.tmpdir.join('test_a.py').write(_pytest._code.Source("""
- def test_always_passes():
- assert 1
- """))
- testdir.tmpdir.join('test_b.py').write(_pytest._code.Source("""
- def test_always_fails():
- assert 0
- """))
- result = testdir.runpytest()
- # Test order will be collection order; alphabetical
- result.stdout.fnmatch_lines([
- "test_a.py*",
- "test_b.py*",
- ])
- result = testdir.runpytest("--lf", "--ff")
- # Test order will be failing tests firs
- result.stdout.fnmatch_lines([
- "test_b.py*",
- "test_a.py*",
- ])
-
- def test_lastfailed_difference_invocations(self, testdir, monkeypatch):
- monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
- testdir.makepyfile(test_a="""
- def test_a1():
- assert 0
- def test_a2():
- assert 1
- """, test_b="""
- def test_b1():
- assert 0
- """)
- p = testdir.tmpdir.join("test_a.py")
- p2 = testdir.tmpdir.join("test_b.py")
-
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*2 failed*",
- ])
- result = testdir.runpytest("--lf", p2)
- result.stdout.fnmatch_lines([
- "*1 failed*",
- ])
- p2.write(_pytest._code.Source("""
- def test_b1():
- assert 1
- """))
- result = testdir.runpytest("--lf", p2)
- result.stdout.fnmatch_lines([
- "*1 passed*",
- ])
- result = testdir.runpytest("--lf", p)
- result.stdout.fnmatch_lines([
- "*1 failed*1 desel*",
- ])
-
- def test_lastfailed_usecase_splice(self, testdir, monkeypatch):
- monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
- testdir.makepyfile("""
- def test_1():
- assert 0
- """)
- p2 = testdir.tmpdir.join("test_something.py")
- p2.write(_pytest._code.Source("""
- def test_2():
- assert 0
- """))
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*2 failed*",
- ])
- result = testdir.runpytest("--lf", p2)
- result.stdout.fnmatch_lines([
- "*1 failed*",
- ])
- result = testdir.runpytest("--lf")
- result.stdout.fnmatch_lines([
- "*2 failed*",
- ])
-
- def test_lastfailed_xpass(self, testdir):
- testdir.inline_runsource("""
- import pytest
- @pytest.mark.xfail
- def test_hello():
- assert 1
- """)
- config = testdir.parseconfigure()
- lastfailed = config.cache.get("cache/lastfailed", -1)
- assert lastfailed == -1
-
- def test_non_serializable_parametrize(self, testdir):
- """Test that failed parametrized tests with unmarshable parameters
- don't break pytest-cache.
- """
- testdir.makepyfile(r"""
- import pytest
-
- @pytest.mark.parametrize('val', [
- b'\xac\x10\x02G',
- ])
- def test_fail(val):
- assert False
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines('*1 failed in*')
-
- def test_lastfailed_collectfailure(self, testdir, monkeypatch):
-
- testdir.makepyfile(test_maybe="""
- import py
- env = py.std.os.environ
- if '1' == env['FAILIMPORT']:
- raise ImportError('fail')
- def test_hello():
- assert '0' == env['FAILTEST']
- """)
-
- def rlf(fail_import, fail_run):
- monkeypatch.setenv('FAILIMPORT', fail_import)
- monkeypatch.setenv('FAILTEST', fail_run)
-
- testdir.runpytest('-q')
- config = testdir.parseconfigure()
- lastfailed = config.cache.get("cache/lastfailed", -1)
- return lastfailed
-
- lastfailed = rlf(fail_import=0, fail_run=0)
- assert lastfailed == -1
-
- lastfailed = rlf(fail_import=1, fail_run=0)
- assert list(lastfailed) == ['test_maybe.py']
-
- lastfailed = rlf(fail_import=0, fail_run=1)
- assert list(lastfailed) == ['test_maybe.py::test_hello']
-
-
- def test_lastfailed_failure_subset(self, testdir, monkeypatch):
-
- testdir.makepyfile(test_maybe="""
- import py
- env = py.std.os.environ
- if '1' == env['FAILIMPORT']:
- raise ImportError('fail')
- def test_hello():
- assert '0' == env['FAILTEST']
- """)
-
- testdir.makepyfile(test_maybe2="""
- import py
- env = py.std.os.environ
- if '1' == env['FAILIMPORT']:
- raise ImportError('fail')
- def test_hello():
- assert '0' == env['FAILTEST']
-
- def test_pass():
- pass
- """)
-
- def rlf(fail_import, fail_run, args=()):
- monkeypatch.setenv('FAILIMPORT', fail_import)
- monkeypatch.setenv('FAILTEST', fail_run)
-
- result = testdir.runpytest('-q', '--lf', *args)
- config = testdir.parseconfigure()
- lastfailed = config.cache.get("cache/lastfailed", -1)
- return result, lastfailed
-
- result, lastfailed = rlf(fail_import=0, fail_run=0)
- assert lastfailed == -1
- result.stdout.fnmatch_lines([
- '*3 passed*',
- ])
-
- result, lastfailed = rlf(fail_import=1, fail_run=0)
- assert sorted(list(lastfailed)) == ['test_maybe.py', 'test_maybe2.py']
-
-
- result, lastfailed = rlf(fail_import=0, fail_run=0,
- args=('test_maybe2.py',))
- assert list(lastfailed) == ['test_maybe.py']
-
-
- # edge case of test selection - even if we remember failures
- # from other tests we still need to run all tests if no test
- # matches the failures
- result, lastfailed = rlf(fail_import=0, fail_run=0,
- args=('test_maybe2.py',))
- assert list(lastfailed) == ['test_maybe.py']
- result.stdout.fnmatch_lines([
- '*2 passed*',
- ])
-
- def test_lastfailed_creates_cache_when_needed(self, testdir):
- # Issue #1342
- testdir.makepyfile(test_empty='')
- testdir.runpytest('-q', '--lf')
- assert not os.path.exists('.cache')
-
- testdir.makepyfile(test_successful='def test_success():\n assert True')
- testdir.runpytest('-q', '--lf')
- assert not os.path.exists('.cache')
-
- testdir.makepyfile(test_errored='def test_error():\n assert False')
- testdir.runpytest('-q', '--lf')
- assert os.path.exists('.cache')
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_capture.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_capture.py
deleted file mode 100644
index 73660692b8e..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_capture.py
+++ /dev/null
@@ -1,1068 +0,0 @@
-# note: py.io capture tests where copied from
-# pylib 1.4.20.dev2 (rev 13d9af95547e)
-from __future__ import with_statement
-import pickle
-import os
-import sys
-
-import _pytest._code
-import py
-import pytest
-import contextlib
-
-from _pytest import capture
-from _pytest.capture import CaptureManager
-from _pytest.main import EXIT_NOTESTSCOLLECTED
-from py.builtin import print_
-
-needsosdup = pytest.mark.xfail("not hasattr(os, 'dup')")
-
-if sys.version_info >= (3, 0):
- def tobytes(obj):
- if isinstance(obj, str):
- obj = obj.encode('UTF-8')
- assert isinstance(obj, bytes)
- return obj
-
- def totext(obj):
- if isinstance(obj, bytes):
- obj = str(obj, 'UTF-8')
- assert isinstance(obj, str)
- return obj
-else:
- def tobytes(obj):
- if isinstance(obj, unicode):
- obj = obj.encode('UTF-8')
- assert isinstance(obj, str)
- return obj
-
- def totext(obj):
- if isinstance(obj, str):
- obj = unicode(obj, 'UTF-8')
- assert isinstance(obj, unicode)
- return obj
-
-
-def oswritebytes(fd, obj):
- os.write(fd, tobytes(obj))
-
-
-
-def StdCaptureFD(out=True, err=True, in_=True):
- return capture.MultiCapture(out, err, in_, Capture=capture.FDCapture)
-
-def StdCapture(out=True, err=True, in_=True):
- return capture.MultiCapture(out, err, in_, Capture=capture.SysCapture)
-
-
-class TestCaptureManager:
- def test_getmethod_default_no_fd(self, monkeypatch):
- from _pytest.capture import pytest_addoption
- from _pytest.config import Parser
- parser = Parser()
- pytest_addoption(parser)
- default = parser._groups[0].options[0].default
- assert default == "fd" if hasattr(os, "dup") else "sys"
- parser = Parser()
- monkeypatch.delattr(os, 'dup', raising=False)
- pytest_addoption(parser)
- assert parser._groups[0].options[0].default == "sys"
-
- @needsosdup
- @pytest.mark.parametrize("method",
- ['no', 'sys', pytest.mark.skipif('not hasattr(os, "dup")', 'fd')])
- def test_capturing_basic_api(self, method):
- capouter = StdCaptureFD()
- old = sys.stdout, sys.stderr, sys.stdin
- try:
- capman = CaptureManager(method)
- capman.init_capturings()
- outerr = capman.suspendcapture()
- assert outerr == ("", "")
- outerr = capman.suspendcapture()
- assert outerr == ("", "")
- print ("hello")
- out, err = capman.suspendcapture()
- if method == "no":
- assert old == (sys.stdout, sys.stderr, sys.stdin)
- else:
- assert not out
- capman.resumecapture()
- print ("hello")
- out, err = capman.suspendcapture()
- if method != "no":
- assert out == "hello\n"
- capman.reset_capturings()
- finally:
- capouter.stop_capturing()
-
- @needsosdup
- def test_init_capturing(self):
- capouter = StdCaptureFD()
- try:
- capman = CaptureManager("fd")
- capman.init_capturings()
- pytest.raises(AssertionError, "capman.init_capturings()")
- capman.reset_capturings()
- finally:
- capouter.stop_capturing()
-
-
-@pytest.mark.parametrize("method", ['fd', 'sys'])
-def test_capturing_unicode(testdir, method):
- if hasattr(sys, "pypy_version_info") and sys.pypy_version_info < (2,2):
- pytest.xfail("does not work on pypy < 2.2")
- if sys.version_info >= (3, 0):
- obj = "'b\u00f6y'"
- else:
- obj = "u'\u00f6y'"
- testdir.makepyfile("""
- # coding=utf8
- # taken from issue 227 from nosetests
- def test_unicode():
- import sys
- print (sys.stdout)
- print (%s)
- """ % obj)
- result = testdir.runpytest("--capture=%s" % method)
- result.stdout.fnmatch_lines([
- "*1 passed*"
- ])
-
-
-@pytest.mark.parametrize("method", ['fd', 'sys'])
-def test_capturing_bytes_in_utf8_encoding(testdir, method):
- testdir.makepyfile("""
- def test_unicode():
- print ('b\\u00f6y')
- """)
- result = testdir.runpytest("--capture=%s" % method)
- result.stdout.fnmatch_lines([
- "*1 passed*"
- ])
-
-
-def test_collect_capturing(testdir):
- p = testdir.makepyfile("""
- print ("collect %s failure" % 13)
- import xyz42123
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*Captured stdout*",
- "*collect 13 failure*",
- ])
-
-
-class TestPerTestCapturing:
- def test_capture_and_fixtures(self, testdir):
- p = testdir.makepyfile("""
- def setup_module(mod):
- print ("setup module")
- def setup_function(function):
- print ("setup " + function.__name__)
- def test_func1():
- print ("in func1")
- assert 0
- def test_func2():
- print ("in func2")
- assert 0
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "setup module*",
- "setup test_func1*",
- "in func1*",
- "setup test_func2*",
- "in func2*",
- ])
-
- @pytest.mark.xfail(reason="unimplemented feature")
- def test_capture_scope_cache(self, testdir):
- p = testdir.makepyfile("""
- import sys
- def setup_module(func):
- print ("module-setup")
- def setup_function(func):
- print ("function-setup")
- def test_func():
- print ("in function")
- assert 0
- def teardown_function(func):
- print ("in teardown")
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*test_func():*",
- "*Captured stdout during setup*",
- "module-setup*",
- "function-setup*",
- "*Captured stdout*",
- "in teardown*",
- ])
-
- def test_no_carry_over(self, testdir):
- p = testdir.makepyfile("""
- def test_func1():
- print ("in func1")
- def test_func2():
- print ("in func2")
- assert 0
- """)
- result = testdir.runpytest(p)
- s = result.stdout.str()
- assert "in func1" not in s
- assert "in func2" in s
-
- def test_teardown_capturing(self, testdir):
- p = testdir.makepyfile("""
- def setup_function(function):
- print ("setup func1")
- def teardown_function(function):
- print ("teardown func1")
- assert 0
- def test_func1():
- print ("in func1")
- pass
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- '*teardown_function*',
- '*Captured stdout*',
- "setup func1*",
- "in func1*",
- "teardown func1*",
- #"*1 fixture failure*"
- ])
-
- def test_teardown_capturing_final(self, testdir):
- p = testdir.makepyfile("""
- def teardown_module(mod):
- print ("teardown module")
- assert 0
- def test_func():
- pass
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*def teardown_module(mod):*",
- "*Captured stdout*",
- "*teardown module*",
- "*1 error*",
- ])
-
- def test_capturing_outerr(self, testdir):
- p1 = testdir.makepyfile("""
- import sys
- def test_capturing():
- print (42)
- sys.stderr.write(str(23))
- def test_capturing_error():
- print (1)
- sys.stderr.write(str(2))
- raise ValueError
- """)
- result = testdir.runpytest(p1)
- result.stdout.fnmatch_lines([
- "*test_capturing_outerr.py .F",
- "====* FAILURES *====",
- "____*____",
- "*test_capturing_outerr.py:8: ValueError",
- "*--- Captured stdout *call*",
- "1",
- "*--- Captured stderr *call*",
- "2",
- ])
-
-
-class TestLoggingInteraction:
- def test_logging_stream_ownership(self, testdir):
- p = testdir.makepyfile("""
- def test_logging():
- import logging
- import pytest
- stream = capture.TextIO()
- logging.basicConfig(stream=stream)
- stream.close() # to free memory/release resources
- """)
- result = testdir.runpytest_subprocess(p)
- result.stderr.str().find("atexit") == -1
-
- def test_logging_and_immediate_setupteardown(self, testdir):
- p = testdir.makepyfile("""
- import logging
- def setup_function(function):
- logging.warn("hello1")
-
- def test_logging():
- logging.warn("hello2")
- assert 0
-
- def teardown_function(function):
- logging.warn("hello3")
- assert 0
- """)
- for optargs in (('--capture=sys',), ('--capture=fd',)):
- print (optargs)
- result = testdir.runpytest_subprocess(p, *optargs)
- s = result.stdout.str()
- result.stdout.fnmatch_lines([
- "*WARN*hello3", # errors show first!
- "*WARN*hello1",
- "*WARN*hello2",
- ])
- # verify proper termination
- assert "closed" not in s
-
- def test_logging_and_crossscope_fixtures(self, testdir):
- p = testdir.makepyfile("""
- import logging
- def setup_module(function):
- logging.warn("hello1")
-
- def test_logging():
- logging.warn("hello2")
- assert 0
-
- def teardown_module(function):
- logging.warn("hello3")
- assert 0
- """)
- for optargs in (('--capture=sys',), ('--capture=fd',)):
- print (optargs)
- result = testdir.runpytest_subprocess(p, *optargs)
- s = result.stdout.str()
- result.stdout.fnmatch_lines([
- "*WARN*hello3", # errors come first
- "*WARN*hello1",
- "*WARN*hello2",
- ])
- # verify proper termination
- assert "closed" not in s
-
- def test_logging_initialized_in_test(self, testdir):
- p = testdir.makepyfile("""
- import sys
- def test_something():
- # pytest does not import logging
- assert 'logging' not in sys.modules
- import logging
- logging.basicConfig()
- logging.warn("hello432")
- assert 0
- """)
- result = testdir.runpytest_subprocess(
- p, "--traceconfig",
- "-p", "no:capturelog")
- assert result.ret != 0
- result.stdout.fnmatch_lines([
- "*hello432*",
- ])
- assert 'operation on closed file' not in result.stderr.str()
-
- def test_conftestlogging_is_shown(self, testdir):
- testdir.makeconftest("""
- import logging
- logging.basicConfig()
- logging.warn("hello435")
- """)
- # make sure that logging is still captured in tests
- result = testdir.runpytest_subprocess("-s", "-p", "no:capturelog")
- assert result.ret == EXIT_NOTESTSCOLLECTED
- result.stderr.fnmatch_lines([
- "WARNING*hello435*",
- ])
- assert 'operation on closed file' not in result.stderr.str()
-
- def test_conftestlogging_and_test_logging(self, testdir):
- testdir.makeconftest("""
- import logging
- logging.basicConfig()
- """)
- # make sure that logging is still captured in tests
- p = testdir.makepyfile("""
- def test_hello():
- import logging
- logging.warn("hello433")
- assert 0
- """)
- result = testdir.runpytest_subprocess(p, "-p", "no:capturelog")
- assert result.ret != 0
- result.stdout.fnmatch_lines([
- "WARNING*hello433*",
- ])
- assert 'something' not in result.stderr.str()
- assert 'operation on closed file' not in result.stderr.str()
-
-
-class TestCaptureFixture:
- @pytest.mark.parametrize("opt", [[], ["-s"]])
- def test_std_functional(self, testdir, opt):
- reprec = testdir.inline_runsource("""
- def test_hello(capsys):
- print (42)
- out, err = capsys.readouterr()
- assert out.startswith("42")
- """, *opt)
- reprec.assertoutcome(passed=1)
-
- def test_capsyscapfd(self, testdir):
- p = testdir.makepyfile("""
- def test_one(capsys, capfd):
- pass
- def test_two(capfd, capsys):
- pass
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*ERROR*setup*test_one*",
- "*capsys*capfd*same*time*",
- "*ERROR*setup*test_two*",
- "*capsys*capfd*same*time*",
- "*2 error*"])
-
- @pytest.mark.parametrize("method", ["sys", "fd"])
- def test_capture_is_represented_on_failure_issue128(self, testdir, method):
- p = testdir.makepyfile("""
- def test_hello(cap%s):
- print ("xxx42xxx")
- assert 0
- """ % method)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "xxx42xxx",
- ])
-
- @needsosdup
- def test_stdfd_functional(self, testdir):
- reprec = testdir.inline_runsource("""
- def test_hello(capfd):
- import os
- os.write(1, "42".encode('ascii'))
- out, err = capfd.readouterr()
- assert out.startswith("42")
- capfd.close()
- """)
- reprec.assertoutcome(passed=1)
-
- def test_partial_setup_failure(self, testdir):
- p = testdir.makepyfile("""
- def test_hello(capsys, missingarg):
- pass
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*test_partial_setup_failure*",
- "*1 error*",
- ])
-
- @needsosdup
- def test_keyboardinterrupt_disables_capturing(self, testdir):
- p = testdir.makepyfile("""
- def test_hello(capfd):
- import os
- os.write(1, str(42).encode('ascii'))
- raise KeyboardInterrupt()
- """)
- result = testdir.runpytest_subprocess(p)
- result.stdout.fnmatch_lines([
- "*KeyboardInterrupt*"
- ])
- assert result.ret == 2
-
- @pytest.mark.issue14
- def test_capture_and_logging(self, testdir):
- p = testdir.makepyfile("""
- import logging
- def test_log(capsys):
- logging.error('x')
- """)
- result = testdir.runpytest_subprocess(p)
- assert 'closed' not in result.stderr.str()
-
-
-def test_setup_failure_does_not_kill_capturing(testdir):
- sub1 = testdir.mkpydir("sub1")
- sub1.join("conftest.py").write(_pytest._code.Source("""
- def pytest_runtest_setup(item):
- raise ValueError(42)
- """))
- sub1.join("test_mod.py").write("def test_func1(): pass")
- result = testdir.runpytest(testdir.tmpdir, '--traceconfig')
- result.stdout.fnmatch_lines([
- "*ValueError(42)*",
- "*1 error*"
- ])
-
-
-def test_fdfuncarg_skips_on_no_osdup(testdir):
- testdir.makepyfile("""
- import os
- if hasattr(os, 'dup'):
- del os.dup
- def test_hello(capfd):
- pass
- """)
- result = testdir.runpytest_subprocess("--capture=no")
- result.stdout.fnmatch_lines([
- "*1 skipped*"
- ])
-
-
-def test_capture_conftest_runtest_setup(testdir):
- testdir.makeconftest("""
- def pytest_runtest_setup():
- print ("hello19")
- """)
- testdir.makepyfile("def test_func(): pass")
- result = testdir.runpytest()
- assert result.ret == 0
- assert 'hello19' not in result.stdout.str()
-
-
-def test_capture_badoutput_issue412(testdir):
- testdir.makepyfile("""
- import os
-
- def test_func():
- omg = bytearray([1,129,1])
- os.write(1, omg)
- assert 0
- """)
- result = testdir.runpytest('--cap=fd')
- result.stdout.fnmatch_lines('''
- *def test_func*
- *assert 0*
- *Captured*
- *1 failed*
- ''')
-
-
-def test_capture_early_option_parsing(testdir):
- testdir.makeconftest("""
- def pytest_runtest_setup():
- print ("hello19")
- """)
- testdir.makepyfile("def test_func(): pass")
- result = testdir.runpytest("-vs")
- assert result.ret == 0
- assert 'hello19' in result.stdout.str()
-
-
-def test_capture_binary_output(testdir):
- testdir.makepyfile(r"""
- import pytest
-
- def test_a():
- import sys
- import subprocess
- subprocess.call([sys.executable, __file__])
-
- def test_foo():
- import os;os.write(1, b'\xc3')
-
- if __name__ == '__main__':
- test_foo()
- """)
- result = testdir.runpytest('--assert=plain')
- result.assert_outcomes(passed=2)
-
-
-def test_error_during_readouterr(testdir):
- """Make sure we suspend capturing if errors occurr during readouterr"""
- testdir.makepyfile(pytest_xyz="""
- from _pytest.capture import FDCapture
- def bad_snap(self):
- raise Exception('boom')
- assert FDCapture.snap
- FDCapture.snap = bad_snap
- """)
- result = testdir.runpytest_subprocess(
- "-p", "pytest_xyz", "--version", syspathinsert=True
- )
- result.stderr.fnmatch_lines([
- "*in bad_snap",
- " raise Exception('boom')",
- "Exception: boom",
- ])
-
-
-class TestTextIO:
- def test_text(self):
- f = capture.TextIO()
- f.write("hello")
- s = f.getvalue()
- assert s == "hello"
- f.close()
-
- def test_unicode_and_str_mixture(self):
- f = capture.TextIO()
- if sys.version_info >= (3, 0):
- f.write("\u00f6")
- pytest.raises(TypeError, "f.write(bytes('hello', 'UTF-8'))")
- else:
- f.write(unicode("\u00f6", 'UTF-8'))
- f.write("hello") # bytes
- s = f.getvalue()
- f.close()
- assert isinstance(s, unicode)
-
-
-def test_bytes_io():
- f = py.io.BytesIO()
- f.write(tobytes("hello"))
- pytest.raises(TypeError, "f.write(totext('hello'))")
- s = f.getvalue()
- assert s == tobytes("hello")
-
-
-def test_dontreadfrominput():
- from _pytest.capture import DontReadFromInput
- f = DontReadFromInput()
- assert not f.isatty()
- pytest.raises(IOError, f.read)
- pytest.raises(IOError, f.readlines)
- pytest.raises(IOError, iter, f)
- pytest.raises(ValueError, f.fileno)
- f.close() # just for completeness
-
-
-@pytest.yield_fixture
-def tmpfile(testdir):
- f = testdir.makepyfile("").open('wb+')
- yield f
- if not f.closed:
- f.close()
-
-@needsosdup
-def test_dupfile(tmpfile):
- flist = []
- for i in range(5):
- nf = capture.safe_text_dupfile(tmpfile, "wb")
- assert nf != tmpfile
- assert nf.fileno() != tmpfile.fileno()
- assert nf not in flist
- print_(i, end="", file=nf)
- flist.append(nf)
- for i in range(5):
- f = flist[i]
- f.close()
- tmpfile.seek(0)
- s = tmpfile.read()
- assert "01234" in repr(s)
- tmpfile.close()
-
-def test_dupfile_on_bytesio():
- io = py.io.BytesIO()
- f = capture.safe_text_dupfile(io, "wb")
- f.write("hello")
- assert io.getvalue() == b"hello"
-
-def test_dupfile_on_textio():
- io = py.io.TextIO()
- f = capture.safe_text_dupfile(io, "wb")
- f.write("hello")
- assert io.getvalue() == "hello"
-
-
-@contextlib.contextmanager
-def lsof_check():
- pid = os.getpid()
- try:
- out = py.process.cmdexec("lsof -p %d" % pid)
- except (py.process.cmdexec.Error, UnicodeDecodeError):
- # about UnicodeDecodeError, see note on pytester
- pytest.skip("could not run 'lsof'")
- yield
- out2 = py.process.cmdexec("lsof -p %d" % pid)
- len1 = len([x for x in out.split("\n") if "REG" in x])
- len2 = len([x for x in out2.split("\n") if "REG" in x])
- assert len2 < len1 + 3, out2
-
-
-class TestFDCapture:
- pytestmark = needsosdup
-
- def test_simple(self, tmpfile):
- fd = tmpfile.fileno()
- cap = capture.FDCapture(fd)
- data = tobytes("hello")
- os.write(fd, data)
- s = cap.snap()
- cap.done()
- assert not s
- cap = capture.FDCapture(fd)
- cap.start()
- os.write(fd, data)
- s = cap.snap()
- cap.done()
- assert s == "hello"
-
- def test_simple_many(self, tmpfile):
- for i in range(10):
- self.test_simple(tmpfile)
-
- def test_simple_many_check_open_files(self, testdir):
- with lsof_check():
- with testdir.makepyfile("").open('wb+') as tmpfile:
- self.test_simple_many(tmpfile)
-
- def test_simple_fail_second_start(self, tmpfile):
- fd = tmpfile.fileno()
- cap = capture.FDCapture(fd)
- cap.done()
- pytest.raises(ValueError, cap.start)
-
- def test_stderr(self):
- cap = capture.FDCapture(2)
- cap.start()
- print_("hello", file=sys.stderr)
- s = cap.snap()
- cap.done()
- assert s == "hello\n"
-
- def test_stdin(self, tmpfile):
- cap = capture.FDCapture(0)
- cap.start()
- x = os.read(0, 100).strip()
- cap.done()
- assert x == tobytes('')
-
- def test_writeorg(self, tmpfile):
- data1, data2 = tobytes("foo"), tobytes("bar")
- cap = capture.FDCapture(tmpfile.fileno())
- cap.start()
- tmpfile.write(data1)
- tmpfile.flush()
- cap.writeorg(data2)
- scap = cap.snap()
- cap.done()
- assert scap == totext(data1)
- with open(tmpfile.name, 'rb') as stmp_file:
- stmp = stmp_file.read()
- assert stmp == data2
-
- def test_simple_resume_suspend(self, tmpfile):
- with saved_fd(1):
- cap = capture.FDCapture(1)
- cap.start()
- data = tobytes("hello")
- os.write(1, data)
- sys.stdout.write("whatever")
- s = cap.snap()
- assert s == "hellowhatever"
- cap.suspend()
- os.write(1, tobytes("world"))
- sys.stdout.write("qlwkej")
- assert not cap.snap()
- cap.resume()
- os.write(1, tobytes("but now"))
- sys.stdout.write(" yes\n")
- s = cap.snap()
- assert s == "but now yes\n"
- cap.suspend()
- cap.done()
- pytest.raises(AttributeError, cap.suspend)
-
-
-@contextlib.contextmanager
-def saved_fd(fd):
- new_fd = os.dup(fd)
- try:
- yield
- finally:
- os.dup2(new_fd, fd)
- os.close(new_fd)
-
-
-class TestStdCapture:
- captureclass = staticmethod(StdCapture)
-
- @contextlib.contextmanager
- def getcapture(self, **kw):
- cap = self.__class__.captureclass(**kw)
- cap.start_capturing()
- try:
- yield cap
- finally:
- cap.stop_capturing()
-
- def test_capturing_done_simple(self):
- with self.getcapture() as cap:
- sys.stdout.write("hello")
- sys.stderr.write("world")
- out, err = cap.readouterr()
- assert out == "hello"
- assert err == "world"
-
- def test_capturing_reset_simple(self):
- with self.getcapture() as cap:
- print("hello world")
- sys.stderr.write("hello error\n")
- out, err = cap.readouterr()
- assert out == "hello world\n"
- assert err == "hello error\n"
-
- def test_capturing_readouterr(self):
- with self.getcapture() as cap:
- print ("hello world")
- sys.stderr.write("hello error\n")
- out, err = cap.readouterr()
- assert out == "hello world\n"
- assert err == "hello error\n"
- sys.stderr.write("error2")
- out, err = cap.readouterr()
- assert err == "error2"
-
- def test_capturing_readouterr_unicode(self):
- with self.getcapture() as cap:
- print ("hx\xc4\x85\xc4\x87")
- out, err = cap.readouterr()
- assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8")
-
- @pytest.mark.skipif('sys.version_info >= (3,)',
- reason='text output different for bytes on python3')
- def test_capturing_readouterr_decode_error_handling(self):
- with self.getcapture() as cap:
- # triggered a internal error in pytest
- print('\xa6')
- out, err = cap.readouterr()
- assert out == py.builtin._totext('\ufffd\n', 'unicode-escape')
-
- def test_reset_twice_error(self):
- with self.getcapture() as cap:
- print ("hello")
- out, err = cap.readouterr()
- pytest.raises(ValueError, cap.stop_capturing)
- assert out == "hello\n"
- assert not err
-
- def test_capturing_modify_sysouterr_in_between(self):
- oldout = sys.stdout
- olderr = sys.stderr
- with self.getcapture() as cap:
- sys.stdout.write("hello")
- sys.stderr.write("world")
- sys.stdout = capture.TextIO()
- sys.stderr = capture.TextIO()
- print ("not seen")
- sys.stderr.write("not seen\n")
- out, err = cap.readouterr()
- assert out == "hello"
- assert err == "world"
- assert sys.stdout == oldout
- assert sys.stderr == olderr
-
- def test_capturing_error_recursive(self):
- with self.getcapture() as cap1:
- print ("cap1")
- with self.getcapture() as cap2:
- print ("cap2")
- out2, err2 = cap2.readouterr()
- out1, err1 = cap1.readouterr()
- assert out1 == "cap1\n"
- assert out2 == "cap2\n"
-
- def test_just_out_capture(self):
- with self.getcapture(out=True, err=False) as cap:
- sys.stdout.write("hello")
- sys.stderr.write("world")
- out, err = cap.readouterr()
- assert out == "hello"
- assert not err
-
- def test_just_err_capture(self):
- with self.getcapture(out=False, err=True) as cap:
- sys.stdout.write("hello")
- sys.stderr.write("world")
- out, err = cap.readouterr()
- assert err == "world"
- assert not out
-
- def test_stdin_restored(self):
- old = sys.stdin
- with self.getcapture(in_=True):
- newstdin = sys.stdin
- assert newstdin != sys.stdin
- assert sys.stdin is old
-
- def test_stdin_nulled_by_default(self):
- print ("XXX this test may well hang instead of crashing")
- print ("XXX which indicates an error in the underlying capturing")
- print ("XXX mechanisms")
- with self.getcapture():
- pytest.raises(IOError, "sys.stdin.read()")
-
-
-class TestStdCaptureFD(TestStdCapture):
- pytestmark = needsosdup
- captureclass = staticmethod(StdCaptureFD)
-
- def test_simple_only_fd(self, testdir):
- testdir.makepyfile("""
- import os
- def test_x():
- os.write(1, "hello\\n".encode("ascii"))
- assert 0
- """)
- result = testdir.runpytest_subprocess()
- result.stdout.fnmatch_lines("""
- *test_x*
- *assert 0*
- *Captured stdout*
- """)
-
- def test_intermingling(self):
- with self.getcapture() as cap:
- oswritebytes(1, "1")
- sys.stdout.write(str(2))
- sys.stdout.flush()
- oswritebytes(1, "3")
- oswritebytes(2, "a")
- sys.stderr.write("b")
- sys.stderr.flush()
- oswritebytes(2, "c")
- out, err = cap.readouterr()
- assert out == "123"
- assert err == "abc"
-
- def test_many(self, capfd):
- with lsof_check():
- for i in range(10):
- cap = StdCaptureFD()
- cap.stop_capturing()
-
-
-class TestStdCaptureFDinvalidFD:
- pytestmark = needsosdup
-
- def test_stdcapture_fd_invalid_fd(self, testdir):
- testdir.makepyfile("""
- import os
- from _pytest import capture
- def StdCaptureFD(out=True, err=True, in_=True):
- return capture.MultiCapture(out, err, in_,
- Capture=capture.FDCapture)
- def test_stdout():
- os.close(1)
- cap = StdCaptureFD(out=True, err=False, in_=False)
- cap.stop_capturing()
- def test_stderr():
- os.close(2)
- cap = StdCaptureFD(out=False, err=True, in_=False)
- cap.stop_capturing()
- def test_stdin():
- os.close(0)
- cap = StdCaptureFD(out=False, err=False, in_=True)
- cap.stop_capturing()
- """)
- result = testdir.runpytest_subprocess("--capture=fd")
- assert result.ret == 0
- assert result.parseoutcomes()['passed'] == 3
-
-
-def test_capture_not_started_but_reset():
- capsys = StdCapture()
- capsys.stop_capturing()
-
-
-@needsosdup
-@pytest.mark.parametrize('use', [True, False])
-def test_fdcapture_tmpfile_remains_the_same(tmpfile, use):
- if not use:
- tmpfile = True
- cap = StdCaptureFD(out=False, err=tmpfile)
- try:
- cap.start_capturing()
- capfile = cap.err.tmpfile
- cap.readouterr()
- finally:
- cap.stop_capturing()
- capfile2 = cap.err.tmpfile
- assert capfile2 == capfile
-
-@needsosdup
-def test_close_and_capture_again(testdir):
- testdir.makepyfile("""
- import os
- def test_close():
- os.close(1)
- def test_capture_again():
- os.write(1, b"hello\\n")
- assert 0
- """)
- result = testdir.runpytest_subprocess()
- result.stdout.fnmatch_lines("""
- *test_capture_again*
- *assert 0*
- *stdout*
- *hello*
- """)
-
-
-
-@pytest.mark.parametrize('method', ['SysCapture', 'FDCapture'])
-def test_capturing_and_logging_fundamentals(testdir, method):
- if method == "StdCaptureFD" and not hasattr(os, 'dup'):
- pytest.skip("need os.dup")
- # here we check a fundamental feature
- p = testdir.makepyfile("""
- import sys, os
- import py, logging
- from _pytest import capture
- cap = capture.MultiCapture(out=False, in_=False,
- Capture=capture.%s)
- cap.start_capturing()
-
- logging.warn("hello1")
- outerr = cap.readouterr()
- print ("suspend, captured %%s" %%(outerr,))
- logging.warn("hello2")
-
- cap.pop_outerr_to_orig()
- logging.warn("hello3")
-
- outerr = cap.readouterr()
- print ("suspend2, captured %%s" %% (outerr,))
- """ % (method,))
- result = testdir.runpython(p)
- result.stdout.fnmatch_lines("""
- suspend, captured*hello1*
- suspend2, captured*WARNING:root:hello3*
- """)
- result.stderr.fnmatch_lines("""
- WARNING:root:hello2
- """)
- assert "atexit" not in result.stderr.str()
-
-
-def test_error_attribute_issue555(testdir):
- testdir.makepyfile("""
- import sys
- def test_capattr():
- assert sys.stdout.errors == "strict"
- assert sys.stderr.errors == "strict"
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
-
-def test_dontreadfrominput_has_encoding(testdir):
- testdir.makepyfile("""
- import sys
- def test_capattr():
- # should not raise AttributeError
- assert sys.stdout.encoding
- assert sys.stderr.encoding
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
-
-def test_pickling_and_unpickling_enocded_file():
- # See https://bitbucket.org/pytest-dev/pytest/pull-request/194
- # pickle.loads() raises infinite recursion if
- # EncodedFile.__getattr__ is not implemented properly
- ef = capture.EncodedFile(None, None)
- ef_as_str = pickle.dumps(ef)
- pickle.loads(ef_as_str)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_collection.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_collection.py
deleted file mode 100644
index 749c5b7ce45..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_collection.py
+++ /dev/null
@@ -1,641 +0,0 @@
-import pytest, py
-
-from _pytest.main import Session, EXIT_NOTESTSCOLLECTED
-
-class TestCollector:
- def test_collect_versus_item(self):
- from pytest import Collector, Item
- assert not issubclass(Collector, Item)
- assert not issubclass(Item, Collector)
-
- def test_compat_attributes(self, testdir, recwarn):
- modcol = testdir.getmodulecol("""
- def test_pass(): pass
- def test_fail(): assert 0
- """)
- recwarn.clear()
- assert modcol.Module == pytest.Module
- assert modcol.Class == pytest.Class
- assert modcol.Item == pytest.Item
- assert modcol.File == pytest.File
- assert modcol.Function == pytest.Function
-
- def test_check_equality(self, testdir):
- modcol = testdir.getmodulecol("""
- def test_pass(): pass
- def test_fail(): assert 0
- """)
- fn1 = testdir.collect_by_name(modcol, "test_pass")
- assert isinstance(fn1, pytest.Function)
- fn2 = testdir.collect_by_name(modcol, "test_pass")
- assert isinstance(fn2, pytest.Function)
-
- assert fn1 == fn2
- assert fn1 != modcol
- if py.std.sys.version_info < (3, 0):
- assert cmp(fn1, fn2) == 0
- assert hash(fn1) == hash(fn2)
-
- fn3 = testdir.collect_by_name(modcol, "test_fail")
- assert isinstance(fn3, pytest.Function)
- assert not (fn1 == fn3)
- assert fn1 != fn3
-
- for fn in fn1,fn2,fn3:
- assert fn != 3
- assert fn != modcol
- assert fn != [1,2,3]
- assert [1,2,3] != fn
- assert modcol != fn
-
- def test_getparent(self, testdir):
- modcol = testdir.getmodulecol("""
- class TestClass:
- def test_foo():
- pass
- """)
- cls = testdir.collect_by_name(modcol, "TestClass")
- fn = testdir.collect_by_name(
- testdir.collect_by_name(cls, "()"), "test_foo")
-
- parent = fn.getparent(pytest.Module)
- assert parent is modcol
-
- parent = fn.getparent(pytest.Function)
- assert parent is fn
-
- parent = fn.getparent(pytest.Class)
- assert parent is cls
-
-
- def test_getcustomfile_roundtrip(self, testdir):
- hello = testdir.makefile(".xxx", hello="world")
- testdir.makepyfile(conftest="""
- import pytest
- class CustomFile(pytest.File):
- pass
- def pytest_collect_file(path, parent):
- if path.ext == ".xxx":
- return CustomFile(path, parent=parent)
- """)
- node = testdir.getpathnode(hello)
- assert isinstance(node, pytest.File)
- assert node.name == "hello.xxx"
- nodes = node.session.perform_collect([node.nodeid], genitems=False)
- assert len(nodes) == 1
- assert isinstance(nodes[0], pytest.File)
-
-class TestCollectFS:
- def test_ignored_certain_directories(self, testdir):
- tmpdir = testdir.tmpdir
- tmpdir.ensure("_darcs", 'test_notfound.py')
- tmpdir.ensure("CVS", 'test_notfound.py')
- tmpdir.ensure("{arch}", 'test_notfound.py')
- tmpdir.ensure(".whatever", 'test_notfound.py')
- tmpdir.ensure(".bzr", 'test_notfound.py')
- tmpdir.ensure("normal", 'test_found.py')
- for x in tmpdir.visit("test_*.py"):
- x.write("def test_hello(): pass")
-
- result = testdir.runpytest("--collect-only")
- s = result.stdout.str()
- assert "test_notfound" not in s
- assert "test_found" in s
-
- def test_custom_norecursedirs(self, testdir):
- testdir.makeini("""
- [pytest]
- norecursedirs = mydir xyz*
- """)
- tmpdir = testdir.tmpdir
- tmpdir.ensure("mydir", "test_hello.py").write("def test_1(): pass")
- tmpdir.ensure("xyz123", "test_2.py").write("def test_2(): 0/0")
- tmpdir.ensure("xy", "test_ok.py").write("def test_3(): pass")
- rec = testdir.inline_run()
- rec.assertoutcome(passed=1)
- rec = testdir.inline_run("xyz123/test_2.py")
- rec.assertoutcome(failed=1)
-
- def test_testpaths_ini(self, testdir, monkeypatch):
- testdir.makeini("""
- [pytest]
- testpaths = gui uts
- """)
- tmpdir = testdir.tmpdir
- tmpdir.ensure("env", "test_1.py").write("def test_env(): pass")
- tmpdir.ensure("gui", "test_2.py").write("def test_gui(): pass")
- tmpdir.ensure("uts", "test_3.py").write("def test_uts(): pass")
-
- # executing from rootdir only tests from `testpaths` directories
- # are collected
- items, reprec = testdir.inline_genitems('-v')
- assert [x.name for x in items] == ['test_gui', 'test_uts']
-
- # check that explicitly passing directories in the command-line
- # collects the tests
- for dirname in ('env', 'gui', 'uts'):
- items, reprec = testdir.inline_genitems(tmpdir.join(dirname))
- assert [x.name for x in items] == ['test_%s' % dirname]
-
- # changing cwd to each subdirectory and running pytest without
- # arguments collects the tests in that directory normally
- for dirname in ('env', 'gui', 'uts'):
- monkeypatch.chdir(testdir.tmpdir.join(dirname))
- items, reprec = testdir.inline_genitems()
- assert [x.name for x in items] == ['test_%s' % dirname]
-
-
-class TestCollectPluginHookRelay:
- def test_pytest_collect_file(self, testdir):
- wascalled = []
- class Plugin:
- def pytest_collect_file(self, path, parent):
- wascalled.append(path)
- testdir.makefile(".abc", "xyz")
- pytest.main([testdir.tmpdir], plugins=[Plugin()])
- assert len(wascalled) == 1
- assert wascalled[0].ext == '.abc'
-
- def test_pytest_collect_directory(self, testdir):
- wascalled = []
- class Plugin:
- def pytest_collect_directory(self, path, parent):
- wascalled.append(path.basename)
- testdir.mkdir("hello")
- testdir.mkdir("world")
- pytest.main(testdir.tmpdir, plugins=[Plugin()])
- assert "hello" in wascalled
- assert "world" in wascalled
-
-class TestPrunetraceback:
- def test_collection_error(self, testdir):
- p = testdir.makepyfile("""
- import not_exists
- """)
- result = testdir.runpytest(p)
- assert "__import__" not in result.stdout.str(), "too long traceback"
- result.stdout.fnmatch_lines([
- "*ERROR collecting*",
- "*mport*not_exists*"
- ])
-
- def test_custom_repr_failure(self, testdir):
- p = testdir.makepyfile("""
- import not_exists
- """)
- testdir.makeconftest("""
- import pytest
- def pytest_collect_file(path, parent):
- return MyFile(path, parent)
- class MyError(Exception):
- pass
- class MyFile(pytest.File):
- def collect(self):
- raise MyError()
- def repr_failure(self, excinfo):
- if excinfo.errisinstance(MyError):
- return "hello world"
- return pytest.File.repr_failure(self, excinfo)
- """)
-
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*ERROR collecting*",
- "*hello world*",
- ])
-
- @pytest.mark.xfail(reason="other mechanism for adding to reporting needed")
- def test_collect_report_postprocessing(self, testdir):
- p = testdir.makepyfile("""
- import not_exists
- """)
- testdir.makeconftest("""
- import pytest
- def pytest_make_collect_report(__multicall__):
- rep = __multicall__.execute()
- rep.headerlines += ["header1"]
- return rep
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*ERROR collecting*",
- "*header1*",
- ])
-
-
-class TestCustomConftests:
- def test_ignore_collect_path(self, testdir):
- testdir.makeconftest("""
- def pytest_ignore_collect(path, config):
- return path.basename.startswith("x") or \
- path.basename == "test_one.py"
- """)
- sub = testdir.mkdir("xy123")
- sub.ensure("test_hello.py").write("syntax error")
- sub.join("conftest.py").write("syntax error")
- testdir.makepyfile("def test_hello(): pass")
- testdir.makepyfile(test_one="syntax error")
- result = testdir.runpytest("--fulltrace")
- assert result.ret == 0
- result.stdout.fnmatch_lines(["*1 passed*"])
-
- def test_ignore_collect_not_called_on_argument(self, testdir):
- testdir.makeconftest("""
- def pytest_ignore_collect(path, config):
- return True
- """)
- p = testdir.makepyfile("def test_hello(): pass")
- result = testdir.runpytest(p)
- assert result.ret == 0
- result.stdout.fnmatch_lines("*1 passed*")
- result = testdir.runpytest()
- assert result.ret == EXIT_NOTESTSCOLLECTED
- result.stdout.fnmatch_lines("*collected 0 items*")
-
- def test_collectignore_exclude_on_option(self, testdir):
- testdir.makeconftest("""
- collect_ignore = ['hello', 'test_world.py']
- def pytest_addoption(parser):
- parser.addoption("--XX", action="store_true", default=False)
- def pytest_configure(config):
- if config.getvalue("XX"):
- collect_ignore[:] = []
- """)
- testdir.mkdir("hello")
- testdir.makepyfile(test_world="def test_hello(): pass")
- result = testdir.runpytest()
- assert result.ret == EXIT_NOTESTSCOLLECTED
- assert "passed" not in result.stdout.str()
- result = testdir.runpytest("--XX")
- assert result.ret == 0
- assert "passed" in result.stdout.str()
-
- def test_pytest_fs_collect_hooks_are_seen(self, testdir):
- testdir.makeconftest("""
- import pytest
- class MyModule(pytest.Module):
- pass
- def pytest_collect_file(path, parent):
- if path.ext == ".py":
- return MyModule(path, parent)
- """)
- testdir.mkdir("sub")
- testdir.makepyfile("def test_x(): pass")
- result = testdir.runpytest("--collect-only")
- result.stdout.fnmatch_lines([
- "*MyModule*",
- "*test_x*"
- ])
-
- def test_pytest_collect_file_from_sister_dir(self, testdir):
- sub1 = testdir.mkpydir("sub1")
- sub2 = testdir.mkpydir("sub2")
- conf1 = testdir.makeconftest("""
- import pytest
- class MyModule1(pytest.Module):
- pass
- def pytest_collect_file(path, parent):
- if path.ext == ".py":
- return MyModule1(path, parent)
- """)
- conf1.move(sub1.join(conf1.basename))
- conf2 = testdir.makeconftest("""
- import pytest
- class MyModule2(pytest.Module):
- pass
- def pytest_collect_file(path, parent):
- if path.ext == ".py":
- return MyModule2(path, parent)
- """)
- conf2.move(sub2.join(conf2.basename))
- p = testdir.makepyfile("def test_x(): pass")
- p.copy(sub1.join(p.basename))
- p.copy(sub2.join(p.basename))
- result = testdir.runpytest("--collect-only")
- result.stdout.fnmatch_lines([
- "*MyModule1*",
- "*MyModule2*",
- "*test_x*"
- ])
-
-class TestSession:
- def test_parsearg(self, testdir):
- p = testdir.makepyfile("def test_func(): pass")
- subdir = testdir.mkdir("sub")
- subdir.ensure("__init__.py")
- target = subdir.join(p.basename)
- p.move(target)
- subdir.chdir()
- config = testdir.parseconfig(p.basename)
- rcol = Session(config=config)
- assert rcol.fspath == subdir
- parts = rcol._parsearg(p.basename)
-
- assert parts[0] == target
- assert len(parts) == 1
- parts = rcol._parsearg(p.basename + "::test_func")
- assert parts[0] == target
- assert parts[1] == "test_func"
- assert len(parts) == 2
-
- def test_collect_topdir(self, testdir):
- p = testdir.makepyfile("def test_func(): pass")
- id = "::".join([p.basename, "test_func"])
- # XXX migrate to collectonly? (see below)
- config = testdir.parseconfig(id)
- topdir = testdir.tmpdir
- rcol = Session(config)
- assert topdir == rcol.fspath
- #rootid = rcol.nodeid
- #root2 = rcol.perform_collect([rcol.nodeid], genitems=False)[0]
- #assert root2 == rcol, rootid
- colitems = rcol.perform_collect([rcol.nodeid], genitems=False)
- assert len(colitems) == 1
- assert colitems[0].fspath == p
-
-
- def test_collect_protocol_single_function(self, testdir):
- p = testdir.makepyfile("def test_func(): pass")
- id = "::".join([p.basename, "test_func"])
- items, hookrec = testdir.inline_genitems(id)
- item, = items
- assert item.name == "test_func"
- newid = item.nodeid
- assert newid == id
- py.std.pprint.pprint(hookrec.calls)
- topdir = testdir.tmpdir # noqa
- hookrec.assert_contains([
- ("pytest_collectstart", "collector.fspath == topdir"),
- ("pytest_make_collect_report", "collector.fspath == topdir"),
- ("pytest_collectstart", "collector.fspath == p"),
- ("pytest_make_collect_report", "collector.fspath == p"),
- ("pytest_pycollect_makeitem", "name == 'test_func'"),
- ("pytest_collectreport", "report.nodeid.startswith(p.basename)"),
- ("pytest_collectreport", "report.nodeid == ''")
- ])
-
- def test_collect_protocol_method(self, testdir):
- p = testdir.makepyfile("""
- class TestClass:
- def test_method(self):
- pass
- """)
- normid = p.basename + "::TestClass::()::test_method"
- for id in [p.basename,
- p.basename + "::TestClass",
- p.basename + "::TestClass::()",
- normid,
- ]:
- items, hookrec = testdir.inline_genitems(id)
- assert len(items) == 1
- assert items[0].name == "test_method"
- newid = items[0].nodeid
- assert newid == normid
-
- def test_collect_custom_nodes_multi_id(self, testdir):
- p = testdir.makepyfile("def test_func(): pass")
- testdir.makeconftest("""
- import pytest
- class SpecialItem(pytest.Item):
- def runtest(self):
- return # ok
- class SpecialFile(pytest.File):
- def collect(self):
- return [SpecialItem(name="check", parent=self)]
- def pytest_collect_file(path, parent):
- if path.basename == %r:
- return SpecialFile(fspath=path, parent=parent)
- """ % p.basename)
- id = p.basename
-
- items, hookrec = testdir.inline_genitems(id)
- py.std.pprint.pprint(hookrec.calls)
- assert len(items) == 2
- hookrec.assert_contains([
- ("pytest_collectstart",
- "collector.fspath == collector.session.fspath"),
- ("pytest_collectstart",
- "collector.__class__.__name__ == 'SpecialFile'"),
- ("pytest_collectstart",
- "collector.__class__.__name__ == 'Module'"),
- ("pytest_pycollect_makeitem", "name == 'test_func'"),
- ("pytest_collectreport", "report.nodeid.startswith(p.basename)"),
- #("pytest_collectreport",
- # "report.fspath == %r" % str(rcol.fspath)),
- ])
-
- def test_collect_subdir_event_ordering(self, testdir):
- p = testdir.makepyfile("def test_func(): pass")
- aaa = testdir.mkpydir("aaa")
- test_aaa = aaa.join("test_aaa.py")
- p.move(test_aaa)
-
- items, hookrec = testdir.inline_genitems()
- assert len(items) == 1
- py.std.pprint.pprint(hookrec.calls)
- hookrec.assert_contains([
- ("pytest_collectstart", "collector.fspath == test_aaa"),
- ("pytest_pycollect_makeitem", "name == 'test_func'"),
- ("pytest_collectreport",
- "report.nodeid.startswith('aaa/test_aaa.py')"),
- ])
-
- def test_collect_two_commandline_args(self, testdir):
- p = testdir.makepyfile("def test_func(): pass")
- aaa = testdir.mkpydir("aaa")
- bbb = testdir.mkpydir("bbb")
- test_aaa = aaa.join("test_aaa.py")
- p.copy(test_aaa)
- test_bbb = bbb.join("test_bbb.py")
- p.move(test_bbb)
-
- id = "."
-
- items, hookrec = testdir.inline_genitems(id)
- assert len(items) == 2
- py.std.pprint.pprint(hookrec.calls)
- hookrec.assert_contains([
- ("pytest_collectstart", "collector.fspath == test_aaa"),
- ("pytest_pycollect_makeitem", "name == 'test_func'"),
- ("pytest_collectreport", "report.nodeid == 'aaa/test_aaa.py'"),
- ("pytest_collectstart", "collector.fspath == test_bbb"),
- ("pytest_pycollect_makeitem", "name == 'test_func'"),
- ("pytest_collectreport", "report.nodeid == 'bbb/test_bbb.py'"),
- ])
-
- def test_serialization_byid(self, testdir):
- testdir.makepyfile("def test_func(): pass")
- items, hookrec = testdir.inline_genitems()
- assert len(items) == 1
- item, = items
- items2, hookrec = testdir.inline_genitems(item.nodeid)
- item2, = items2
- assert item2.name == item.name
- assert item2.fspath == item.fspath
-
- def test_find_byid_without_instance_parents(self, testdir):
- p = testdir.makepyfile("""
- class TestClass:
- def test_method(self):
- pass
- """)
- arg = p.basename + ("::TestClass::test_method")
- items, hookrec = testdir.inline_genitems(arg)
- assert len(items) == 1
- item, = items
- assert item.nodeid.endswith("TestClass::()::test_method")
-
-class Test_getinitialnodes:
- def test_global_file(self, testdir, tmpdir):
- x = tmpdir.ensure("x.py")
- config = testdir.parseconfigure(x)
- col = testdir.getnode(config, x)
- assert isinstance(col, pytest.Module)
- assert col.name == 'x.py'
- assert col.parent.name == testdir.tmpdir.basename
- assert col.parent.parent is None
- for col in col.listchain():
- assert col.config is config
-
- def test_pkgfile(self, testdir):
- tmpdir = testdir.tmpdir
- subdir = tmpdir.join("subdir")
- x = subdir.ensure("x.py")
- subdir.ensure("__init__.py")
- config = testdir.parseconfigure(x)
- col = testdir.getnode(config, x)
- assert isinstance(col, pytest.Module)
- assert col.name == 'x.py'
- assert col.parent.parent is None
- for col in col.listchain():
- assert col.config is config
-
-class Test_genitems:
- def test_check_collect_hashes(self, testdir):
- p = testdir.makepyfile("""
- def test_1():
- pass
-
- def test_2():
- pass
- """)
- p.copy(p.dirpath(p.purebasename + "2" + ".py"))
- items, reprec = testdir.inline_genitems(p.dirpath())
- assert len(items) == 4
- for numi, i in enumerate(items):
- for numj, j in enumerate(items):
- if numj != numi:
- assert hash(i) != hash(j)
- assert i != j
-
- def test_example_items1(self, testdir):
- p = testdir.makepyfile('''
- def testone():
- pass
-
- class TestX:
- def testmethod_one(self):
- pass
-
- class TestY(TestX):
- pass
- ''')
- items, reprec = testdir.inline_genitems(p)
- assert len(items) == 3
- assert items[0].name == 'testone'
- assert items[1].name == 'testmethod_one'
- assert items[2].name == 'testmethod_one'
-
- # let's also test getmodpath here
- assert items[0].getmodpath() == "testone"
- assert items[1].getmodpath() == "TestX.testmethod_one"
- assert items[2].getmodpath() == "TestY.testmethod_one"
-
- s = items[0].getmodpath(stopatmodule=False)
- assert s.endswith("test_example_items1.testone")
- print(s)
-
- def test_class_and_functions_discovery_using_glob(self, testdir):
- """
- tests that python_classes and python_functions config options work
- as prefixes and glob-like patterns (issue #600).
- """
- testdir.makeini("""
- [pytest]
- python_classes = *Suite Test
- python_functions = *_test test
- """)
- p = testdir.makepyfile('''
- class MyTestSuite:
- def x_test(self):
- pass
-
- class TestCase:
- def test_y(self):
- pass
- ''')
- items, reprec = testdir.inline_genitems(p)
- ids = [x.getmodpath() for x in items]
- assert ids == ['MyTestSuite.x_test', 'TestCase.test_y']
-
-
-def test_matchnodes_two_collections_same_file(testdir):
- testdir.makeconftest("""
- import pytest
- def pytest_configure(config):
- config.pluginmanager.register(Plugin2())
-
- class Plugin2:
- def pytest_collect_file(self, path, parent):
- if path.ext == ".abc":
- return MyFile2(path, parent)
-
- def pytest_collect_file(path, parent):
- if path.ext == ".abc":
- return MyFile1(path, parent)
-
- class MyFile1(pytest.Item, pytest.File):
- def runtest(self):
- pass
- class MyFile2(pytest.File):
- def collect(self):
- return [Item2("hello", parent=self)]
-
- class Item2(pytest.Item):
- def runtest(self):
- pass
- """)
- p = testdir.makefile(".abc", "")
- result = testdir.runpytest()
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*2 passed*",
- ])
- res = testdir.runpytest("%s::hello" % p.basename)
- res.stdout.fnmatch_lines([
- "*1 passed*",
- ])
-
-
-class TestNodekeywords:
- def test_no_under(self, testdir):
- modcol = testdir.getmodulecol("""
- def test_pass(): pass
- def test_fail(): assert 0
- """)
- l = list(modcol.keywords)
- assert modcol.name in l
- for x in l:
- assert not x.startswith("_")
- assert modcol.name in repr(modcol.keywords)
-
- def test_issue345(self, testdir):
- testdir.makepyfile("""
- def test_should_not_be_selected():
- assert False, 'I should not have been selected to run'
-
- def test___repr__():
- pass
- """)
- reprec = testdir.inline_run("-k repr")
- reprec.assertoutcome(passed=1, failed=0)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_config.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_config.py
deleted file mode 100644
index 92c9bdb8b08..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_config.py
+++ /dev/null
@@ -1,570 +0,0 @@
-import py, pytest
-
-import _pytest._code
-from _pytest.config import getcfg, get_common_ancestor, determine_setup
-from _pytest.main import EXIT_NOTESTSCOLLECTED
-
-class TestParseIni:
- def test_getcfg_and_config(self, testdir, tmpdir):
- sub = tmpdir.mkdir("sub")
- sub.chdir()
- tmpdir.join("setup.cfg").write(_pytest._code.Source("""
- [pytest]
- name = value
- """))
- rootdir, inifile, cfg = getcfg([sub], ["setup.cfg"])
- assert cfg['name'] == "value"
- config = testdir.parseconfigure(sub)
- assert config.inicfg['name'] == 'value'
-
- def test_getcfg_empty_path(self, tmpdir):
- getcfg([''], ['setup.cfg']) #happens on py.test ""
-
- def test_append_parse_args(self, testdir, tmpdir, monkeypatch):
- monkeypatch.setenv('PYTEST_ADDOPTS', '--color no -rs --tb="short"')
- tmpdir.join("setup.cfg").write(_pytest._code.Source("""
- [pytest]
- addopts = --verbose
- """))
- config = testdir.parseconfig(tmpdir)
- assert config.option.color == 'no'
- assert config.option.reportchars == 's'
- assert config.option.tbstyle == 'short'
- assert config.option.verbose
- #config = testdir.Config()
- #args = [tmpdir,]
- #config._preparse(args, addopts=False)
- #assert len(args) == 1
-
- def test_tox_ini_wrong_version(self, testdir):
- testdir.makefile('.ini', tox="""
- [pytest]
- minversion=9.0
- """)
- result = testdir.runpytest()
- assert result.ret != 0
- result.stderr.fnmatch_lines([
- "*tox.ini:2*requires*9.0*actual*"
- ])
-
- @pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
- def test_ini_names(self, testdir, name):
- testdir.tmpdir.join(name).write(py.std.textwrap.dedent("""
- [pytest]
- minversion = 1.0
- """))
- config = testdir.parseconfig()
- assert config.getini("minversion") == "1.0"
-
- def test_toxini_before_lower_pytestini(self, testdir):
- sub = testdir.tmpdir.mkdir("sub")
- sub.join("tox.ini").write(py.std.textwrap.dedent("""
- [pytest]
- minversion = 2.0
- """))
- testdir.tmpdir.join("pytest.ini").write(py.std.textwrap.dedent("""
- [pytest]
- minversion = 1.5
- """))
- config = testdir.parseconfigure(sub)
- assert config.getini("minversion") == "2.0"
-
- @pytest.mark.xfail(reason="probably not needed")
- def test_confcutdir(self, testdir):
- sub = testdir.mkdir("sub")
- sub.chdir()
- testdir.makeini("""
- [pytest]
- addopts = --qwe
- """)
- result = testdir.inline_run("--confcutdir=.")
- assert result.ret == 0
-
-class TestConfigCmdlineParsing:
- def test_parsing_again_fails(self, testdir):
- config = testdir.parseconfig()
- pytest.raises(AssertionError, lambda: config.parse([]))
-
- def test_explicitly_specified_config_file_is_loaded(self, testdir):
- testdir.makeconftest("""
- def pytest_addoption(parser):
- parser.addini("custom", "")
- """)
- testdir.makeini("""
- [pytest]
- custom = 0
- """)
- testdir.makefile(".cfg", custom = """
- [pytest]
- custom = 1
- """)
- config = testdir.parseconfig("-c", "custom.cfg")
- assert config.getini("custom") == "1"
-
-class TestConfigAPI:
- def test_config_trace(self, testdir):
- config = testdir.parseconfig()
- l = []
- config.trace.root.setwriter(l.append)
- config.trace("hello")
- assert len(l) == 1
- assert l[0] == "hello [config]\n"
-
- def test_config_getoption(self, testdir):
- testdir.makeconftest("""
- def pytest_addoption(parser):
- parser.addoption("--hello", "-X", dest="hello")
- """)
- config = testdir.parseconfig("--hello=this")
- for x in ("hello", "--hello", "-X"):
- assert config.getoption(x) == "this"
- pytest.raises(ValueError, "config.getoption('qweqwe')")
-
- @pytest.mark.skipif('sys.version_info[:2] not in [(2, 6), (2, 7)]')
- def test_config_getoption_unicode(self, testdir):
- testdir.makeconftest("""
- from __future__ import unicode_literals
-
- def pytest_addoption(parser):
- parser.addoption('--hello', type='string')
- """)
- config = testdir.parseconfig('--hello=this')
- assert config.getoption('hello') == 'this'
-
- def test_config_getvalueorskip(self, testdir):
- config = testdir.parseconfig()
- pytest.raises(pytest.skip.Exception,
- "config.getvalueorskip('hello')")
- verbose = config.getvalueorskip("verbose")
- assert verbose == config.option.verbose
-
- def test_config_getvalueorskip_None(self, testdir):
- testdir.makeconftest("""
- def pytest_addoption(parser):
- parser.addoption("--hello")
- """)
- config = testdir.parseconfig()
- with pytest.raises(pytest.skip.Exception):
- config.getvalueorskip('hello')
-
- def test_getoption(self, testdir):
- config = testdir.parseconfig()
- with pytest.raises(ValueError):
- config.getvalue('x')
- assert config.getoption("x", 1) == 1
-
- def test_getconftest_pathlist(self, testdir, tmpdir):
- somepath = tmpdir.join("x", "y", "z")
- p = tmpdir.join("conftest.py")
- p.write("pathlist = ['.', %r]" % str(somepath))
- config = testdir.parseconfigure(p)
- assert config._getconftest_pathlist('notexist', path=tmpdir) is None
- pl = config._getconftest_pathlist('pathlist', path=tmpdir)
- print(pl)
- assert len(pl) == 2
- assert pl[0] == tmpdir
- assert pl[1] == somepath
-
- def test_addini(self, testdir):
- testdir.makeconftest("""
- def pytest_addoption(parser):
- parser.addini("myname", "my new ini value")
- """)
- testdir.makeini("""
- [pytest]
- myname=hello
- """)
- config = testdir.parseconfig()
- val = config.getini("myname")
- assert val == "hello"
- pytest.raises(ValueError, config.getini, 'other')
-
- def test_addini_pathlist(self, testdir):
- testdir.makeconftest("""
- def pytest_addoption(parser):
- parser.addini("paths", "my new ini value", type="pathlist")
- parser.addini("abc", "abc value")
- """)
- p = testdir.makeini("""
- [pytest]
- paths=hello world/sub.py
- """)
- config = testdir.parseconfig()
- l = config.getini("paths")
- assert len(l) == 2
- assert l[0] == p.dirpath('hello')
- assert l[1] == p.dirpath('world/sub.py')
- pytest.raises(ValueError, config.getini, 'other')
-
- def test_addini_args(self, testdir):
- testdir.makeconftest("""
- def pytest_addoption(parser):
- parser.addini("args", "new args", type="args")
- parser.addini("a2", "", "args", default="1 2 3".split())
- """)
- testdir.makeini("""
- [pytest]
- args=123 "123 hello" "this"
- """)
- config = testdir.parseconfig()
- l = config.getini("args")
- assert len(l) == 3
- assert l == ["123", "123 hello", "this"]
- l = config.getini("a2")
- assert l == list("123")
-
- def test_addini_linelist(self, testdir):
- testdir.makeconftest("""
- def pytest_addoption(parser):
- parser.addini("xy", "", type="linelist")
- parser.addini("a2", "", "linelist")
- """)
- testdir.makeini("""
- [pytest]
- xy= 123 345
- second line
- """)
- config = testdir.parseconfig()
- l = config.getini("xy")
- assert len(l) == 2
- assert l == ["123 345", "second line"]
- l = config.getini("a2")
- assert l == []
-
- @pytest.mark.parametrize('str_val, bool_val',
- [('True', True), ('no', False), ('no-ini', True)])
- def test_addini_bool(self, testdir, str_val, bool_val):
- testdir.makeconftest("""
- def pytest_addoption(parser):
- parser.addini("strip", "", type="bool", default=True)
- """)
- if str_val != 'no-ini':
- testdir.makeini("""
- [pytest]
- strip=%s
- """ % str_val)
- config = testdir.parseconfig()
- assert config.getini("strip") is bool_val
-
- def test_addinivalue_line_existing(self, testdir):
- testdir.makeconftest("""
- def pytest_addoption(parser):
- parser.addini("xy", "", type="linelist")
- """)
- testdir.makeini("""
- [pytest]
- xy= 123
- """)
- config = testdir.parseconfig()
- l = config.getini("xy")
- assert len(l) == 1
- assert l == ["123"]
- config.addinivalue_line("xy", "456")
- l = config.getini("xy")
- assert len(l) == 2
- assert l == ["123", "456"]
-
- def test_addinivalue_line_new(self, testdir):
- testdir.makeconftest("""
- def pytest_addoption(parser):
- parser.addini("xy", "", type="linelist")
- """)
- config = testdir.parseconfig()
- assert not config.getini("xy")
- config.addinivalue_line("xy", "456")
- l = config.getini("xy")
- assert len(l) == 1
- assert l == ["456"]
- config.addinivalue_line("xy", "123")
- l = config.getini("xy")
- assert len(l) == 2
- assert l == ["456", "123"]
-
-
-class TestConfigFromdictargs:
- def test_basic_behavior(self):
- from _pytest.config import Config
- option_dict = {
- 'verbose': 444,
- 'foo': 'bar',
- 'capture': 'no',
- }
- args = ['a', 'b']
-
- config = Config.fromdictargs(option_dict, args)
- with pytest.raises(AssertionError):
- config.parse(['should refuse to parse again'])
- assert config.option.verbose == 444
- assert config.option.foo == 'bar'
- assert config.option.capture == 'no'
- assert config.args == args
-
- def test_origargs(self):
- """Show that fromdictargs can handle args in their "orig" format"""
- from _pytest.config import Config
- option_dict = {}
- args = ['-vvvv', '-s', 'a', 'b']
-
- config = Config.fromdictargs(option_dict, args)
- assert config.args == ['a', 'b']
- assert config._origargs == args
- assert config.option.verbose == 4
- assert config.option.capture == 'no'
-
- def test_inifilename(self, tmpdir):
- tmpdir.join("foo/bar.ini").ensure().write(_pytest._code.Source("""
- [pytest]
- name = value
- """))
-
- from _pytest.config import Config
- inifile = '../../foo/bar.ini'
- option_dict = {
- 'inifilename': inifile,
- 'capture': 'no',
- }
-
- cwd = tmpdir.join('a/b')
- cwd.join('pytest.ini').ensure().write(_pytest._code.Source("""
- [pytest]
- name = wrong-value
- should_not_be_set = true
- """))
- with cwd.ensure(dir=True).as_cwd():
- config = Config.fromdictargs(option_dict, ())
-
- assert config.args == [str(cwd)]
- assert config.option.inifilename == inifile
- assert config.option.capture == 'no'
-
- # this indicates this is the file used for getting configuration values
- assert config.inifile == inifile
- assert config.inicfg.get('name') == 'value'
- assert config.inicfg.get('should_not_be_set') is None
-
-
-def test_options_on_small_file_do_not_blow_up(testdir):
- def runfiletest(opts):
- reprec = testdir.inline_run(*opts)
- passed, skipped, failed = reprec.countoutcomes()
- assert failed == 2
- assert skipped == passed == 0
- path = testdir.makepyfile("""
- def test_f1(): assert 0
- def test_f2(): assert 0
- """)
-
- for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'],
- ['--tb=long'], ['--fulltrace'], ['--nomagic'],
- ['--traceconfig'], ['-v'], ['-v', '-v']):
- runfiletest(opts + [path])
-
-def test_preparse_ordering_with_setuptools(testdir, monkeypatch):
- pkg_resources = pytest.importorskip("pkg_resources")
- def my_iter(name):
- assert name == "pytest11"
- class EntryPoint:
- name = "mytestplugin"
- class dist:
- pass
- def load(self):
- class PseudoPlugin:
- x = 42
- return PseudoPlugin()
- return iter([EntryPoint()])
- monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
- testdir.makeconftest("""
- pytest_plugins = "mytestplugin",
- """)
- monkeypatch.setenv("PYTEST_PLUGINS", "mytestplugin")
- config = testdir.parseconfig()
- plugin = config.pluginmanager.getplugin("mytestplugin")
- assert plugin.x == 42
-
-def test_plugin_preparse_prevents_setuptools_loading(testdir, monkeypatch):
- pkg_resources = pytest.importorskip("pkg_resources")
- def my_iter(name):
- assert name == "pytest11"
- class EntryPoint:
- name = "mytestplugin"
- def load(self):
- assert 0, "should not arrive here"
- return iter([EntryPoint()])
- monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
- config = testdir.parseconfig("-p", "no:mytestplugin")
- plugin = config.pluginmanager.getplugin("mytestplugin")
- assert plugin is None
-
-def test_cmdline_processargs_simple(testdir):
- testdir.makeconftest("""
- def pytest_cmdline_preparse(args):
- args.append("-h")
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*pytest*",
- "*-h*",
- ])
-
-def test_invalid_options_show_extra_information(testdir):
- """display extra information when pytest exits due to unrecognized
- options in the command-line"""
- testdir.makeini("""
- [pytest]
- addopts = --invalid-option
- """)
- result = testdir.runpytest()
- result.stderr.fnmatch_lines([
- "*error: unrecognized arguments: --invalid-option*",
- "* inifile: %s*" % testdir.tmpdir.join('tox.ini'),
- "* rootdir: %s*" % testdir.tmpdir,
- ])
-
-
-@pytest.mark.parametrize('args', [
- ['dir1', 'dir2', '-v'],
- ['dir1', '-v', 'dir2'],
- ['dir2', '-v', 'dir1'],
- ['-v', 'dir2', 'dir1'],
-])
-def test_consider_args_after_options_for_rootdir_and_inifile(testdir, args):
- """
- Consider all arguments in the command-line for rootdir and inifile
- discovery, even if they happen to occur after an option. #949
- """
- # replace "dir1" and "dir2" from "args" into their real directory
- root = testdir.tmpdir.mkdir('myroot')
- d1 = root.mkdir('dir1')
- d2 = root.mkdir('dir2')
- for i, arg in enumerate(args):
- if arg == 'dir1':
- args[i] = d1
- elif arg == 'dir2':
- args[i] = d2
- result = testdir.runpytest(*args)
- result.stdout.fnmatch_lines(['*rootdir: *myroot, inifile: '])
-
-
-@pytest.mark.skipif("sys.platform == 'win32'")
-def test_toolongargs_issue224(testdir):
- result = testdir.runpytest("-m", "hello" * 500)
- assert result.ret == EXIT_NOTESTSCOLLECTED
-
-def test_notify_exception(testdir, capfd):
- config = testdir.parseconfig()
- excinfo = pytest.raises(ValueError, "raise ValueError(1)")
- config.notify_exception(excinfo)
- out, err = capfd.readouterr()
- assert "ValueError" in err
- class A:
- def pytest_internalerror(self, excrepr):
- return True
- config.pluginmanager.register(A())
- config.notify_exception(excinfo)
- out, err = capfd.readouterr()
- assert not err
-
-
-def test_load_initial_conftest_last_ordering(testdir):
- from _pytest.config import get_config
- pm = get_config().pluginmanager
- class My:
- def pytest_load_initial_conftests(self):
- pass
- m = My()
- pm.register(m)
- hc = pm.hook.pytest_load_initial_conftests
- l = hc._nonwrappers + hc._wrappers
- assert l[-1].function.__module__ == "_pytest.capture"
- assert l[-2].function == m.pytest_load_initial_conftests
- assert l[-3].function.__module__ == "_pytest.config"
-
-class TestWarning:
- def test_warn_config(self, testdir):
- testdir.makeconftest("""
- l = []
- def pytest_configure(config):
- config.warn("C1", "hello")
- def pytest_logwarning(code, message):
- if message == "hello" and code == "C1":
- l.append(1)
- """)
- testdir.makepyfile("""
- def test_proper(pytestconfig):
- import conftest
- assert conftest.l == [1]
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_warn_on_test_item_from_request(self, testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture
- def fix(request):
- request.node.warn("T1", "hello")
- def test_hello(fix):
- pass
- """)
- result = testdir.runpytest()
- assert result.parseoutcomes()["pytest-warnings"] > 0
- assert "hello" not in result.stdout.str()
-
- result = testdir.runpytest("-rw")
- result.stdout.fnmatch_lines("""
- ===*pytest-warning summary*===
- *WT1*test_warn_on_test_item*:5*hello*
- """)
-
-class TestRootdir:
- def test_simple_noini(self, tmpdir):
- assert get_common_ancestor([tmpdir]) == tmpdir
- assert get_common_ancestor([tmpdir.mkdir("a"), tmpdir]) == tmpdir
- assert get_common_ancestor([tmpdir, tmpdir.join("a")]) == tmpdir
- with tmpdir.as_cwd():
- assert get_common_ancestor([]) == tmpdir
-
- @pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
- def test_with_ini(self, tmpdir, name):
- inifile = tmpdir.join(name)
- inifile.write("[pytest]\n")
-
- a = tmpdir.mkdir("a")
- b = a.mkdir("b")
- for args in ([tmpdir], [a], [b]):
- rootdir, inifile, inicfg = determine_setup(None, args)
- assert rootdir == tmpdir
- assert inifile == inifile
- rootdir, inifile, inicfg = determine_setup(None, [b,a])
- assert rootdir == tmpdir
- assert inifile == inifile
-
- @pytest.mark.parametrize("name", "setup.cfg tox.ini".split())
- def test_pytestini_overides_empty_other(self, tmpdir, name):
- inifile = tmpdir.ensure("pytest.ini")
- a = tmpdir.mkdir("a")
- a.ensure(name)
- rootdir, inifile, inicfg = determine_setup(None, [a])
- assert rootdir == tmpdir
- assert inifile == inifile
-
- def test_setuppy_fallback(self, tmpdir):
- a = tmpdir.mkdir("a")
- a.ensure("setup.cfg")
- tmpdir.ensure("setup.py")
- rootdir, inifile, inicfg = determine_setup(None, [a])
- assert rootdir == tmpdir
- assert inifile is None
- assert inicfg == {}
-
- def test_nothing(self, tmpdir):
- rootdir, inifile, inicfg = determine_setup(None, [tmpdir])
- assert rootdir == tmpdir
- assert inifile is None
- assert inicfg == {}
-
- def test_with_specific_inifile(self, tmpdir):
- inifile = tmpdir.ensure("pytest.ini")
- rootdir, inifile, inicfg = determine_setup(inifile, [tmpdir])
- assert rootdir == tmpdir
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_conftest.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_conftest.py
deleted file mode 100644
index 6f5e77f6d49..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_conftest.py
+++ /dev/null
@@ -1,409 +0,0 @@
-from textwrap import dedent
-
-import _pytest._code
-import py
-import pytest
-from _pytest.config import PytestPluginManager
-from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
-
-
-@pytest.fixture(scope="module", params=["global", "inpackage"])
-def basedir(request, tmpdir_factory):
- from _pytest.tmpdir import tmpdir
- tmpdir = tmpdir(request, tmpdir_factory)
- tmpdir.ensure("adir/conftest.py").write("a=1 ; Directory = 3")
- tmpdir.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5")
- if request.param == "inpackage":
- tmpdir.ensure("adir/__init__.py")
- tmpdir.ensure("adir/b/__init__.py")
- return tmpdir
-
-def ConftestWithSetinitial(path):
- conftest = PytestPluginManager()
- conftest_setinitial(conftest, [path])
- return conftest
-
-def conftest_setinitial(conftest, args, confcutdir=None):
- class Namespace:
- def __init__(self):
- self.file_or_dir = args
- self.confcutdir = str(confcutdir)
- self.noconftest = False
- conftest._set_initial_conftests(Namespace())
-
-class TestConftestValueAccessGlobal:
- def test_basic_init(self, basedir):
- conftest = PytestPluginManager()
- p = basedir.join("adir")
- assert conftest._rget_with_confmod("a", p)[1] == 1
-
- def test_immediate_initialiation_and_incremental_are_the_same(self, basedir):
- conftest = PytestPluginManager()
- len(conftest._path2confmods)
- conftest._getconftestmodules(basedir)
- snap1 = len(conftest._path2confmods)
- #assert len(conftest._path2confmods) == snap1 + 1
- conftest._getconftestmodules(basedir.join('adir'))
- assert len(conftest._path2confmods) == snap1 + 1
- conftest._getconftestmodules(basedir.join('b'))
- assert len(conftest._path2confmods) == snap1 + 2
-
- def test_value_access_not_existing(self, basedir):
- conftest = ConftestWithSetinitial(basedir)
- with pytest.raises(KeyError):
- conftest._rget_with_confmod('a', basedir)
-
- def test_value_access_by_path(self, basedir):
- conftest = ConftestWithSetinitial(basedir)
- adir = basedir.join("adir")
- assert conftest._rget_with_confmod("a", adir)[1] == 1
- assert conftest._rget_with_confmod("a", adir.join("b"))[1] == 1.5
-
- def test_value_access_with_confmod(self, basedir):
- startdir = basedir.join("adir", "b")
- startdir.ensure("xx", dir=True)
- conftest = ConftestWithSetinitial(startdir)
- mod, value = conftest._rget_with_confmod("a", startdir)
- assert value == 1.5
- path = py.path.local(mod.__file__)
- assert path.dirpath() == basedir.join("adir", "b")
- assert path.purebasename.startswith("conftest")
-
-def test_conftest_in_nonpkg_with_init(tmpdir):
- tmpdir.ensure("adir-1.0/conftest.py").write("a=1 ; Directory = 3")
- tmpdir.ensure("adir-1.0/b/conftest.py").write("b=2 ; a = 1.5")
- tmpdir.ensure("adir-1.0/b/__init__.py")
- tmpdir.ensure("adir-1.0/__init__.py")
- ConftestWithSetinitial(tmpdir.join("adir-1.0", "b"))
-
-def test_doubledash_considered(testdir):
- conf = testdir.mkdir("--option")
- conf.join("conftest.py").ensure()
- conftest = PytestPluginManager()
- conftest_setinitial(conftest, [conf.basename, conf.basename])
- l = conftest._getconftestmodules(conf)
- assert len(l) == 1
-
-def test_issue151_load_all_conftests(testdir):
- names = "code proj src".split()
- for name in names:
- p = testdir.mkdir(name)
- p.ensure("conftest.py")
-
- conftest = PytestPluginManager()
- conftest_setinitial(conftest, names)
- d = list(conftest._conftestpath2mod.values())
- assert len(d) == len(names)
-
-def test_conftest_global_import(testdir):
- testdir.makeconftest("x=3")
- p = testdir.makepyfile("""
- import py, pytest
- from _pytest.config import PytestPluginManager
- conf = PytestPluginManager()
- mod = conf._importconftest(py.path.local("conftest.py"))
- assert mod.x == 3
- import conftest
- assert conftest is mod, (conftest, mod)
- subconf = py.path.local().ensure("sub", "conftest.py")
- subconf.write("y=4")
- mod2 = conf._importconftest(subconf)
- assert mod != mod2
- assert mod2.y == 4
- import conftest
- assert conftest is mod2, (conftest, mod)
- """)
- res = testdir.runpython(p)
- assert res.ret == 0
-
-def test_conftestcutdir(testdir):
- conf = testdir.makeconftest("")
- p = testdir.mkdir("x")
- conftest = PytestPluginManager()
- conftest_setinitial(conftest, [testdir.tmpdir], confcutdir=p)
- l = conftest._getconftestmodules(p)
- assert len(l) == 0
- l = conftest._getconftestmodules(conf.dirpath())
- assert len(l) == 0
- assert conf not in conftest._conftestpath2mod
- # but we can still import a conftest directly
- conftest._importconftest(conf)
- l = conftest._getconftestmodules(conf.dirpath())
- assert l[0].__file__.startswith(str(conf))
- # and all sub paths get updated properly
- l = conftest._getconftestmodules(p)
- assert len(l) == 1
- assert l[0].__file__.startswith(str(conf))
-
-def test_conftestcutdir_inplace_considered(testdir):
- conf = testdir.makeconftest("")
- conftest = PytestPluginManager()
- conftest_setinitial(conftest, [conf.dirpath()], confcutdir=conf.dirpath())
- l = conftest._getconftestmodules(conf.dirpath())
- assert len(l) == 1
- assert l[0].__file__.startswith(str(conf))
-
-@pytest.mark.parametrize("name", 'test tests whatever .dotdir'.split())
-def test_setinitial_conftest_subdirs(testdir, name):
- sub = testdir.mkdir(name)
- subconftest = sub.ensure("conftest.py")
- conftest = PytestPluginManager()
- conftest_setinitial(conftest, [sub.dirpath()], confcutdir=testdir.tmpdir)
- if name not in ('whatever', '.dotdir'):
- assert subconftest in conftest._conftestpath2mod
- assert len(conftest._conftestpath2mod) == 1
- else:
- assert subconftest not in conftest._conftestpath2mod
- assert len(conftest._conftestpath2mod) == 0
-
-def test_conftest_confcutdir(testdir):
- testdir.makeconftest("assert 0")
- x = testdir.mkdir("x")
- x.join("conftest.py").write(_pytest._code.Source("""
- def pytest_addoption(parser):
- parser.addoption("--xyz", action="store_true")
- """))
- result = testdir.runpytest("-h", "--confcutdir=%s" % x, x)
- result.stdout.fnmatch_lines(["*--xyz*"])
- assert 'warning: could not load initial' not in result.stdout.str()
-
-def test_no_conftest(testdir):
- testdir.makeconftest("assert 0")
- result = testdir.runpytest("--noconftest")
- assert result.ret == EXIT_NOTESTSCOLLECTED
-
- result = testdir.runpytest()
- assert result.ret == EXIT_USAGEERROR
-
-def test_conftest_existing_resultlog(testdir):
- x = testdir.mkdir("tests")
- x.join("conftest.py").write(_pytest._code.Source("""
- def pytest_addoption(parser):
- parser.addoption("--xyz", action="store_true")
- """))
- testdir.makefile(ext=".log", result="") # Writes result.log
- result = testdir.runpytest("-h", "--resultlog", "result.log")
- result.stdout.fnmatch_lines(["*--xyz*"])
-
-def test_conftest_existing_junitxml(testdir):
- x = testdir.mkdir("tests")
- x.join("conftest.py").write(_pytest._code.Source("""
- def pytest_addoption(parser):
- parser.addoption("--xyz", action="store_true")
- """))
- testdir.makefile(ext=".xml", junit="") # Writes junit.xml
- result = testdir.runpytest("-h", "--junitxml", "junit.xml")
- result.stdout.fnmatch_lines(["*--xyz*"])
-
-def test_conftest_import_order(testdir, monkeypatch):
- ct1 = testdir.makeconftest("")
- sub = testdir.mkdir("sub")
- ct2 = sub.join("conftest.py")
- ct2.write("")
- def impct(p):
- return p
- conftest = PytestPluginManager()
- monkeypatch.setattr(conftest, '_importconftest', impct)
- assert conftest._getconftestmodules(sub) == [ct1, ct2]
-
-
-def test_fixture_dependency(testdir, monkeypatch):
- ct1 = testdir.makeconftest("")
- ct1 = testdir.makepyfile("__init__.py")
- ct1.write("")
- sub = testdir.mkdir("sub")
- sub.join("__init__.py").write("")
- sub.join("conftest.py").write(py.std.textwrap.dedent("""
- import pytest
-
- @pytest.fixture
- def not_needed():
- assert False, "Should not be called!"
-
- @pytest.fixture
- def foo():
- assert False, "Should not be called!"
-
- @pytest.fixture
- def bar(foo):
- return 'bar'
- """))
- subsub = sub.mkdir("subsub")
- subsub.join("__init__.py").write("")
- subsub.join("test_bar.py").write(py.std.textwrap.dedent("""
- import pytest
-
- @pytest.fixture
- def bar():
- return 'sub bar'
-
- def test_event_fixture(bar):
- assert bar == 'sub bar'
- """))
- result = testdir.runpytest("sub")
- result.stdout.fnmatch_lines(["*1 passed*"])
-
-
-def test_conftest_found_with_double_dash(testdir):
- sub = testdir.mkdir("sub")
- sub.join("conftest.py").write(py.std.textwrap.dedent("""
- def pytest_addoption(parser):
- parser.addoption("--hello-world", action="store_true")
- """))
- p = sub.join("test_hello.py")
- p.write(py.std.textwrap.dedent("""
- import pytest
- def test_hello(found):
- assert found == 1
- """))
- result = testdir.runpytest(str(p) + "::test_hello", "-h")
- result.stdout.fnmatch_lines("""
- *--hello-world*
- """)
-
-
-class TestConftestVisibility:
- def _setup_tree(self, testdir): # for issue616
- # example mostly taken from:
- # https://mail.python.org/pipermail/pytest-dev/2014-September/002617.html
- runner = testdir.mkdir("empty")
- package = testdir.mkdir("package")
-
- package.join("conftest.py").write(dedent("""\
- import pytest
- @pytest.fixture
- def fxtr():
- return "from-package"
- """))
- package.join("test_pkgroot.py").write(dedent("""\
- def test_pkgroot(fxtr):
- assert fxtr == "from-package"
- """))
-
- swc = package.mkdir("swc")
- swc.join("__init__.py").ensure()
- swc.join("conftest.py").write(dedent("""\
- import pytest
- @pytest.fixture
- def fxtr():
- return "from-swc"
- """))
- swc.join("test_with_conftest.py").write(dedent("""\
- def test_with_conftest(fxtr):
- assert fxtr == "from-swc"
-
- """))
-
- snc = package.mkdir("snc")
- snc.join("__init__.py").ensure()
- snc.join("test_no_conftest.py").write(dedent("""\
- def test_no_conftest(fxtr):
- assert fxtr == "from-package" # No local conftest.py, so should
- # use value from parent dir's
-
- """))
- print ("created directory structure:")
- for x in testdir.tmpdir.visit():
- print (" " + x.relto(testdir.tmpdir))
-
- return {
- "runner": runner,
- "package": package,
- "swc": swc,
- "snc": snc}
-
- # N.B.: "swc" stands for "subdir with conftest.py"
- # "snc" stands for "subdir no [i.e. without] conftest.py"
- @pytest.mark.parametrize("chdir,testarg,expect_ntests_passed", [
- # Effective target: package/..
- ("runner", "..", 3),
- ("package", "..", 3),
- ("swc", "../..", 3),
- ("snc", "../..", 3),
-
- # Effective target: package
- ("runner", "../package", 3),
- ("package", ".", 3),
- ("swc", "..", 3),
- ("snc", "..", 3),
-
- # Effective target: package/swc
- ("runner", "../package/swc", 1),
- ("package", "./swc", 1),
- ("swc", ".", 1),
- ("snc", "../swc", 1),
-
- # Effective target: package/snc
- ("runner", "../package/snc", 1),
- ("package", "./snc", 1),
- ("swc", "../snc", 1),
- ("snc", ".", 1),
- ])
- @pytest.mark.issue616
- def test_parsefactories_relative_node_ids(
- self, testdir, chdir,testarg, expect_ntests_passed):
- dirs = self._setup_tree(testdir)
- print("pytest run in cwd: %s" %(
- dirs[chdir].relto(testdir.tmpdir)))
- print("pytestarg : %s" %(testarg))
- print("expected pass : %s" %(expect_ntests_passed))
- with dirs[chdir].as_cwd():
- reprec = testdir.inline_run(testarg, "-q", "--traceconfig")
- reprec.assertoutcome(passed=expect_ntests_passed)
-
-
-@pytest.mark.parametrize('confcutdir,passed,error', [
- ('.', 2, 0),
- ('src', 1, 1),
- (None, 1, 1),
-])
-def test_search_conftest_up_to_inifile(testdir, confcutdir, passed, error):
- """Test that conftest files are detected only up to a ini file, unless
- an explicit --confcutdir option is given.
- """
- root = testdir.tmpdir
- src = root.join('src').ensure(dir=1)
- src.join('pytest.ini').write('[pytest]')
- src.join('conftest.py').write(_pytest._code.Source("""
- import pytest
- @pytest.fixture
- def fix1(): pass
- """))
- src.join('test_foo.py').write(_pytest._code.Source("""
- def test_1(fix1):
- pass
- def test_2(out_of_reach):
- pass
- """))
- root.join('conftest.py').write(_pytest._code.Source("""
- import pytest
- @pytest.fixture
- def out_of_reach(): pass
- """))
-
- args = [str(src)]
- if confcutdir:
- args = ['--confcutdir=%s' % root.join(confcutdir)]
- result = testdir.runpytest(*args)
- match = ''
- if passed:
- match += '*%d passed*' % passed
- if error:
- match += '*%d error*' % error
- result.stdout.fnmatch_lines(match)
-
-
-def test_issue1073_conftest_special_objects(testdir):
- testdir.makeconftest("""
- class DontTouchMe:
- def __getattr__(self, x):
- raise Exception('cant touch me')
-
- x = DontTouchMe()
- """)
- testdir.makepyfile("""
- def test_some():
- pass
- """)
- res = testdir.runpytest()
- assert res.ret == 0
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_doctest.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_doctest.py
deleted file mode 100644
index a4821ee4c9e..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_doctest.py
+++ /dev/null
@@ -1,715 +0,0 @@
-# encoding: utf-8
-import sys
-import _pytest._code
-from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile
-import pytest
-
-class TestDoctests:
-
- def test_collect_testtextfile(self, testdir):
- w = testdir.maketxtfile(whatever="")
- checkfile = testdir.maketxtfile(test_something="""
- alskdjalsdk
- >>> i = 5
- >>> i-1
- 4
- """)
- for x in (testdir.tmpdir, checkfile):
- #print "checking that %s returns custom items" % (x,)
- items, reprec = testdir.inline_genitems(x)
- assert len(items) == 1
- assert isinstance(items[0], DoctestTextfile)
- items, reprec = testdir.inline_genitems(w)
- assert len(items) == 1
-
- def test_collect_module_empty(self, testdir):
- path = testdir.makepyfile(whatever="#")
- for p in (path, testdir.tmpdir):
- items, reprec = testdir.inline_genitems(p,
- '--doctest-modules')
- assert len(items) == 0
-
- def test_collect_module_single_modulelevel_doctest(self, testdir):
- path = testdir.makepyfile(whatever='""">>> pass"""')
- for p in (path, testdir.tmpdir):
- items, reprec = testdir.inline_genitems(p,
- '--doctest-modules')
- assert len(items) == 1
- assert isinstance(items[0], DoctestItem)
- assert isinstance(items[0].parent, DoctestModule)
-
- def test_collect_module_two_doctest_one_modulelevel(self, testdir):
- path = testdir.makepyfile(whatever="""
- '>>> x = None'
- def my_func():
- ">>> magic = 42 "
- """)
- for p in (path, testdir.tmpdir):
- items, reprec = testdir.inline_genitems(p,
- '--doctest-modules')
- assert len(items) == 2
- assert isinstance(items[0], DoctestItem)
- assert isinstance(items[1], DoctestItem)
- assert isinstance(items[0].parent, DoctestModule)
- assert items[0].parent is items[1].parent
-
- def test_collect_module_two_doctest_no_modulelevel(self, testdir):
- path = testdir.makepyfile(whatever="""
- '# Empty'
- def my_func():
- ">>> magic = 42 "
- def unuseful():
- '''
- # This is a function
- # >>> # it doesn't have any doctest
- '''
- def another():
- '''
- # This is another function
- >>> import os # this one does have a doctest
- '''
- """)
- for p in (path, testdir.tmpdir):
- items, reprec = testdir.inline_genitems(p,
- '--doctest-modules')
- assert len(items) == 2
- assert isinstance(items[0], DoctestItem)
- assert isinstance(items[1], DoctestItem)
- assert isinstance(items[0].parent, DoctestModule)
- assert items[0].parent is items[1].parent
-
- def test_simple_doctestfile(self, testdir):
- p = testdir.maketxtfile(test_doc="""
- >>> x = 1
- >>> x == 1
- False
- """)
- reprec = testdir.inline_run(p, )
- reprec.assertoutcome(failed=1)
-
- def test_new_pattern(self, testdir):
- p = testdir.maketxtfile(xdoc="""
- >>> x = 1
- >>> x == 1
- False
- """)
- reprec = testdir.inline_run(p, "--doctest-glob=x*.txt")
- reprec.assertoutcome(failed=1)
-
- def test_multiple_patterns(self, testdir):
- """Test support for multiple --doctest-glob arguments (#1255).
- """
- testdir.maketxtfile(xdoc="""
- >>> 1
- 1
- """)
- testdir.makefile('.foo', test="""
- >>> 1
- 1
- """)
- testdir.maketxtfile(test_normal="""
- >>> 1
- 1
- """)
- expected = set(['xdoc.txt', 'test.foo', 'test_normal.txt'])
- assert set(x.basename for x in testdir.tmpdir.listdir()) == expected
- args = ["--doctest-glob=xdoc*.txt", "--doctest-glob=*.foo"]
- result = testdir.runpytest(*args)
- result.stdout.fnmatch_lines([
- '*test.foo *',
- '*xdoc.txt *',
- '*2 passed*',
- ])
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- '*test_normal.txt *',
- '*1 passed*',
- ])
-
- def test_doctest_unexpected_exception(self, testdir):
- testdir.maketxtfile("""
- >>> i = 0
- >>> 0 / i
- 2
- """)
- result = testdir.runpytest("--doctest-modules")
- result.stdout.fnmatch_lines([
- "*unexpected_exception*",
- "*>>> i = 0*",
- "*>>> 0 / i*",
- "*UNEXPECTED*ZeroDivision*",
- ])
-
- def test_docstring_context_around_error(self, testdir):
- """Test that we show some context before the actual line of a failing
- doctest.
- """
- testdir.makepyfile('''
- def foo():
- """
- text-line-1
- text-line-2
- text-line-3
- text-line-4
- text-line-5
- text-line-6
- text-line-7
- text-line-8
- text-line-9
- text-line-10
- text-line-11
- >>> 1 + 1
- 3
-
- text-line-after
- """
- ''')
- result = testdir.runpytest('--doctest-modules')
- result.stdout.fnmatch_lines([
- '*docstring_context_around_error*',
- '005*text-line-3',
- '006*text-line-4',
- '013*text-line-11',
- '014*>>> 1 + 1',
- 'Expected:',
- ' 3',
- 'Got:',
- ' 2',
- ])
- # lines below should be trimmed out
- assert 'text-line-2' not in result.stdout.str()
- assert 'text-line-after' not in result.stdout.str()
-
- def test_doctest_linedata_missing(self, testdir):
- testdir.tmpdir.join('hello.py').write(_pytest._code.Source("""
- class Fun(object):
- @property
- def test(self):
- '''
- >>> a = 1
- >>> 1/0
- '''
- """))
- result = testdir.runpytest("--doctest-modules")
- result.stdout.fnmatch_lines([
- "*hello*",
- "*EXAMPLE LOCATION UNKNOWN, not showing all tests of that example*",
- "*1/0*",
- "*UNEXPECTED*ZeroDivision*",
- "*1 failed*",
- ])
-
-
- def test_doctest_unex_importerror(self, testdir):
- testdir.tmpdir.join("hello.py").write(_pytest._code.Source("""
- import asdalsdkjaslkdjasd
- """))
- testdir.maketxtfile("""
- >>> import hello
- >>>
- """)
- result = testdir.runpytest("--doctest-modules")
- result.stdout.fnmatch_lines([
- "*>>> import hello",
- "*UNEXPECTED*ImportError*",
- "*import asdals*",
- ])
-
- def test_doctestmodule(self, testdir):
- p = testdir.makepyfile("""
- '''
- >>> x = 1
- >>> x == 1
- False
-
- '''
- """)
- reprec = testdir.inline_run(p, "--doctest-modules")
- reprec.assertoutcome(failed=1)
-
- def test_doctestmodule_external_and_issue116(self, testdir):
- p = testdir.mkpydir("hello")
- p.join("__init__.py").write(_pytest._code.Source("""
- def somefunc():
- '''
- >>> i = 0
- >>> i + 1
- 2
- '''
- """))
- result = testdir.runpytest(p, "--doctest-modules")
- result.stdout.fnmatch_lines([
- '004 *>>> i = 0',
- '005 *>>> i + 1',
- '*Expected:',
- "* 2",
- "*Got:",
- "* 1",
- "*:5: DocTestFailure"
- ])
-
-
- def test_txtfile_failing(self, testdir):
- p = testdir.maketxtfile("""
- >>> i = 0
- >>> i + 1
- 2
- """)
- result = testdir.runpytest(p, "-s")
- result.stdout.fnmatch_lines([
- '001 >>> i = 0',
- '002 >>> i + 1',
- 'Expected:',
- " 2",
- "Got:",
- " 1",
- "*test_txtfile_failing.txt:2: DocTestFailure"
- ])
-
- def test_txtfile_with_fixtures(self, testdir):
- p = testdir.maketxtfile("""
- >>> dir = getfixture('tmpdir')
- >>> type(dir).__name__
- 'LocalPath'
- """)
- reprec = testdir.inline_run(p, )
- reprec.assertoutcome(passed=1)
-
- def test_txtfile_with_usefixtures_in_ini(self, testdir):
- testdir.makeini("""
- [pytest]
- usefixtures = myfixture
- """)
- testdir.makeconftest("""
- import pytest
- @pytest.fixture
- def myfixture(monkeypatch):
- monkeypatch.setenv("HELLO", "WORLD")
- """)
-
- p = testdir.maketxtfile("""
- >>> import os
- >>> os.environ["HELLO"]
- 'WORLD'
- """)
- reprec = testdir.inline_run(p, )
- reprec.assertoutcome(passed=1)
-
- def test_doctestmodule_with_fixtures(self, testdir):
- p = testdir.makepyfile("""
- '''
- >>> dir = getfixture('tmpdir')
- >>> type(dir).__name__
- 'LocalPath'
- '''
- """)
- reprec = testdir.inline_run(p, "--doctest-modules")
- reprec.assertoutcome(passed=1)
-
- def test_doctestmodule_three_tests(self, testdir):
- p = testdir.makepyfile("""
- '''
- >>> dir = getfixture('tmpdir')
- >>> type(dir).__name__
- 'LocalPath'
- '''
- def my_func():
- '''
- >>> magic = 42
- >>> magic - 42
- 0
- '''
- def unuseful():
- pass
- def another():
- '''
- >>> import os
- >>> os is os
- True
- '''
- """)
- reprec = testdir.inline_run(p, "--doctest-modules")
- reprec.assertoutcome(passed=3)
-
- def test_doctestmodule_two_tests_one_fail(self, testdir):
- p = testdir.makepyfile("""
- class MyClass:
- def bad_meth(self):
- '''
- >>> magic = 42
- >>> magic
- 0
- '''
- def nice_meth(self):
- '''
- >>> magic = 42
- >>> magic - 42
- 0
- '''
- """)
- reprec = testdir.inline_run(p, "--doctest-modules")
- reprec.assertoutcome(failed=1, passed=1)
-
- def test_ignored_whitespace(self, testdir):
- testdir.makeini("""
- [pytest]
- doctest_optionflags = ELLIPSIS NORMALIZE_WHITESPACE
- """)
- p = testdir.makepyfile("""
- class MyClass:
- '''
- >>> a = "foo "
- >>> print(a)
- foo
- '''
- pass
- """)
- reprec = testdir.inline_run(p, "--doctest-modules")
- reprec.assertoutcome(passed=1)
-
- def test_non_ignored_whitespace(self, testdir):
- testdir.makeini("""
- [pytest]
- doctest_optionflags = ELLIPSIS
- """)
- p = testdir.makepyfile("""
- class MyClass:
- '''
- >>> a = "foo "
- >>> print(a)
- foo
- '''
- pass
- """)
- reprec = testdir.inline_run(p, "--doctest-modules")
- reprec.assertoutcome(failed=1, passed=0)
-
- def test_ignored_whitespace_glob(self, testdir):
- testdir.makeini("""
- [pytest]
- doctest_optionflags = ELLIPSIS NORMALIZE_WHITESPACE
- """)
- p = testdir.maketxtfile(xdoc="""
- >>> a = "foo "
- >>> print(a)
- foo
- """)
- reprec = testdir.inline_run(p, "--doctest-glob=x*.txt")
- reprec.assertoutcome(passed=1)
-
- def test_non_ignored_whitespace_glob(self, testdir):
- testdir.makeini("""
- [pytest]
- doctest_optionflags = ELLIPSIS
- """)
- p = testdir.maketxtfile(xdoc="""
- >>> a = "foo "
- >>> print(a)
- foo
- """)
- reprec = testdir.inline_run(p, "--doctest-glob=x*.txt")
- reprec.assertoutcome(failed=1, passed=0)
-
- def test_contains_unicode(self, testdir):
- """Fix internal error with docstrings containing non-ascii characters.
- """
- testdir.makepyfile(u'''
- # encoding: utf-8
- def foo():
- """
- >>> name = 'с' # not letter 'c' but instead Cyrillic 's'.
- 'anything'
- """
- ''')
- result = testdir.runpytest('--doctest-modules')
- result.stdout.fnmatch_lines([
- 'Got nothing',
- '* 1 failed in*',
- ])
-
- def test_ignore_import_errors_on_doctest(self, testdir):
- p = testdir.makepyfile("""
- import asdf
-
- def add_one(x):
- '''
- >>> add_one(1)
- 2
- '''
- return x + 1
- """)
-
- reprec = testdir.inline_run(p, "--doctest-modules",
- "--doctest-ignore-import-errors")
- reprec.assertoutcome(skipped=1, failed=1, passed=0)
-
- def test_junit_report_for_doctest(self, testdir):
- """
- #713: Fix --junit-xml option when used with --doctest-modules.
- """
- p = testdir.makepyfile("""
- def foo():
- '''
- >>> 1 + 1
- 3
- '''
- pass
- """)
- reprec = testdir.inline_run(p, "--doctest-modules",
- "--junit-xml=junit.xml")
- reprec.assertoutcome(failed=1)
-
-
-class TestLiterals:
-
- @pytest.mark.parametrize('config_mode', ['ini', 'comment'])
- def test_allow_unicode(self, testdir, config_mode):
- """Test that doctests which output unicode work in all python versions
- tested by pytest when the ALLOW_UNICODE option is used (either in
- the ini file or by an inline comment).
- """
- if config_mode == 'ini':
- testdir.makeini('''
- [pytest]
- doctest_optionflags = ALLOW_UNICODE
- ''')
- comment = ''
- else:
- comment = '#doctest: +ALLOW_UNICODE'
-
- testdir.maketxtfile(test_doc="""
- >>> b'12'.decode('ascii') {comment}
- '12'
- """.format(comment=comment))
- testdir.makepyfile(foo="""
- def foo():
- '''
- >>> b'12'.decode('ascii') {comment}
- '12'
- '''
- """.format(comment=comment))
- reprec = testdir.inline_run("--doctest-modules")
- reprec.assertoutcome(passed=2)
-
- @pytest.mark.parametrize('config_mode', ['ini', 'comment'])
- def test_allow_bytes(self, testdir, config_mode):
- """Test that doctests which output bytes work in all python versions
- tested by pytest when the ALLOW_BYTES option is used (either in
- the ini file or by an inline comment)(#1287).
- """
- if config_mode == 'ini':
- testdir.makeini('''
- [pytest]
- doctest_optionflags = ALLOW_BYTES
- ''')
- comment = ''
- else:
- comment = '#doctest: +ALLOW_BYTES'
-
- testdir.maketxtfile(test_doc="""
- >>> b'foo' {comment}
- 'foo'
- """.format(comment=comment))
- testdir.makepyfile(foo="""
- def foo():
- '''
- >>> b'foo' {comment}
- 'foo'
- '''
- """.format(comment=comment))
- reprec = testdir.inline_run("--doctest-modules")
- reprec.assertoutcome(passed=2)
-
- def test_unicode_string(self, testdir):
- """Test that doctests which output unicode fail in Python 2 when
- the ALLOW_UNICODE option is not used. The same test should pass
- in Python 3.
- """
- testdir.maketxtfile(test_doc="""
- >>> b'12'.decode('ascii')
- '12'
- """)
- reprec = testdir.inline_run()
- passed = int(sys.version_info[0] >= 3)
- reprec.assertoutcome(passed=passed, failed=int(not passed))
-
- def test_bytes_literal(self, testdir):
- """Test that doctests which output bytes fail in Python 3 when
- the ALLOW_BYTES option is not used. The same test should pass
- in Python 2 (#1287).
- """
- testdir.maketxtfile(test_doc="""
- >>> b'foo'
- 'foo'
- """)
- reprec = testdir.inline_run()
- passed = int(sys.version_info[0] == 2)
- reprec.assertoutcome(passed=passed, failed=int(not passed))
-
-
-class TestDoctestSkips:
- """
- If all examples in a doctest are skipped due to the SKIP option, then
- the tests should be SKIPPED rather than PASSED. (#957)
- """
-
- @pytest.fixture(params=['text', 'module'])
- def makedoctest(self, testdir, request):
- def makeit(doctest):
- mode = request.param
- if mode == 'text':
- testdir.maketxtfile(doctest)
- else:
- assert mode == 'module'
- testdir.makepyfile('"""\n%s"""' % doctest)
-
- return makeit
-
- def test_one_skipped(self, testdir, makedoctest):
- makedoctest("""
- >>> 1 + 1 # doctest: +SKIP
- 2
- >>> 2 + 2
- 4
- """)
- reprec = testdir.inline_run("--doctest-modules")
- reprec.assertoutcome(passed=1)
-
- def test_one_skipped_failed(self, testdir, makedoctest):
- makedoctest("""
- >>> 1 + 1 # doctest: +SKIP
- 2
- >>> 2 + 2
- 200
- """)
- reprec = testdir.inline_run("--doctest-modules")
- reprec.assertoutcome(failed=1)
-
- def test_all_skipped(self, testdir, makedoctest):
- makedoctest("""
- >>> 1 + 1 # doctest: +SKIP
- 2
- >>> 2 + 2 # doctest: +SKIP
- 200
- """)
- reprec = testdir.inline_run("--doctest-modules")
- reprec.assertoutcome(skipped=1)
-
-
-class TestDoctestAutoUseFixtures:
-
- SCOPES = ['module', 'session', 'class', 'function']
-
- def test_doctest_module_session_fixture(self, testdir):
- """Test that session fixtures are initialized for doctest modules (#768)
- """
- # session fixture which changes some global data, which will
- # be accessed by doctests in a module
- testdir.makeconftest("""
- import pytest
- import sys
-
- @pytest.yield_fixture(autouse=True, scope='session')
- def myfixture():
- assert not hasattr(sys, 'pytest_session_data')
- sys.pytest_session_data = 1
- yield
- del sys.pytest_session_data
- """)
- testdir.makepyfile(foo="""
- import sys
-
- def foo():
- '''
- >>> assert sys.pytest_session_data == 1
- '''
-
- def bar():
- '''
- >>> assert sys.pytest_session_data == 1
- '''
- """)
- result = testdir.runpytest("--doctest-modules")
- result.stdout.fnmatch_lines('*2 passed*')
-
- @pytest.mark.parametrize('scope', SCOPES)
- @pytest.mark.parametrize('enable_doctest', [True, False])
- def test_fixture_scopes(self, testdir, scope, enable_doctest):
- """Test that auto-use fixtures work properly with doctest modules.
- See #1057 and #1100.
- """
- testdir.makeconftest('''
- import pytest
-
- @pytest.fixture(autouse=True, scope="{scope}")
- def auto(request):
- return 99
- '''.format(scope=scope))
- testdir.makepyfile(test_1='''
- def test_foo():
- """
- >>> getfixture('auto') + 1
- 100
- """
- def test_bar():
- assert 1
- ''')
- params = ('--doctest-modules',) if enable_doctest else ()
- passes = 3 if enable_doctest else 2
- result = testdir.runpytest(*params)
- result.stdout.fnmatch_lines(['*=== %d passed in *' % passes])
-
- @pytest.mark.parametrize('scope', SCOPES)
- @pytest.mark.parametrize('autouse', [True, False])
- @pytest.mark.parametrize('use_fixture_in_doctest', [True, False])
- def test_fixture_module_doctest_scopes(self, testdir, scope, autouse,
- use_fixture_in_doctest):
- """Test that auto-use fixtures work properly with doctest files.
- See #1057 and #1100.
- """
- testdir.makeconftest('''
- import pytest
-
- @pytest.fixture(autouse={autouse}, scope="{scope}")
- def auto(request):
- return 99
- '''.format(scope=scope, autouse=autouse))
- if use_fixture_in_doctest:
- testdir.maketxtfile(test_doc="""
- >>> getfixture('auto')
- 99
- """)
- else:
- testdir.maketxtfile(test_doc="""
- >>> 1 + 1
- 2
- """)
- result = testdir.runpytest('--doctest-modules')
- assert 'FAILURES' not in str(result.stdout.str())
- result.stdout.fnmatch_lines(['*=== 1 passed in *'])
-
- @pytest.mark.parametrize('scope', SCOPES)
- def test_auto_use_request_attributes(self, testdir, scope):
- """Check that all attributes of a request in an autouse fixture
- behave as expected when requested for a doctest item.
- """
- testdir.makeconftest('''
- import pytest
-
- @pytest.fixture(autouse=True, scope="{scope}")
- def auto(request):
- if "{scope}" == 'module':
- assert request.module is None
- if "{scope}" == 'class':
- assert request.cls is None
- if "{scope}" == 'function':
- assert request.function is None
- return 99
- '''.format(scope=scope))
- testdir.maketxtfile(test_doc="""
- >>> 1 + 1
- 2
- """)
- result = testdir.runpytest('--doctest-modules')
- assert 'FAILURES' not in str(result.stdout.str())
- result.stdout.fnmatch_lines(['*=== 1 passed in *'])
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_genscript.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_genscript.py
deleted file mode 100644
index 1260a5a6b26..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_genscript.py
+++ /dev/null
@@ -1,51 +0,0 @@
-import pytest
-import sys
-
-
-@pytest.fixture(scope="module")
-def standalone(request):
- return Standalone(request)
-
-class Standalone:
- def __init__(self, request):
- self.testdir = request.getfuncargvalue("testdir")
- script = "mypytest"
- result = self.testdir.runpytest("--genscript=%s" % script)
- assert result.ret == 0
- self.script = self.testdir.tmpdir.join(script)
- assert self.script.check()
-
- def run(self, anypython, testdir, *args):
- return testdir._run(anypython, self.script, *args)
-
-def test_gen(testdir, anypython, standalone):
- if sys.version_info >= (2,7):
- result = testdir._run(anypython, "-c",
- "import sys;print (sys.version_info >=(2,7))")
- assert result.ret == 0
- if result.stdout.str() == "False":
- pytest.skip("genscript called from python2.7 cannot work "
- "earlier python versions")
- result = standalone.run(anypython, testdir, '--version')
- if result.ret == 2:
- result.stderr.fnmatch_lines(["*ERROR: setuptools not installed*"])
- elif result.ret == 0:
- result.stderr.fnmatch_lines([
- "*imported from*mypytest*"
- ])
- p = testdir.makepyfile("def test_func(): assert 0")
- result = standalone.run(anypython, testdir, p)
- assert result.ret != 0
- else:
- pytest.fail("Unexpected return code")
-
-
-def test_freeze_includes():
- """
- Smoke test for freeze_includes(), to ensure that it works across all
- supported python versions.
- """
- includes = pytest.freeze_includes()
- assert len(includes) > 1
- assert '_pytest.genscript' in includes
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_helpconfig.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_helpconfig.py
deleted file mode 100644
index 9f8d87b7cb5..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_helpconfig.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from _pytest.main import EXIT_NOTESTSCOLLECTED
-import pytest
-
-def test_version(testdir, pytestconfig):
- result = testdir.runpytest("--version")
- assert result.ret == 0
- #p = py.path.local(py.__file__).dirpath()
- result.stderr.fnmatch_lines([
- '*pytest*%s*imported from*' % (pytest.__version__, )
- ])
- if pytestconfig.pluginmanager.list_plugin_distinfo():
- result.stderr.fnmatch_lines([
- "*setuptools registered plugins:",
- "*at*",
- ])
-
-def test_help(testdir):
- result = testdir.runpytest("--help")
- assert result.ret == 0
- result.stdout.fnmatch_lines("""
- *-v*verbose*
- *setup.cfg*
- *minversion*
- *to see*markers*py.test --markers*
- *to see*fixtures*py.test --fixtures*
- """)
-
-def test_hookvalidation_unknown(testdir):
- testdir.makeconftest("""
- def pytest_hello(xyz):
- pass
- """)
- result = testdir.runpytest()
- assert result.ret != 0
- result.stderr.fnmatch_lines([
- '*unknown hook*pytest_hello*'
- ])
-
-def test_hookvalidation_optional(testdir):
- testdir.makeconftest("""
- import pytest
- @pytest.hookimpl(optionalhook=True)
- def pytest_hello(xyz):
- pass
- """)
- result = testdir.runpytest()
- assert result.ret == EXIT_NOTESTSCOLLECTED
-
-def test_traceconfig(testdir):
- result = testdir.runpytest("--traceconfig")
- result.stdout.fnmatch_lines([
- "*using*pytest*py*",
- "*active plugins*",
- ])
-
-def test_debug(testdir, monkeypatch):
- result = testdir.runpytest_subprocess("--debug")
- assert result.ret == EXIT_NOTESTSCOLLECTED
- p = testdir.tmpdir.join("pytestdebug.log")
- assert "pytest_sessionstart" in p.read()
-
-def test_PYTEST_DEBUG(testdir, monkeypatch):
- monkeypatch.setenv("PYTEST_DEBUG", "1")
- result = testdir.runpytest_subprocess()
- assert result.ret == EXIT_NOTESTSCOLLECTED
- result.stderr.fnmatch_lines([
- "*pytest_plugin_registered*",
- "*manager*PluginManager*"
- ])
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_junitxml.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_junitxml.py
deleted file mode 100644
index 5960f882535..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_junitxml.py
+++ /dev/null
@@ -1,816 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from xml.dom import minidom
-from _pytest.main import EXIT_NOTESTSCOLLECTED
-import py
-import sys
-import os
-from _pytest.junitxml import LogXML
-import pytest
-
-
-def runandparse(testdir, *args):
- resultpath = testdir.tmpdir.join("junit.xml")
- result = testdir.runpytest("--junitxml=%s" % resultpath, *args)
- xmldoc = minidom.parse(str(resultpath))
- return result, DomNode(xmldoc)
-
-
-def assert_attr(node, **kwargs):
- __tracebackhide__ = True
-
- def nodeval(node, name):
- anode = node.getAttributeNode(name)
- if anode is not None:
- return anode.value
-
- expected = dict((name, str(value)) for name, value in kwargs.items())
- on_node = dict((name, nodeval(node, name)) for name in expected)
- assert on_node == expected
-
-
-class DomNode(object):
- def __init__(self, dom):
- self.__node = dom
-
- def __repr__(self):
- return self.__node.toxml()
-
- def find_first_by_tag(self, tag):
- return self.find_nth_by_tag(tag, 0)
-
- def _by_tag(self, tag):
- return self.__node.getElementsByTagName(tag)
-
- def find_nth_by_tag(self, tag, n):
- items = self._by_tag(tag)
- try:
- nth = items[n]
- except IndexError:
- pass
- else:
- return type(self)(nth)
-
- def find_by_tag(self, tag):
- t = type(self)
- return [t(x) for x in self.__node.getElementsByTagName(tag)]
-
- def __getitem__(self, key):
- node = self.__node.getAttributeNode(key)
- if node is not None:
- return node.value
-
- def assert_attr(self, **kwargs):
- __tracebackhide__ = True
- return assert_attr(self.__node, **kwargs)
-
- def toxml(self):
- return self.__node.toxml()
-
- @property
- def text(self):
- return self.__node.childNodes[0].wholeText
-
- @property
- def tag(self):
- return self.__node.tagName
-
- @property
- def next_siebling(self):
- return type(self)(self.__node.nextSibling)
-
-
-class TestPython:
- def test_summing_simple(self, testdir):
- testdir.makepyfile("""
- import pytest
- def test_pass():
- pass
- def test_fail():
- assert 0
- def test_skip():
- pytest.skip("")
- @pytest.mark.xfail
- def test_xfail():
- assert 0
- @pytest.mark.xfail
- def test_xpass():
- assert 1
- """)
- result, dom = runandparse(testdir)
- assert result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(name="pytest", errors=0, failures=1, skips=3, tests=2)
-
- def test_timing_function(self, testdir):
- testdir.makepyfile("""
- import time, pytest
- def setup_module():
- time.sleep(0.01)
- def teardown_module():
- time.sleep(0.01)
- def test_sleep():
- time.sleep(0.01)
- """)
- result, dom = runandparse(testdir)
- node = dom.find_first_by_tag("testsuite")
- tnode = node.find_first_by_tag("testcase")
- val = tnode["time"]
- assert round(float(val), 2) >= 0.03
-
- def test_setup_error(self, testdir):
- testdir.makepyfile("""
- def pytest_funcarg__arg(request):
- raise ValueError()
- def test_function(arg):
- pass
- """)
- result, dom = runandparse(testdir)
- assert result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(errors=1, tests=0)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(
- file="test_setup_error.py",
- line="2",
- classname="test_setup_error",
- name="test_function")
- fnode = tnode.find_first_by_tag("error")
- fnode.assert_attr(message="test setup failure")
- assert "ValueError" in fnode.toxml()
-
- def test_skip_contains_name_reason(self, testdir):
- testdir.makepyfile("""
- import pytest
- def test_skip():
- pytest.skip("hello23")
- """)
- result, dom = runandparse(testdir)
- assert result.ret == 0
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(skips=1)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(
- file="test_skip_contains_name_reason.py",
- line="1",
- classname="test_skip_contains_name_reason",
- name="test_skip")
- snode = tnode.find_first_by_tag("skipped")
- snode.assert_attr(type="pytest.skip", message="hello23", )
-
- def test_classname_instance(self, testdir):
- testdir.makepyfile("""
- class TestClass:
- def test_method(self):
- assert 0
- """)
- result, dom = runandparse(testdir)
- assert result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(failures=1)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(
- file="test_classname_instance.py",
- line="1",
- classname="test_classname_instance.TestClass",
- name="test_method")
-
- def test_classname_nested_dir(self, testdir):
- p = testdir.tmpdir.ensure("sub", "test_hello.py")
- p.write("def test_func(): 0/0")
- result, dom = runandparse(testdir)
- assert result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(failures=1)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(
- file=os.path.join("sub", "test_hello.py"),
- line="0",
- classname="sub.test_hello",
- name="test_func")
-
- def test_internal_error(self, testdir):
- testdir.makeconftest("def pytest_runtest_protocol(): 0 / 0")
- testdir.makepyfile("def test_function(): pass")
- result, dom = runandparse(testdir)
- assert result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(errors=1, tests=0)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(classname="pytest", name="internal")
- fnode = tnode.find_first_by_tag("error")
- fnode.assert_attr(message="internal error")
- assert "Division" in fnode.toxml()
-
- def test_failure_function(self, testdir):
- testdir.makepyfile("""
- import sys
- def test_fail():
- print ("hello-stdout")
- sys.stderr.write("hello-stderr\\n")
- raise ValueError(42)
- """)
-
- result, dom = runandparse(testdir)
- assert result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(failures=1, tests=1)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(
- file="test_failure_function.py",
- line="1",
- classname="test_failure_function",
- name="test_fail")
- fnode = tnode.find_first_by_tag("failure")
- fnode.assert_attr(message="ValueError: 42")
- assert "ValueError" in fnode.toxml()
- systemout = fnode.next_siebling
- assert systemout.tag == "system-out"
- assert "hello-stdout" in systemout.toxml()
- systemerr = systemout.next_siebling
- assert systemerr.tag == "system-err"
- assert "hello-stderr" in systemerr.toxml()
-
- def test_failure_verbose_message(self, testdir):
- testdir.makepyfile("""
- import sys
- def test_fail():
- assert 0, "An error"
- """)
-
- result, dom = runandparse(testdir)
- node = dom.find_first_by_tag("testsuite")
- tnode = node.find_first_by_tag("testcase")
- fnode = tnode.find_first_by_tag("failure")
- fnode.assert_attr(message="AssertionError: An error assert 0")
-
- def test_failure_escape(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.parametrize('arg1', "<&'", ids="<&'")
- def test_func(arg1):
- print(arg1)
- assert 0
- """)
- result, dom = runandparse(testdir)
- assert result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(failures=3, tests=3)
-
- for index, char in enumerate("<&'"):
-
- tnode = node.find_nth_by_tag("testcase", index)
- tnode.assert_attr(
- file="test_failure_escape.py",
- line="1",
- classname="test_failure_escape",
- name="test_func[%s]" % char)
- sysout = tnode.find_first_by_tag('system-out')
- text = sysout.text
- assert text == '%s\n' % char
-
- def test_junit_prefixing(self, testdir):
- testdir.makepyfile("""
- def test_func():
- assert 0
- class TestHello:
- def test_hello(self):
- pass
- """)
- result, dom = runandparse(testdir, "--junitprefix=xyz")
- assert result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(failures=1, tests=2)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(
- file="test_junit_prefixing.py",
- line="0",
- classname="xyz.test_junit_prefixing",
- name="test_func")
- tnode = node.find_nth_by_tag("testcase", 1)
- tnode.assert_attr(
- file="test_junit_prefixing.py",
- line="3",
- classname="xyz.test_junit_prefixing."
- "TestHello",
- name="test_hello")
-
- def test_xfailure_function(self, testdir):
- testdir.makepyfile("""
- import pytest
- def test_xfail():
- pytest.xfail("42")
- """)
- result, dom = runandparse(testdir)
- assert not result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(skips=1, tests=0)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(
- file="test_xfailure_function.py",
- line="1",
- classname="test_xfailure_function",
- name="test_xfail")
- fnode = tnode.find_first_by_tag("skipped")
- fnode.assert_attr(message="expected test failure")
- # assert "ValueError" in fnode.toxml()
-
- def test_xfailure_xpass(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.xfail
- def test_xpass():
- pass
- """)
- result, dom = runandparse(testdir)
- # assert result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(skips=1, tests=0)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(
- file="test_xfailure_xpass.py",
- line="1",
- classname="test_xfailure_xpass",
- name="test_xpass")
- fnode = tnode.find_first_by_tag("skipped")
- fnode.assert_attr(message="xfail-marked test passes unexpectedly")
- # assert "ValueError" in fnode.toxml()
-
- def test_collect_error(self, testdir):
- testdir.makepyfile("syntax error")
- result, dom = runandparse(testdir)
- assert result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(errors=1, tests=0)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(
- file="test_collect_error.py",
- name="test_collect_error")
- assert tnode["line"] is None
- fnode = tnode.find_first_by_tag("error")
- fnode.assert_attr(message="collection failure")
- assert "SyntaxError" in fnode.toxml()
-
- def test_collect_skipped(self, testdir):
- testdir.makepyfile("import pytest; pytest.skip('xyz')")
- result, dom = runandparse(testdir)
- assert result.ret == EXIT_NOTESTSCOLLECTED
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(skips=1, tests=0)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(
- file="test_collect_skipped.py",
- name="test_collect_skipped")
-
- # py.test doesn't give us a line here.
- assert tnode["line"] is None
-
- fnode = tnode.find_first_by_tag("skipped")
- fnode.assert_attr(message="collection skipped")
-
- def test_unicode(self, testdir):
- value = 'hx\xc4\x85\xc4\x87\n'
- testdir.makepyfile("""
- # coding: latin1
- def test_hello():
- print (%r)
- assert 0
- """ % value)
- result, dom = runandparse(testdir)
- assert result.ret == 1
- tnode = dom.find_first_by_tag("testcase")
- fnode = tnode.find_first_by_tag("failure")
- if not sys.platform.startswith("java"):
- assert "hx" in fnode.toxml()
-
- def test_assertion_binchars(self, testdir):
- """this test did fail when the escaping wasnt strict"""
- testdir.makepyfile("""
-
- M1 = '\x01\x02\x03\x04'
- M2 = '\x01\x02\x03\x05'
-
- def test_str_compare():
- assert M1 == M2
- """)
- result, dom = runandparse(testdir)
- print(dom.toxml())
-
- def test_pass_captures_stdout(self, testdir):
- testdir.makepyfile("""
- def test_pass():
- print('hello-stdout')
- """)
- result, dom = runandparse(testdir)
- node = dom.find_first_by_tag("testsuite")
- pnode = node.find_first_by_tag("testcase")
- systemout = pnode.find_first_by_tag("system-out")
- assert "hello-stdout" in systemout.toxml()
-
- def test_pass_captures_stderr(self, testdir):
- testdir.makepyfile("""
- import sys
- def test_pass():
- sys.stderr.write('hello-stderr')
- """)
- result, dom = runandparse(testdir)
- node = dom.find_first_by_tag("testsuite")
- pnode = node.find_first_by_tag("testcase")
- systemout = pnode.find_first_by_tag("system-err")
- assert "hello-stderr" in systemout.toxml()
-
- def test_setup_error_captures_stdout(self, testdir):
- testdir.makepyfile("""
- def pytest_funcarg__arg(request):
- print('hello-stdout')
- raise ValueError()
- def test_function(arg):
- pass
- """)
- result, dom = runandparse(testdir)
- node = dom.find_first_by_tag("testsuite")
- pnode = node.find_first_by_tag("testcase")
- systemout = pnode.find_first_by_tag("system-out")
- assert "hello-stdout" in systemout.toxml()
-
- def test_setup_error_captures_stderr(self, testdir):
- testdir.makepyfile("""
- import sys
- def pytest_funcarg__arg(request):
- sys.stderr.write('hello-stderr')
- raise ValueError()
- def test_function(arg):
- pass
- """)
- result, dom = runandparse(testdir)
- node = dom.find_first_by_tag("testsuite")
- pnode = node.find_first_by_tag("testcase")
- systemout = pnode.find_first_by_tag("system-err")
- assert "hello-stderr" in systemout.toxml()
-
-
-def test_mangle_test_address():
- from _pytest.junitxml import mangle_test_address
- address = '::'.join(
- ["a/my.py.thing.py", "Class", "()", "method", "[a-1-::]"])
- newnames = mangle_test_address(address)
- assert newnames == ["a.my.py.thing", "Class", "method", "[a-1-::]"]
-
-
-def test_dont_configure_on_slaves(tmpdir):
- gotten = []
-
- class FakeConfig:
- def __init__(self):
- self.pluginmanager = self
- self.option = self
-
- junitprefix = None
- # XXX: shouldnt need tmpdir ?
- xmlpath = str(tmpdir.join('junix.xml'))
- register = gotten.append
-
- fake_config = FakeConfig()
- from _pytest import junitxml
- junitxml.pytest_configure(fake_config)
- assert len(gotten) == 1
- FakeConfig.slaveinput = None
- junitxml.pytest_configure(fake_config)
- assert len(gotten) == 1
-
-
-class TestNonPython:
- def test_summing_simple(self, testdir):
- testdir.makeconftest("""
- import pytest
- def pytest_collect_file(path, parent):
- if path.ext == ".xyz":
- return MyItem(path, parent)
- class MyItem(pytest.Item):
- def __init__(self, path, parent):
- super(MyItem, self).__init__(path.basename, parent)
- self.fspath = path
- def runtest(self):
- raise ValueError(42)
- def repr_failure(self, excinfo):
- return "custom item runtest failed"
- """)
- testdir.tmpdir.join("myfile.xyz").write("hello")
- result, dom = runandparse(testdir)
- assert result.ret
- node = dom.find_first_by_tag("testsuite")
- node.assert_attr(errors=0, failures=1, skips=0, tests=1)
- tnode = node.find_first_by_tag("testcase")
- tnode.assert_attr(name="myfile.xyz")
- fnode = tnode.find_first_by_tag("failure")
- fnode.assert_attr(message="custom item runtest failed")
- assert "custom item runtest failed" in fnode.toxml()
-
-
-def test_nullbyte(testdir):
- # A null byte can not occur in XML (see section 2.2 of the spec)
- testdir.makepyfile("""
- import sys
- def test_print_nullbyte():
- sys.stdout.write('Here the null -->' + chr(0) + '<--')
- sys.stdout.write('In repr form -->' + repr(chr(0)) + '<--')
- assert False
- """)
- xmlf = testdir.tmpdir.join('junit.xml')
- testdir.runpytest('--junitxml=%s' % xmlf)
- text = xmlf.read()
- assert '\x00' not in text
- assert '#x00' in text
-
-
-def test_nullbyte_replace(testdir):
- # Check if the null byte gets replaced
- testdir.makepyfile("""
- import sys
- def test_print_nullbyte():
- sys.stdout.write('Here the null -->' + chr(0) + '<--')
- sys.stdout.write('In repr form -->' + repr(chr(0)) + '<--')
- assert False
- """)
- xmlf = testdir.tmpdir.join('junit.xml')
- testdir.runpytest('--junitxml=%s' % xmlf)
- text = xmlf.read()
- assert '#x0' in text
-
-
-def test_invalid_xml_escape():
- # Test some more invalid xml chars, the full range should be
- # tested really but let's just thest the edges of the ranges
- # intead.
- # XXX This only tests low unicode character points for now as
- # there are some issues with the testing infrastructure for
- # the higher ones.
- # XXX Testing 0xD (\r) is tricky as it overwrites the just written
- # line in the output, so we skip it too.
- global unichr
- try:
- unichr(65)
- except NameError:
- unichr = chr
- invalid = (0x00, 0x1, 0xB, 0xC, 0xE, 0x19, 27, # issue #126
- 0xD800, 0xDFFF, 0xFFFE, 0x0FFFF) # , 0x110000)
- valid = (0x9, 0xA, 0x20, )
- # 0xD, 0xD7FF, 0xE000, 0xFFFD, 0x10000, 0x10FFFF)
-
- from _pytest.junitxml import bin_xml_escape
-
- for i in invalid:
- got = bin_xml_escape(unichr(i)).uniobj
- if i <= 0xFF:
- expected = '#x%02X' % i
- else:
- expected = '#x%04X' % i
- assert got == expected
- for i in valid:
- assert chr(i) == bin_xml_escape(unichr(i)).uniobj
-
-
-def test_logxml_path_expansion(tmpdir, monkeypatch):
- home_tilde = py.path.local(os.path.expanduser('~')).join('test.xml')
-
- xml_tilde = LogXML('~%stest.xml' % tmpdir.sep, None)
- assert xml_tilde.logfile == home_tilde
-
- # this is here for when $HOME is not set correct
- monkeypatch.setenv("HOME", tmpdir)
- home_var = os.path.normpath(os.path.expandvars('$HOME/test.xml'))
-
- xml_var = LogXML('$HOME%stest.xml' % tmpdir.sep, None)
- assert xml_var.logfile == home_var
-
-
-def test_logxml_changingdir(testdir):
- testdir.makepyfile("""
- def test_func():
- import os
- os.chdir("a")
- """)
- testdir.tmpdir.mkdir("a")
- result = testdir.runpytest("--junitxml=a/x.xml")
- assert result.ret == 0
- assert testdir.tmpdir.join("a/x.xml").check()
-
-
-def test_logxml_makedir(testdir):
- """--junitxml should automatically create directories for the xml file"""
- testdir.makepyfile("""
- def test_pass():
- pass
- """)
- result = testdir.runpytest("--junitxml=path/to/results.xml")
- assert result.ret == 0
- assert testdir.tmpdir.join("path/to/results.xml").check()
-
-
-def test_escaped_parametrized_names_xml(testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.parametrize('char', ["\\x00"])
- def test_func(char):
- assert char
- """)
- result, dom = runandparse(testdir)
- assert result.ret == 0
- node = dom.find_first_by_tag("testcase")
- node.assert_attr(name="test_func[#x00]")
-
-
-def test_double_colon_split_function_issue469(testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.parametrize('param', ["double::colon"])
- def test_func(param):
- pass
- """)
- result, dom = runandparse(testdir)
- assert result.ret == 0
- node = dom.find_first_by_tag("testcase")
- node.assert_attr(classname="test_double_colon_split_function_issue469")
- node.assert_attr(name='test_func[double::colon]')
-
-
-def test_double_colon_split_method_issue469(testdir):
- testdir.makepyfile("""
- import pytest
- class TestClass:
- @pytest.mark.parametrize('param', ["double::colon"])
- def test_func(self, param):
- pass
- """)
- result, dom = runandparse(testdir)
- assert result.ret == 0
- node = dom.find_first_by_tag("testcase")
- node.assert_attr(
- classname="test_double_colon_split_method_issue469.TestClass")
- node.assert_attr(name='test_func[double::colon]')
-
-
-def test_unicode_issue368(testdir):
- path = testdir.tmpdir.join("test.xml")
- log = LogXML(str(path), None)
- ustr = py.builtin._totext("ВНИ!", "utf-8")
- from _pytest.runner import BaseReport
-
- class Report(BaseReport):
- longrepr = ustr
- sections = []
- nodeid = "something"
- location = 'tests/filename.py', 42, 'TestClass.method'
-
- test_report = Report()
-
- # hopefully this is not too brittle ...
- log.pytest_sessionstart()
- node_reporter = log._opentestcase(test_report)
- node_reporter.append_failure(test_report)
- node_reporter.append_collect_error(test_report)
- node_reporter.append_collect_skipped(test_report)
- node_reporter.append_error(test_report)
- test_report.longrepr = "filename", 1, ustr
- node_reporter.append_skipped(test_report)
- test_report.longrepr = "filename", 1, "Skipped: 卡嘣嘣"
- node_reporter.append_skipped(test_report)
- test_report.wasxfail = ustr
- node_reporter.append_skipped(test_report)
- log.pytest_sessionfinish()
-
-
-def test_record_property(testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture
- def other(record_xml_property):
- record_xml_property("bar", 1)
- def test_record(record_xml_property, other):
- record_xml_property("foo", "<1");
- """)
- result, dom = runandparse(testdir, '-rw')
- node = dom.find_first_by_tag("testsuite")
- tnode = node.find_first_by_tag("testcase")
- psnode = tnode.find_first_by_tag('properties')
- pnodes = psnode.find_by_tag('property')
- pnodes[0].assert_attr(name="bar", value="1")
- pnodes[1].assert_attr(name="foo", value="<1")
- result.stdout.fnmatch_lines('*C3*test_record_property.py*experimental*')
-
-
-def test_record_property_same_name(testdir):
- testdir.makepyfile("""
- def test_record_with_same_name(record_xml_property):
- record_xml_property("foo", "bar")
- record_xml_property("foo", "baz")
- """)
- result, dom = runandparse(testdir, '-rw')
- node = dom.find_first_by_tag("testsuite")
- tnode = node.find_first_by_tag("testcase")
- psnode = tnode.find_first_by_tag('properties')
- pnodes = psnode.find_by_tag('property')
- pnodes[0].assert_attr(name="foo", value="bar")
- pnodes[1].assert_attr(name="foo", value="baz")
-
-
-def test_random_report_log_xdist(testdir):
- """xdist calls pytest_runtest_logreport as they are executed by the slaves,
- with nodes from several nodes overlapping, so junitxml must cope with that
- to produce correct reports. #1064
- """
- pytest.importorskip('xdist')
- testdir.makepyfile("""
- import pytest, time
- @pytest.mark.parametrize('i', list(range(30)))
- def test_x(i):
- assert i != 22
- """)
- _, dom = runandparse(testdir, '-n2')
- suite_node = dom.find_first_by_tag("testsuite")
- failed = []
- for case_node in suite_node.find_by_tag("testcase"):
- if case_node.find_first_by_tag('failure'):
- failed.append(case_node['name'])
-
- assert failed == ['test_x[22]']
-
-
-def test_runs_twice(testdir):
- f = testdir.makepyfile('''
- def test_pass():
- pass
- ''')
-
- result, dom = runandparse(testdir, f, f)
- assert 'INTERNALERROR' not in result.stdout.str()
- first, second = [x['classname'] for x in dom.find_by_tag("testcase")]
- assert first == second
-
-
-@pytest.mark.xfail(reason='hangs', run=False)
-def test_runs_twice_xdist(testdir):
- pytest.importorskip('xdist')
- f = testdir.makepyfile('''
- def test_pass():
- pass
- ''')
-
- result, dom = runandparse(
- testdir, f,
- '--dist', 'each', '--tx', '2*popen',)
- assert 'INTERNALERROR' not in result.stdout.str()
- first, second = [x['classname'] for x in dom.find_by_tag("testcase")]
- assert first == second
-
-
-def test_fancy_items_regression(testdir):
- # issue 1259
- testdir.makeconftest("""
- import pytest
- class FunItem(pytest.Item):
- def runtest(self):
- pass
- class NoFunItem(pytest.Item):
- def runtest(self):
- pass
-
- class FunCollector(pytest.File):
- def collect(self):
- return [
- FunItem('a', self),
- NoFunItem('a', self),
- NoFunItem('b', self),
- ]
-
- def pytest_collect_file(path, parent):
- if path.check(ext='.py'):
- return FunCollector(path, parent)
- """)
-
- testdir.makepyfile('''
- def test_pass():
- pass
- ''')
-
- result, dom = runandparse(testdir)
-
- assert 'INTERNALERROR' not in result.stdout.str()
-
- items = sorted(
- '%(classname)s %(name)s %(file)s' % x
-
- for x in dom.find_by_tag("testcase"))
- import pprint
- pprint.pprint(items)
- assert items == [
- u'conftest a conftest.py',
- u'conftest a conftest.py',
- u'conftest b conftest.py',
- u'test_fancy_items_regression a test_fancy_items_regression.py',
- u'test_fancy_items_regression a test_fancy_items_regression.py',
- u'test_fancy_items_regression b test_fancy_items_regression.py',
- u'test_fancy_items_regression test_pass'
- u' test_fancy_items_regression.py',
- ]
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_mark.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_mark.py
deleted file mode 100644
index aa1be6f7c66..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_mark.py
+++ /dev/null
@@ -1,672 +0,0 @@
-import os
-
-import py, pytest
-from _pytest.mark import MarkGenerator as Mark
-
-class TestMark:
- def test_markinfo_repr(self):
- from _pytest.mark import MarkInfo
- m = MarkInfo("hello", (1,2), {})
- repr(m)
-
- def test_pytest_exists_in_namespace_all(self):
- assert 'mark' in py.test.__all__
- assert 'mark' in pytest.__all__
-
- def test_pytest_mark_notcallable(self):
- mark = Mark()
- pytest.raises((AttributeError, TypeError), mark)
-
- def test_pytest_mark_name_starts_with_underscore(self):
- mark = Mark()
- pytest.raises(AttributeError, getattr, mark, '_some_name')
-
- def test_pytest_mark_bare(self):
- mark = Mark()
- def f():
- pass
- mark.hello(f)
- assert f.hello
-
- def test_pytest_mark_keywords(self):
- mark = Mark()
- def f():
- pass
- mark.world(x=3, y=4)(f)
- assert f.world
- assert f.world.kwargs['x'] == 3
- assert f.world.kwargs['y'] == 4
-
- def test_apply_multiple_and_merge(self):
- mark = Mark()
- def f():
- pass
- mark.world
- mark.world(x=3)(f)
- assert f.world.kwargs['x'] == 3
- mark.world(y=4)(f)
- assert f.world.kwargs['x'] == 3
- assert f.world.kwargs['y'] == 4
- mark.world(y=1)(f)
- assert f.world.kwargs['y'] == 1
- assert len(f.world.args) == 0
-
- def test_pytest_mark_positional(self):
- mark = Mark()
- def f():
- pass
- mark.world("hello")(f)
- assert f.world.args[0] == "hello"
- mark.world("world")(f)
-
- def test_pytest_mark_positional_func_and_keyword(self):
- mark = Mark()
- def f():
- raise Exception
- m = mark.world(f, omega="hello")
- def g():
- pass
- assert m(g) == g
- assert g.world.args[0] is f
- assert g.world.kwargs["omega"] == "hello"
-
- def test_pytest_mark_reuse(self):
- mark = Mark()
- def f():
- pass
- w = mark.some
- w("hello", reason="123")(f)
- assert f.some.args[0] == "hello"
- assert f.some.kwargs['reason'] == "123"
- def g():
- pass
- w("world", reason2="456")(g)
- assert g.some.args[0] == "world"
- assert 'reason' not in g.some.kwargs
- assert g.some.kwargs['reason2'] == "456"
-
-
-def test_marked_class_run_twice(testdir, request):
- """Test fails file is run twice that contains marked class.
- See issue#683.
- """
- py_file = testdir.makepyfile("""
- import pytest
- @pytest.mark.parametrize('abc', [1, 2, 3])
- class Test1(object):
- def test_1(self, abc):
- assert abc in [1, 2, 3]
- """)
- file_name = os.path.basename(py_file.strpath)
- rec = testdir.inline_run(file_name, file_name)
- rec.assertoutcome(passed=6)
-
-
-def test_ini_markers(testdir):
- testdir.makeini("""
- [pytest]
- markers =
- a1: this is a webtest marker
- a2: this is a smoke marker
- """)
- testdir.makepyfile("""
- def test_markers(pytestconfig):
- markers = pytestconfig.getini("markers")
- print (markers)
- assert len(markers) >= 2
- assert markers[0].startswith("a1:")
- assert markers[1].startswith("a2:")
- """)
- rec = testdir.inline_run()
- rec.assertoutcome(passed=1)
-
-def test_markers_option(testdir):
- testdir.makeini("""
- [pytest]
- markers =
- a1: this is a webtest marker
- a1some: another marker
- """)
- result = testdir.runpytest("--markers", )
- result.stdout.fnmatch_lines([
- "*a1*this is a webtest*",
- "*a1some*another marker",
- ])
-
-def test_markers_option_with_plugin_in_current_dir(testdir):
- testdir.makeconftest('pytest_plugins = "flip_flop"')
- testdir.makepyfile(flip_flop="""\
- def pytest_configure(config):
- config.addinivalue_line("markers", "flip:flop")
-
- def pytest_generate_tests(metafunc):
- try:
- mark = metafunc.function.flipper
- except AttributeError:
- return
- metafunc.parametrize("x", (10, 20))""")
- testdir.makepyfile("""\
- import pytest
- @pytest.mark.flipper
- def test_example(x):
- assert x""")
-
- result = testdir.runpytest("--markers")
- result.stdout.fnmatch_lines(["*flip*flop*"])
-
-
-def test_mark_on_pseudo_function(testdir):
- testdir.makepyfile("""
- import pytest
-
- @pytest.mark.r(lambda x: 0/0)
- def test_hello():
- pass
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
-def test_strict_prohibits_unregistered_markers(testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.unregisteredmark
- def test_hello():
- pass
- """)
- result = testdir.runpytest("--strict")
- assert result.ret != 0
- result.stdout.fnmatch_lines([
- "*unregisteredmark*not*registered*",
- ])
-
-@pytest.mark.parametrize("spec", [
- ("xyz", ("test_one",)),
- ("xyz and xyz2", ()),
- ("xyz2", ("test_two",)),
- ("xyz or xyz2", ("test_one", "test_two"),)
-])
-def test_mark_option(spec, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.xyz
- def test_one():
- pass
- @pytest.mark.xyz2
- def test_two():
- pass
- """)
- opt, passed_result = spec
- rec = testdir.inline_run("-m", opt)
- passed, skipped, fail = rec.listoutcomes()
- passed = [x.nodeid.split("::")[-1] for x in passed]
- assert len(passed) == len(passed_result)
- assert list(passed) == list(passed_result)
-
-@pytest.mark.parametrize("spec", [
- ("interface", ("test_interface",)),
- ("not interface", ("test_nointer",)),
-])
-def test_mark_option_custom(spec, testdir):
- testdir.makeconftest("""
- import pytest
- def pytest_collection_modifyitems(items):
- for item in items:
- if "interface" in item.nodeid:
- item.keywords["interface"] = pytest.mark.interface
- """)
- testdir.makepyfile("""
- def test_interface():
- pass
- def test_nointer():
- pass
- """)
- opt, passed_result = spec
- rec = testdir.inline_run("-m", opt)
- passed, skipped, fail = rec.listoutcomes()
- passed = [x.nodeid.split("::")[-1] for x in passed]
- assert len(passed) == len(passed_result)
- assert list(passed) == list(passed_result)
-
-@pytest.mark.parametrize("spec", [
- ("interface", ("test_interface",)),
- ("not interface", ("test_nointer", "test_pass")),
- ("pass", ("test_pass",)),
- ("not pass", ("test_interface", "test_nointer")),
-])
-def test_keyword_option_custom(spec, testdir):
- testdir.makepyfile("""
- def test_interface():
- pass
- def test_nointer():
- pass
- def test_pass():
- pass
- """)
- opt, passed_result = spec
- rec = testdir.inline_run("-k", opt)
- passed, skipped, fail = rec.listoutcomes()
- passed = [x.nodeid.split("::")[-1] for x in passed]
- assert len(passed) == len(passed_result)
- assert list(passed) == list(passed_result)
-
-
-@pytest.mark.parametrize("spec", [
- ("None", ("test_func[None]",)),
- ("1.3", ("test_func[1.3]",)),
- ("2-3", ("test_func[2-3]",))
-])
-def test_keyword_option_parametrize(spec, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.parametrize("arg", [None, 1.3, "2-3"])
- def test_func(arg):
- pass
- """)
- opt, passed_result = spec
- rec = testdir.inline_run("-k", opt)
- passed, skipped, fail = rec.listoutcomes()
- passed = [x.nodeid.split("::")[-1] for x in passed]
- assert len(passed) == len(passed_result)
- assert list(passed) == list(passed_result)
-
-
-def test_parametrized_collected_from_command_line(testdir):
- """Parametrized test not collected if test named specified
- in command line issue#649.
- """
- py_file = testdir.makepyfile("""
- import pytest
- @pytest.mark.parametrize("arg", [None, 1.3, "2-3"])
- def test_func(arg):
- pass
- """)
- file_name = os.path.basename(py_file.strpath)
- rec = testdir.inline_run(file_name + "::" + "test_func")
- rec.assertoutcome(passed=3)
-
-
-class TestFunctional:
-
- def test_mark_per_function(self, testdir):
- p = testdir.makepyfile("""
- import pytest
- @pytest.mark.hello
- def test_hello():
- assert hasattr(test_hello, 'hello')
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines(["*1 passed*"])
-
- def test_mark_per_module(self, testdir):
- item = testdir.getitem("""
- import pytest
- pytestmark = pytest.mark.hello
- def test_func():
- pass
- """)
- keywords = item.keywords
- assert 'hello' in keywords
-
- def test_marklist_per_class(self, testdir):
- item = testdir.getitem("""
- import pytest
- class TestClass:
- pytestmark = [pytest.mark.hello, pytest.mark.world]
- def test_func(self):
- assert TestClass.test_func.hello
- assert TestClass.test_func.world
- """)
- keywords = item.keywords
- assert 'hello' in keywords
-
- def test_marklist_per_module(self, testdir):
- item = testdir.getitem("""
- import pytest
- pytestmark = [pytest.mark.hello, pytest.mark.world]
- class TestClass:
- def test_func(self):
- assert TestClass.test_func.hello
- assert TestClass.test_func.world
- """)
- keywords = item.keywords
- assert 'hello' in keywords
- assert 'world' in keywords
-
- def test_mark_per_class_decorator(self, testdir):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.hello
- class TestClass:
- def test_func(self):
- assert TestClass.test_func.hello
- """)
- keywords = item.keywords
- assert 'hello' in keywords
-
- def test_mark_per_class_decorator_plus_existing_dec(self, testdir):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.hello
- class TestClass:
- pytestmark = pytest.mark.world
- def test_func(self):
- assert TestClass.test_func.hello
- assert TestClass.test_func.world
- """)
- keywords = item.keywords
- assert 'hello' in keywords
- assert 'world' in keywords
-
- def test_merging_markers(self, testdir):
- p = testdir.makepyfile("""
- import pytest
- pytestmark = pytest.mark.hello("pos1", x=1, y=2)
- class TestClass:
- # classlevel overrides module level
- pytestmark = pytest.mark.hello(x=3)
- @pytest.mark.hello("pos0", z=4)
- def test_func(self):
- pass
- """)
- items, rec = testdir.inline_genitems(p)
- item, = items
- keywords = item.keywords
- marker = keywords['hello']
- assert marker.args == ("pos0", "pos1")
- assert marker.kwargs == {'x': 1, 'y': 2, 'z': 4}
-
- # test the new __iter__ interface
- l = list(marker)
- assert len(l) == 3
- assert l[0].args == ("pos0",)
- assert l[1].args == ()
- assert l[2].args == ("pos1", )
-
- @pytest.mark.xfail(reason='unfixed')
- def test_merging_markers_deep(self, testdir):
- # issue 199 - propagate markers into nested classes
- p = testdir.makepyfile("""
- import pytest
- class TestA:
- pytestmark = pytest.mark.a
- def test_b(self):
- assert True
- class TestC:
- # this one didnt get marked
- def test_d(self):
- assert True
- """)
- items, rec = testdir.inline_genitems(p)
- for item in items:
- print (item, item.keywords)
- assert 'a' in item.keywords
-
- def test_mark_decorator_subclass_does_not_propagate_to_base(self, testdir):
- p = testdir.makepyfile("""
- import pytest
-
- @pytest.mark.a
- class Base: pass
-
- @pytest.mark.b
- class Test1(Base):
- def test_foo(self): pass
-
- class Test2(Base):
- def test_bar(self): pass
- """)
- items, rec = testdir.inline_genitems(p)
- self.assert_markers(items, test_foo=('a', 'b'), test_bar=('a',))
-
- def test_mark_decorator_baseclasses_merged(self, testdir):
- p = testdir.makepyfile("""
- import pytest
-
- @pytest.mark.a
- class Base: pass
-
- @pytest.mark.b
- class Base2(Base): pass
-
- @pytest.mark.c
- class Test1(Base2):
- def test_foo(self): pass
-
- class Test2(Base2):
- @pytest.mark.d
- def test_bar(self): pass
- """)
- items, rec = testdir.inline_genitems(p)
- self.assert_markers(items, test_foo=('a', 'b', 'c'),
- test_bar=('a', 'b', 'd'))
-
- def test_mark_with_wrong_marker(self, testdir):
- reprec = testdir.inline_runsource("""
- import pytest
- class pytestmark:
- pass
- def test_func():
- pass
- """)
- l = reprec.getfailedcollections()
- assert len(l) == 1
- assert "TypeError" in str(l[0].longrepr)
-
- def test_mark_dynamically_in_funcarg(self, testdir):
- testdir.makeconftest("""
- import pytest
- def pytest_funcarg__arg(request):
- request.applymarker(pytest.mark.hello)
- def pytest_terminal_summary(terminalreporter):
- l = terminalreporter.stats['passed']
- terminalreporter.writer.line("keyword: %s" % l[0].keywords)
- """)
- testdir.makepyfile("""
- def test_func(arg):
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "keyword: *hello*"
- ])
-
- def test_merging_markers_two_functions(self, testdir):
- p = testdir.makepyfile("""
- import pytest
- @pytest.mark.hello("pos1", z=4)
- @pytest.mark.hello("pos0", z=3)
- def test_func():
- pass
- """)
- items, rec = testdir.inline_genitems(p)
- item, = items
- keywords = item.keywords
- marker = keywords['hello']
- l = list(marker)
- assert len(l) == 2
- assert l[0].args == ("pos0",)
- assert l[1].args == ("pos1",)
-
- def test_no_marker_match_on_unmarked_names(self, testdir):
- p = testdir.makepyfile("""
- import pytest
- @pytest.mark.shouldmatch
- def test_marked():
- assert 1
-
- def test_unmarked():
- assert 1
- """)
- reprec = testdir.inline_run("-m", "test_unmarked", p)
- passed, skipped, failed = reprec.listoutcomes()
- assert len(passed) + len(skipped) + len(failed) == 0
- dlist = reprec.getcalls("pytest_deselected")
- deselected_tests = dlist[0].items
- assert len(deselected_tests) == 2
-
- def test_keywords_at_node_level(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope="session", autouse=True)
- def some(request):
- request.keywords["hello"] = 42
- assert "world" not in request.keywords
-
- @pytest.fixture(scope="function", autouse=True)
- def funcsetup(request):
- assert "world" in request.keywords
- assert "hello" in request.keywords
-
- @pytest.mark.world
- def test_function():
- pass
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_keyword_added_for_session(self, testdir):
- testdir.makeconftest("""
- import pytest
- def pytest_collection_modifyitems(session):
- session.add_marker("mark1")
- session.add_marker(pytest.mark.mark2)
- session.add_marker(pytest.mark.mark3)
- pytest.raises(ValueError, lambda:
- session.add_marker(10))
- """)
- testdir.makepyfile("""
- def test_some(request):
- assert "mark1" in request.keywords
- assert "mark2" in request.keywords
- assert "mark3" in request.keywords
- assert 10 not in request.keywords
- marker = request.node.get_marker("mark1")
- assert marker.name == "mark1"
- assert marker.args == ()
- assert marker.kwargs == {}
- """)
- reprec = testdir.inline_run("-m", "mark1")
- reprec.assertoutcome(passed=1)
-
- def assert_markers(self, items, **expected):
- """assert that given items have expected marker names applied to them.
- expected should be a dict of (item name -> seq of expected marker names)
-
- .. note:: this could be moved to ``testdir`` if proven to be useful
- to other modules.
- """
- from _pytest.mark import MarkInfo
- items = dict((x.name, x) for x in items)
- for name, expected_markers in expected.items():
- markers = items[name].keywords._markers
- marker_names = set([name for (name, v) in markers.items()
- if isinstance(v, MarkInfo)])
- assert marker_names == set(expected_markers)
-
-
-class TestKeywordSelection:
- def test_select_simple(self, testdir):
- file_test = testdir.makepyfile("""
- def test_one():
- assert 0
- class TestClass(object):
- def test_method_one(self):
- assert 42 == 43
- """)
- def check(keyword, name):
- reprec = testdir.inline_run("-s", "-k", keyword, file_test)
- passed, skipped, failed = reprec.listoutcomes()
- assert len(failed) == 1
- assert failed[0].nodeid.split("::")[-1] == name
- assert len(reprec.getcalls('pytest_deselected')) == 1
-
- for keyword in ['test_one', 'est_on']:
- check(keyword, 'test_one')
- check('TestClass and test', 'test_method_one')
-
- @pytest.mark.parametrize("keyword", [
- 'xxx', 'xxx and test_2', 'TestClass', 'xxx and not test_1',
- 'TestClass and test_2', 'xxx and TestClass and test_2'])
- def test_select_extra_keywords(self, testdir, keyword):
- p = testdir.makepyfile(test_select="""
- def test_1():
- pass
- class TestClass:
- def test_2(self):
- pass
- """)
- testdir.makepyfile(conftest="""
- import pytest
- @pytest.hookimpl(hookwrapper=True)
- def pytest_pycollect_makeitem(name):
- outcome = yield
- if name == "TestClass":
- item = outcome.get_result()
- item.extra_keyword_matches.add("xxx")
- """)
- reprec = testdir.inline_run(p.dirpath(), '-s', '-k', keyword)
- py.builtin.print_("keyword", repr(keyword))
- passed, skipped, failed = reprec.listoutcomes()
- assert len(passed) == 1
- assert passed[0].nodeid.endswith("test_2")
- dlist = reprec.getcalls("pytest_deselected")
- assert len(dlist) == 1
- assert dlist[0].items[0].name == 'test_1'
-
- def test_select_starton(self, testdir):
- threepass = testdir.makepyfile(test_threepass="""
- def test_one(): assert 1
- def test_two(): assert 1
- def test_three(): assert 1
- """)
- reprec = testdir.inline_run("-k", "test_two:", threepass)
- passed, skipped, failed = reprec.listoutcomes()
- assert len(passed) == 2
- assert not failed
- dlist = reprec.getcalls("pytest_deselected")
- assert len(dlist) == 1
- item = dlist[0].items[0]
- assert item.name == "test_one"
-
- def test_keyword_extra(self, testdir):
- p = testdir.makepyfile("""
- def test_one():
- assert 0
- test_one.mykeyword = True
- """)
- reprec = testdir.inline_run("-k", "mykeyword", p)
- passed, skipped, failed = reprec.countoutcomes()
- assert failed == 1
-
- @pytest.mark.xfail
- def test_keyword_extra_dash(self, testdir):
- p = testdir.makepyfile("""
- def test_one():
- assert 0
- test_one.mykeyword = True
- """)
- # with argparse the argument to an option cannot
- # start with '-'
- reprec = testdir.inline_run("-k", "-mykeyword", p)
- passed, skipped, failed = reprec.countoutcomes()
- assert passed + skipped + failed == 0
-
- def test_no_magic_values(self, testdir):
- """Make sure the tests do not match on magic values,
- no double underscored values, like '__dict__',
- and no instance values, like '()'.
- """
- p = testdir.makepyfile("""
- def test_one(): assert 1
- """)
- def assert_test_is_not_selected(keyword):
- reprec = testdir.inline_run("-k", keyword, p)
- passed, skipped, failed = reprec.countoutcomes()
- dlist = reprec.getcalls("pytest_deselected")
- assert passed + skipped + failed == 0
- deselected_tests = dlist[0].items
- assert len(deselected_tests) == 1
-
- assert_test_is_not_selected("__")
- assert_test_is_not_selected("()")
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_monkeypatch.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_monkeypatch.py
deleted file mode 100644
index 048c942c867..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_monkeypatch.py
+++ /dev/null
@@ -1,330 +0,0 @@
-import os
-import sys
-import textwrap
-
-import pytest
-from _pytest.monkeypatch import monkeypatch as MonkeyPatch
-
-
-def pytest_funcarg__mp(request):
- cwd = os.getcwd()
- sys_path = list(sys.path)
-
- def cleanup():
- sys.path[:] = sys_path
- os.chdir(cwd)
-
- request.addfinalizer(cleanup)
- return MonkeyPatch()
-
-
-def test_setattr():
- class A:
- x = 1
-
- monkeypatch = MonkeyPatch()
- pytest.raises(AttributeError, "monkeypatch.setattr(A, 'notexists', 2)")
- monkeypatch.setattr(A, 'y', 2, raising=False)
- assert A.y == 2
- monkeypatch.undo()
- assert not hasattr(A, 'y')
-
- monkeypatch = MonkeyPatch()
- monkeypatch.setattr(A, 'x', 2)
- assert A.x == 2
- monkeypatch.setattr(A, 'x', 3)
- assert A.x == 3
- monkeypatch.undo()
- assert A.x == 1
-
- A.x = 5
- monkeypatch.undo() # double-undo makes no modification
- assert A.x == 5
-
-
-class TestSetattrWithImportPath:
- def test_string_expression(self, monkeypatch):
- monkeypatch.setattr("os.path.abspath", lambda x: "hello2")
- assert os.path.abspath("123") == "hello2"
-
- def test_string_expression_class(self, monkeypatch):
- monkeypatch.setattr("_pytest.config.Config", 42)
- import _pytest
- assert _pytest.config.Config == 42
-
- def test_unicode_string(self, monkeypatch):
- monkeypatch.setattr("_pytest.config.Config", 42)
- import _pytest
- assert _pytest.config.Config == 42
- monkeypatch.delattr("_pytest.config.Config")
-
- def test_wrong_target(self, monkeypatch):
- pytest.raises(TypeError, lambda: monkeypatch.setattr(None, None))
-
- def test_unknown_import(self, monkeypatch):
- pytest.raises(ImportError,
- lambda: monkeypatch.setattr("unkn123.classx", None))
-
- def test_unknown_attr(self, monkeypatch):
- pytest.raises(AttributeError,
- lambda: monkeypatch.setattr("os.path.qweqwe", None))
-
- def test_unknown_attr_non_raising(self, monkeypatch):
- # https://github.com/pytest-dev/pytest/issues/746
- monkeypatch.setattr('os.path.qweqwe', 42, raising=False)
- assert os.path.qweqwe == 42
-
- def test_delattr(self, monkeypatch):
- monkeypatch.delattr("os.path.abspath")
- assert not hasattr(os.path, "abspath")
- monkeypatch.undo()
- assert os.path.abspath
-
-
-def test_delattr():
- class A:
- x = 1
-
- monkeypatch = MonkeyPatch()
- monkeypatch.delattr(A, 'x')
- assert not hasattr(A, 'x')
- monkeypatch.undo()
- assert A.x == 1
-
- monkeypatch = MonkeyPatch()
- monkeypatch.delattr(A, 'x')
- pytest.raises(AttributeError, "monkeypatch.delattr(A, 'y')")
- monkeypatch.delattr(A, 'y', raising=False)
- monkeypatch.setattr(A, 'x', 5, raising=False)
- assert A.x == 5
- monkeypatch.undo()
- assert A.x == 1
-
-
-def test_setitem():
- d = {'x': 1}
- monkeypatch = MonkeyPatch()
- monkeypatch.setitem(d, 'x', 2)
- monkeypatch.setitem(d, 'y', 1700)
- monkeypatch.setitem(d, 'y', 1700)
- assert d['x'] == 2
- assert d['y'] == 1700
- monkeypatch.setitem(d, 'x', 3)
- assert d['x'] == 3
- monkeypatch.undo()
- assert d['x'] == 1
- assert 'y' not in d
- d['x'] = 5
- monkeypatch.undo()
- assert d['x'] == 5
-
-
-def test_setitem_deleted_meanwhile():
- d = {}
- monkeypatch = MonkeyPatch()
- monkeypatch.setitem(d, 'x', 2)
- del d['x']
- monkeypatch.undo()
- assert not d
-
-
-@pytest.mark.parametrize("before", [True, False])
-def test_setenv_deleted_meanwhile(before):
- key = "qwpeoip123"
- if before:
- os.environ[key] = "world"
- monkeypatch = MonkeyPatch()
- monkeypatch.setenv(key, 'hello')
- del os.environ[key]
- monkeypatch.undo()
- if before:
- assert os.environ[key] == "world"
- del os.environ[key]
- else:
- assert key not in os.environ
-
-
-def test_delitem():
- d = {'x': 1}
- monkeypatch = MonkeyPatch()
- monkeypatch.delitem(d, 'x')
- assert 'x' not in d
- monkeypatch.delitem(d, 'y', raising=False)
- pytest.raises(KeyError, "monkeypatch.delitem(d, 'y')")
- assert not d
- monkeypatch.setitem(d, 'y', 1700)
- assert d['y'] == 1700
- d['hello'] = 'world'
- monkeypatch.setitem(d, 'x', 1500)
- assert d['x'] == 1500
- monkeypatch.undo()
- assert d == {'hello': 'world', 'x': 1}
-
-
-def test_setenv():
- monkeypatch = MonkeyPatch()
- monkeypatch.setenv('XYZ123', 2)
- import os
- assert os.environ['XYZ123'] == "2"
- monkeypatch.undo()
- assert 'XYZ123' not in os.environ
-
-
-def test_delenv():
- name = 'xyz1234'
- assert name not in os.environ
- monkeypatch = MonkeyPatch()
- pytest.raises(KeyError, "monkeypatch.delenv(%r, raising=True)" % name)
- monkeypatch.delenv(name, raising=False)
- monkeypatch.undo()
- os.environ[name] = "1"
- try:
- monkeypatch = MonkeyPatch()
- monkeypatch.delenv(name)
- assert name not in os.environ
- monkeypatch.setenv(name, "3")
- assert os.environ[name] == "3"
- monkeypatch.undo()
- assert os.environ[name] == "1"
- finally:
- if name in os.environ:
- del os.environ[name]
-
-
-def test_setenv_prepend():
- import os
- monkeypatch = MonkeyPatch()
- monkeypatch.setenv('XYZ123', 2, prepend="-")
- assert os.environ['XYZ123'] == "2"
- monkeypatch.setenv('XYZ123', 3, prepend="-")
- assert os.environ['XYZ123'] == "3-2"
- monkeypatch.undo()
- assert 'XYZ123' not in os.environ
-
-
-def test_monkeypatch_plugin(testdir):
- reprec = testdir.inline_runsource("""
- def test_method(monkeypatch):
- assert monkeypatch.__class__.__name__ == "monkeypatch"
- """)
- res = reprec.countoutcomes()
- assert tuple(res) == (1, 0, 0), res
-
-
-def test_syspath_prepend(mp):
- old = list(sys.path)
- mp.syspath_prepend('world')
- mp.syspath_prepend('hello')
- assert sys.path[0] == "hello"
- assert sys.path[1] == "world"
- mp.undo()
- assert sys.path == old
- mp.undo()
- assert sys.path == old
-
-
-def test_syspath_prepend_double_undo(mp):
- mp.syspath_prepend('hello world')
- mp.undo()
- sys.path.append('more hello world')
- mp.undo()
- assert sys.path[-1] == 'more hello world'
-
-
-def test_chdir_with_path_local(mp, tmpdir):
- mp.chdir(tmpdir)
- assert os.getcwd() == tmpdir.strpath
-
-
-def test_chdir_with_str(mp, tmpdir):
- mp.chdir(tmpdir.strpath)
- assert os.getcwd() == tmpdir.strpath
-
-
-def test_chdir_undo(mp, tmpdir):
- cwd = os.getcwd()
- mp.chdir(tmpdir)
- mp.undo()
- assert os.getcwd() == cwd
-
-
-def test_chdir_double_undo(mp, tmpdir):
- mp.chdir(tmpdir.strpath)
- mp.undo()
- tmpdir.chdir()
- mp.undo()
- assert os.getcwd() == tmpdir.strpath
-
-
-def test_issue185_time_breaks(testdir):
- testdir.makepyfile("""
- import time
- def test_m(monkeypatch):
- def f():
- raise Exception
- monkeypatch.setattr(time, "time", f)
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines("""
- *1 passed*
- """)
-
-
-def test_importerror(testdir):
- p = testdir.mkpydir("package")
- p.join("a.py").write(textwrap.dedent("""\
- import doesnotexist
-
- x = 1
- """))
- testdir.tmpdir.join("test_importerror.py").write(textwrap.dedent("""\
- def test_importerror(monkeypatch):
- monkeypatch.setattr('package.a.x', 2)
- """))
- result = testdir.runpytest()
- result.stdout.fnmatch_lines("""
- *import error in package.a: No module named {0}doesnotexist{0}*
- """.format("'" if sys.version_info > (3, 0) else ""))
-
-
-class SampleNew(object):
- @staticmethod
- def hello():
- return True
-
-
-class SampleNewInherit(SampleNew):
- pass
-
-
-class SampleOld:
- # oldstyle on python2
- @staticmethod
- def hello():
- return True
-
-
-class SampleOldInherit(SampleOld):
- pass
-
-
-@pytest.mark.parametrize('Sample', [
- SampleNew, SampleNewInherit,
- SampleOld, SampleOldInherit,
-], ids=['new', 'new-inherit', 'old', 'old-inherit'])
-def test_issue156_undo_staticmethod(Sample):
- monkeypatch = MonkeyPatch()
-
- monkeypatch.setattr(Sample, 'hello', None)
- assert Sample.hello is None
-
- monkeypatch.undo()
- assert Sample.hello()
-
-def test_issue1338_name_resolving():
- pytest.importorskip('requests')
- monkeypatch = MonkeyPatch()
- try:
- monkeypatch.delattr('requests.sessions.Session.request')
- finally:
- monkeypatch.undo() \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_nose.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_nose.py
deleted file mode 100644
index a5162381e47..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_nose.py
+++ /dev/null
@@ -1,394 +0,0 @@
-import pytest
-
-def setup_module(mod):
- mod.nose = pytest.importorskip("nose")
-
-def test_nose_setup(testdir):
- p = testdir.makepyfile("""
- l = []
- from nose.tools import with_setup
-
- @with_setup(lambda: l.append(1), lambda: l.append(2))
- def test_hello():
- assert l == [1]
-
- def test_world():
- assert l == [1,2]
-
- test_hello.setup = lambda: l.append(1)
- test_hello.teardown = lambda: l.append(2)
- """)
- result = testdir.runpytest(p, '-p', 'nose')
- result.assert_outcomes(passed=2)
-
-
-def test_setup_func_with_setup_decorator():
- from _pytest.nose import call_optional
- l = []
- class A:
- @pytest.fixture(autouse=True)
- def f(self):
- l.append(1)
- call_optional(A(), "f")
- assert not l
-
-
-def test_setup_func_not_callable():
- from _pytest.nose import call_optional
- class A:
- f = 1
- call_optional(A(), "f")
-
-def test_nose_setup_func(testdir):
- p = testdir.makepyfile("""
- from nose.tools import with_setup
-
- l = []
-
- def my_setup():
- a = 1
- l.append(a)
-
- def my_teardown():
- b = 2
- l.append(b)
-
- @with_setup(my_setup, my_teardown)
- def test_hello():
- print (l)
- assert l == [1]
-
- def test_world():
- print (l)
- assert l == [1,2]
-
- """)
- result = testdir.runpytest(p, '-p', 'nose')
- result.assert_outcomes(passed=2)
-
-
-def test_nose_setup_func_failure(testdir):
- p = testdir.makepyfile("""
- from nose.tools import with_setup
-
- l = []
- my_setup = lambda x: 1
- my_teardown = lambda x: 2
-
- @with_setup(my_setup, my_teardown)
- def test_hello():
- print (l)
- assert l == [1]
-
- def test_world():
- print (l)
- assert l == [1,2]
-
- """)
- result = testdir.runpytest(p, '-p', 'nose')
- result.stdout.fnmatch_lines([
- "*TypeError: <lambda>()*"
- ])
-
-
-def test_nose_setup_func_failure_2(testdir):
- testdir.makepyfile("""
- l = []
-
- my_setup = 1
- my_teardown = 2
-
- def test_hello():
- assert l == []
-
- test_hello.setup = my_setup
- test_hello.teardown = my_teardown
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
-def test_nose_setup_partial(testdir):
- pytest.importorskip("functools")
- p = testdir.makepyfile("""
- from functools import partial
-
- l = []
-
- def my_setup(x):
- a = x
- l.append(a)
-
- def my_teardown(x):
- b = x
- l.append(b)
-
- my_setup_partial = partial(my_setup, 1)
- my_teardown_partial = partial(my_teardown, 2)
-
- def test_hello():
- print (l)
- assert l == [1]
-
- def test_world():
- print (l)
- assert l == [1,2]
-
- test_hello.setup = my_setup_partial
- test_hello.teardown = my_teardown_partial
- """)
- result = testdir.runpytest(p, '-p', 'nose')
- result.stdout.fnmatch_lines([
- "*2 passed*"
- ])
-
-
-def test_nose_test_generator_fixtures(testdir):
- p = testdir.makepyfile("""
- # taken from nose-0.11.1 unit_tests/test_generator_fixtures.py
- from nose.tools import eq_
- called = []
-
- def outer_setup():
- called.append('outer_setup')
-
- def outer_teardown():
- called.append('outer_teardown')
-
- def inner_setup():
- called.append('inner_setup')
-
- def inner_teardown():
- called.append('inner_teardown')
-
- def test_gen():
- called[:] = []
- for i in range(0, 5):
- yield check, i
-
- def check(i):
- expect = ['outer_setup']
- for x in range(0, i):
- expect.append('inner_setup')
- expect.append('inner_teardown')
- expect.append('inner_setup')
- eq_(called, expect)
-
-
- test_gen.setup = outer_setup
- test_gen.teardown = outer_teardown
- check.setup = inner_setup
- check.teardown = inner_teardown
-
- class TestClass(object):
- def setup(self):
- print ("setup called in %s" % self)
- self.called = ['setup']
-
- def teardown(self):
- print ("teardown called in %s" % self)
- eq_(self.called, ['setup'])
- self.called.append('teardown')
-
- def test(self):
- print ("test called in %s" % self)
- for i in range(0, 5):
- yield self.check, i
-
- def check(self, i):
- print ("check called in %s" % self)
- expect = ['setup']
- #for x in range(0, i):
- # expect.append('setup')
- # expect.append('teardown')
- #expect.append('setup')
- eq_(self.called, expect)
- """)
- result = testdir.runpytest(p, '-p', 'nose')
- result.stdout.fnmatch_lines([
- "*10 passed*"
- ])
-
-
-def test_module_level_setup(testdir):
- testdir.makepyfile("""
- from nose.tools import with_setup
- items = {}
-
- def setup():
- items[1]=1
-
- def teardown():
- del items[1]
-
- def setup2():
- items[2] = 2
-
- def teardown2():
- del items[2]
-
- def test_setup_module_setup():
- assert items[1] == 1
-
- @with_setup(setup2, teardown2)
- def test_local_setup():
- assert items[2] == 2
- assert 1 not in items
- """)
- result = testdir.runpytest('-p', 'nose')
- result.stdout.fnmatch_lines([
- "*2 passed*",
- ])
-
-
-def test_nose_style_setup_teardown(testdir):
- testdir.makepyfile("""
- l = []
-
- def setup_module():
- l.append(1)
-
- def teardown_module():
- del l[0]
-
- def test_hello():
- assert l == [1]
-
- def test_world():
- assert l == [1]
- """)
- result = testdir.runpytest('-p', 'nose')
- result.stdout.fnmatch_lines([
- "*2 passed*",
- ])
-
-def test_nose_setup_ordering(testdir):
- testdir.makepyfile("""
- def setup_module(mod):
- mod.visited = True
-
- class TestClass:
- def setup(self):
- assert visited
- def test_first(self):
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*1 passed*",
- ])
-
-
-def test_apiwrapper_problem_issue260(testdir):
- # this would end up trying a call a optional teardown on the class
- # for plain unittests we dont want nose behaviour
- testdir.makepyfile("""
- import unittest
- class TestCase(unittest.TestCase):
- def setup(self):
- #should not be called in unittest testcases
- assert 0, 'setup'
- def teardown(self):
- #should not be called in unittest testcases
- assert 0, 'teardown'
- def setUp(self):
- print('setup')
- def tearDown(self):
- print('teardown')
- def test_fun(self):
- pass
- """)
- result = testdir.runpytest()
- result.assert_outcomes(passed=1)
-
-def test_setup_teardown_linking_issue265(testdir):
- # we accidentally didnt integrate nose setupstate with normal setupstate
- # this test ensures that won't happen again
- testdir.makepyfile('''
- import pytest
-
- class TestGeneric(object):
- def test_nothing(self):
- """Tests the API of the implementation (for generic and specialized)."""
-
- @pytest.mark.skipif("True", reason=
- "Skip tests to check if teardown is skipped as well.")
- class TestSkipTeardown(TestGeneric):
-
- def setup(self):
- """Sets up my specialized implementation for $COOL_PLATFORM."""
- raise Exception("should not call setup for skipped tests")
-
- def teardown(self):
- """Undoes the setup."""
- raise Exception("should not call teardown for skipped tests")
- ''')
- reprec = testdir.runpytest()
- reprec.assert_outcomes(passed=1, skipped=1)
-
-
-def test_SkipTest_during_collection(testdir):
- p = testdir.makepyfile("""
- import nose
- raise nose.SkipTest("during collection")
- def test_failing():
- assert False
- """)
- result = testdir.runpytest(p)
- result.assert_outcomes(skipped=1)
-
-
-def test_SkipTest_in_test(testdir):
- testdir.makepyfile("""
- import nose
-
- def test_skipping():
- raise nose.SkipTest("in test")
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(skipped=1)
-
-def test_istest_function_decorator(testdir):
- p = testdir.makepyfile("""
- import nose.tools
- @nose.tools.istest
- def not_test_prefix():
- pass
- """)
- result = testdir.runpytest(p)
- result.assert_outcomes(passed=1)
-
-def test_nottest_function_decorator(testdir):
- testdir.makepyfile("""
- import nose.tools
- @nose.tools.nottest
- def test_prefix():
- pass
- """)
- reprec = testdir.inline_run()
- assert not reprec.getfailedcollections()
- calls = reprec.getreports("pytest_runtest_logreport")
- assert not calls
-
-def test_istest_class_decorator(testdir):
- p = testdir.makepyfile("""
- import nose.tools
- @nose.tools.istest
- class NotTestPrefix:
- def test_method(self):
- pass
- """)
- result = testdir.runpytest(p)
- result.assert_outcomes(passed=1)
-
-def test_nottest_class_decorator(testdir):
- testdir.makepyfile("""
- import nose.tools
- @nose.tools.nottest
- class TestPrefix:
- def test_method(self):
- pass
- """)
- reprec = testdir.inline_run()
- assert not reprec.getfailedcollections()
- calls = reprec.getreports("pytest_runtest_logreport")
- assert not calls
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_parseopt.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_parseopt.py
deleted file mode 100644
index e45ee285409..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_parseopt.py
+++ /dev/null
@@ -1,287 +0,0 @@
-from __future__ import with_statement
-import sys
-import os
-import py, pytest
-from _pytest import config as parseopt
-
-@pytest.fixture
-def parser():
- return parseopt.Parser()
-
-class TestParser:
- def test_no_help_by_default(self, capsys):
- parser = parseopt.Parser(usage="xyz")
- pytest.raises(SystemExit, lambda: parser.parse(["-h"]))
- out, err = capsys.readouterr()
- assert err.find("error: unrecognized arguments") != -1
-
- def test_argument(self):
- with pytest.raises(parseopt.ArgumentError):
- # need a short or long option
- argument = parseopt.Argument()
- argument = parseopt.Argument('-t')
- assert argument._short_opts == ['-t']
- assert argument._long_opts == []
- assert argument.dest == 't'
- argument = parseopt.Argument('-t', '--test')
- assert argument._short_opts == ['-t']
- assert argument._long_opts == ['--test']
- assert argument.dest == 'test'
- argument = parseopt.Argument('-t', '--test', dest='abc')
- assert argument.dest == 'abc'
-
- def test_argument_type(self):
- argument = parseopt.Argument('-t', dest='abc', type='int')
- assert argument.type is int
- argument = parseopt.Argument('-t', dest='abc', type='string')
- assert argument.type is str
- argument = parseopt.Argument('-t', dest='abc', type=float)
- assert argument.type is float
- with pytest.raises(KeyError):
- argument = parseopt.Argument('-t', dest='abc', type='choice')
- argument = parseopt.Argument('-t', dest='abc', type='choice',
- choices=['red', 'blue'])
- assert argument.type is str
-
- def test_argument_processopt(self):
- argument = parseopt.Argument('-t', type=int)
- argument.default = 42
- argument.dest = 'abc'
- res = argument.attrs()
- assert res['default'] == 42
- assert res['dest'] == 'abc'
-
- def test_group_add_and_get(self, parser):
- group = parser.getgroup("hello", description="desc")
- assert group.name == "hello"
- assert group.description == "desc"
-
- def test_getgroup_simple(self, parser):
- group = parser.getgroup("hello", description="desc")
- assert group.name == "hello"
- assert group.description == "desc"
- group2 = parser.getgroup("hello")
- assert group2 is group
-
- def test_group_ordering(self, parser):
- parser.getgroup("1")
- parser.getgroup("2")
- parser.getgroup("3", after="1")
- groups = parser._groups
- groups_names = [x.name for x in groups]
- assert groups_names == list("132")
-
- def test_group_addoption(self):
- group = parseopt.OptionGroup("hello")
- group.addoption("--option1", action="store_true")
- assert len(group.options) == 1
- assert isinstance(group.options[0], parseopt.Argument)
-
- def test_group_shortopt_lowercase(self, parser):
- group = parser.getgroup("hello")
- pytest.raises(ValueError, """
- group.addoption("-x", action="store_true")
- """)
- assert len(group.options) == 0
- group._addoption("-x", action="store_true")
- assert len(group.options) == 1
-
- def test_parser_addoption(self, parser):
- group = parser.getgroup("custom options")
- assert len(group.options) == 0
- group.addoption("--option1", action="store_true")
- assert len(group.options) == 1
-
- def test_parse(self, parser):
- parser.addoption("--hello", dest="hello", action="store")
- args = parser.parse(['--hello', 'world'])
- assert args.hello == "world"
- assert not getattr(args, parseopt.FILE_OR_DIR)
-
- def test_parse2(self, parser):
- args = parser.parse([py.path.local()])
- assert getattr(args, parseopt.FILE_OR_DIR)[0] == py.path.local()
-
- def test_parse_known_args(self, parser):
- parser.parse_known_args([py.path.local()])
- parser.addoption("--hello", action="store_true")
- ns = parser.parse_known_args(["x", "--y", "--hello", "this"])
- assert ns.hello
- assert ns.file_or_dir == ['x']
-
- def test_parse_known_and_unknown_args(self, parser):
- parser.addoption("--hello", action="store_true")
- ns, unknown = parser.parse_known_and_unknown_args(["x", "--y",
- "--hello", "this"])
- assert ns.hello
- assert ns.file_or_dir == ['x']
- assert unknown == ['--y', 'this']
-
- def test_parse_will_set_default(self, parser):
- parser.addoption("--hello", dest="hello", default="x", action="store")
- option = parser.parse([])
- assert option.hello == "x"
- del option.hello
- parser.parse_setoption([], option)
- assert option.hello == "x"
-
- def test_parse_setoption(self, parser):
- parser.addoption("--hello", dest="hello", action="store")
- parser.addoption("--world", dest="world", default=42)
- class A: pass
- option = A()
- args = parser.parse_setoption(['--hello', 'world'], option)
- assert option.hello == "world"
- assert option.world == 42
- assert not args
-
- def test_parse_special_destination(self, parser):
- parser.addoption("--ultimate-answer", type=int)
- args = parser.parse(['--ultimate-answer', '42'])
- assert args.ultimate_answer == 42
-
- def test_parse_split_positional_arguments(self, parser):
- parser.addoption("-R", action='store_true')
- parser.addoption("-S", action='store_false')
- args = parser.parse(['-R', '4', '2', '-S'])
- assert getattr(args, parseopt.FILE_OR_DIR) == ['4', '2']
- args = parser.parse(['-R', '-S', '4', '2', '-R'])
- assert getattr(args, parseopt.FILE_OR_DIR) == ['4', '2']
- assert args.R == True
- assert args.S == False
- args = parser.parse(['-R', '4', '-S', '2'])
- assert getattr(args, parseopt.FILE_OR_DIR) == ['4', '2']
- assert args.R == True
- assert args.S == False
-
- def test_parse_defaultgetter(self):
- def defaultget(option):
- if not hasattr(option, 'type'):
- return
- if option.type is int:
- option.default = 42
- elif option.type is str:
- option.default = "world"
- parser = parseopt.Parser(processopt=defaultget)
- parser.addoption("--this", dest="this", type="int", action="store")
- parser.addoption("--hello", dest="hello", type="string", action="store")
- parser.addoption("--no", dest="no", action="store_true")
- option = parser.parse([])
- assert option.hello == "world"
- assert option.this == 42
- assert option.no is False
-
- def test_drop_short_helper(self):
- parser = py.std.argparse.ArgumentParser(formatter_class=parseopt.DropShorterLongHelpFormatter)
- parser.add_argument('-t', '--twoword', '--duo', '--two-word', '--two',
- help='foo').map_long_option = {'two': 'two-word'}
- # throws error on --deux only!
- parser.add_argument('-d', '--deuxmots', '--deux-mots',
- action='store_true', help='foo').map_long_option = {'deux': 'deux-mots'}
- parser.add_argument('-s', action='store_true', help='single short')
- parser.add_argument('--abc', '-a',
- action='store_true', help='bar')
- parser.add_argument('--klm', '-k', '--kl-m',
- action='store_true', help='bar')
- parser.add_argument('-P', '--pq-r', '-p', '--pqr',
- action='store_true', help='bar')
- parser.add_argument('--zwei-wort', '--zweiwort', '--zweiwort',
- action='store_true', help='bar')
- parser.add_argument('-x', '--exit-on-first', '--exitfirst',
- action='store_true', help='spam').map_long_option = {'exitfirst': 'exit-on-first'}
- parser.add_argument('files_and_dirs', nargs='*')
- args = parser.parse_args(['-k', '--duo', 'hallo', '--exitfirst'])
- assert args.twoword == 'hallo'
- assert args.klm is True
- assert args.zwei_wort is False
- assert args.exit_on_first is True
- assert args.s is False
- args = parser.parse_args(['--deux-mots'])
- with pytest.raises(AttributeError):
- assert args.deux_mots is True
- assert args.deuxmots is True
- args = parser.parse_args(['file', 'dir'])
- assert '|'.join(args.files_and_dirs) == 'file|dir'
-
- def test_drop_short_0(self, parser):
- parser.addoption('--funcarg', '--func-arg', action='store_true')
- parser.addoption('--abc-def', '--abc-def', action='store_true')
- parser.addoption('--klm-hij', action='store_true')
- args = parser.parse(['--funcarg', '--k'])
- assert args.funcarg is True
- assert args.abc_def is False
- assert args.klm_hij is True
-
- def test_drop_short_2(self, parser):
- parser.addoption('--func-arg', '--doit', action='store_true')
- args = parser.parse(['--doit'])
- assert args.func_arg is True
-
- def test_drop_short_3(self, parser):
- parser.addoption('--func-arg', '--funcarg', '--doit', action='store_true')
- args = parser.parse(['abcd'])
- assert args.func_arg is False
- assert args.file_or_dir == ['abcd']
-
- def test_drop_short_help0(self, parser, capsys):
- parser.addoption('--func-args', '--doit', help = 'foo',
- action='store_true')
- parser.parse([])
- help = parser.optparser.format_help()
- assert '--func-args, --doit foo' in help
-
- # testing would be more helpful with all help generated
- def test_drop_short_help1(self, parser, capsys):
- group = parser.getgroup("general")
- group.addoption('--doit', '--func-args', action='store_true', help='foo')
- group._addoption("-h", "--help", action="store_true", dest="help",
- help="show help message and configuration info")
- parser.parse(['-h'])
- help = parser.optparser.format_help()
- assert '-doit, --func-args foo' in help
-
-
-def test_argcomplete(testdir, monkeypatch):
- if not py.path.local.sysfind('bash'):
- pytest.skip("bash not available")
- script = str(testdir.tmpdir.join("test_argcomplete"))
- pytest_bin = sys.argv[0]
- if "py.test" not in os.path.basename(pytest_bin):
- pytest.skip("need to be run with py.test executable, not %s" %(pytest_bin,))
-
- with open(str(script), 'w') as fp:
- # redirect output from argcomplete to stdin and stderr is not trivial
- # http://stackoverflow.com/q/12589419/1307905
- # so we use bash
- fp.write('COMP_WORDBREAKS="$COMP_WORDBREAKS" %s 8>&1 9>&2' % pytest_bin)
- # alternative would be exteneded Testdir.{run(),_run(),popen()} to be able
- # to handle a keyword argument env that replaces os.environ in popen or
- # extends the copy, advantage: could not forget to restore
- monkeypatch.setenv('_ARGCOMPLETE', "1")
- monkeypatch.setenv('_ARGCOMPLETE_IFS',"\x0b")
- monkeypatch.setenv('COMP_WORDBREAKS', ' \\t\\n"\\\'><=;|&(:')
-
- arg = '--fu'
- monkeypatch.setenv('COMP_LINE', "py.test " + arg)
- monkeypatch.setenv('COMP_POINT', str(len("py.test " + arg)))
- result = testdir.run('bash', str(script), arg)
- if result.ret == 255:
- # argcomplete not found
- pytest.skip("argcomplete not available")
- elif not result.stdout.str():
- pytest.skip("bash provided no output, argcomplete not available?")
- else:
- if py.std.sys.version_info < (2,7):
- result.stdout.lines = result.stdout.lines[0].split('\x0b')
- result.stdout.fnmatch_lines(["--funcargs", "--fulltrace"])
- else:
- result.stdout.fnmatch_lines(["--funcargs", "--fulltrace"])
- if py.std.sys.version_info < (2,7):
- return
- os.mkdir('test_argcomplete.d')
- arg = 'test_argc'
- monkeypatch.setenv('COMP_LINE', "py.test " + arg)
- monkeypatch.setenv('COMP_POINT', str(len('py.test ' + arg)))
- result = testdir.run('bash', str(script), arg)
- result.stdout.fnmatch_lines(["test_argcomplete", "test_argcomplete.d/"])
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_pastebin.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_pastebin.py
deleted file mode 100644
index 03570a5c70b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_pastebin.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# encoding: utf-8
-import sys
-import pytest
-
-class TestPasteCapture:
-
- @pytest.fixture
- def pastebinlist(self, monkeypatch, request):
- pastebinlist = []
- plugin = request.config.pluginmanager.getplugin('pastebin')
- monkeypatch.setattr(plugin, 'create_new_paste', pastebinlist.append)
- return pastebinlist
-
- def test_failed(self, testdir, pastebinlist):
- testpath = testdir.makepyfile("""
- import pytest
- def test_pass():
- pass
- def test_fail():
- assert 0
- def test_skip():
- pytest.skip("")
- """)
- reprec = testdir.inline_run(testpath, "--paste=failed")
- assert len(pastebinlist) == 1
- s = pastebinlist[0]
- assert s.find("def test_fail") != -1
- assert reprec.countoutcomes() == [1,1,1]
-
- def test_all(self, testdir, pastebinlist):
- from _pytest.pytester import LineMatcher
- testpath = testdir.makepyfile("""
- import pytest
- def test_pass():
- pass
- def test_fail():
- assert 0
- def test_skip():
- pytest.skip("")
- """)
- reprec = testdir.inline_run(testpath, "--pastebin=all", '-v')
- assert reprec.countoutcomes() == [1,1,1]
- assert len(pastebinlist) == 1
- contents = pastebinlist[0].decode('utf-8')
- matcher = LineMatcher(contents.splitlines())
- matcher.fnmatch_lines([
- '*test_pass PASSED*',
- '*test_fail FAILED*',
- '*test_skip SKIPPED*',
- '*== 1 failed, 1 passed, 1 skipped in *'
- ])
-
- def test_non_ascii_paste_text(self, testdir):
- """Make sure that text which contains non-ascii characters is pasted
- correctly. See #1219.
- """
- testdir.makepyfile(test_unicode="""
- # encoding: utf-8
- def test():
- assert '☺' == 1
- """)
- result = testdir.runpytest('--pastebin=all')
- if sys.version_info[0] == 3:
- expected_msg = "*assert '☺' == 1*"
- else:
- expected_msg = "*assert '\\xe2\\x98\\xba' == 1*"
- result.stdout.fnmatch_lines([
- expected_msg,
- "*== 1 failed in *",
- '*Sending information to Paste Service*',
- ])
-
-
-class TestPaste:
-
- @pytest.fixture
- def pastebin(self, request):
- return request.config.pluginmanager.getplugin('pastebin')
-
- @pytest.fixture
- def mocked_urlopen(self, monkeypatch):
- """
- monkeypatch the actual urlopen calls done by the internal plugin
- function that connects to bpaste service.
- """
- calls = []
- def mocked(url, data):
- calls.append((url, data))
- class DummyFile:
- def read(self):
- # part of html of a normal response
- return b'View <a href="/raw/3c0c6750bd">raw</a>.'
- return DummyFile()
-
- if sys.version_info < (3, 0):
- import urllib
- monkeypatch.setattr(urllib, 'urlopen', mocked)
- else:
- import urllib.request
- monkeypatch.setattr(urllib.request, 'urlopen', mocked)
- return calls
-
- def test_create_new_paste(self, pastebin, mocked_urlopen):
- result = pastebin.create_new_paste(b'full-paste-contents')
- assert result == 'https://bpaste.net/show/3c0c6750bd'
- assert len(mocked_urlopen) == 1
- url, data = mocked_urlopen[0]
- assert type(data) is bytes
- lexer = 'python3' if sys.version_info[0] == 3 else 'python'
- assert url == 'https://bpaste.net'
- assert 'lexer=%s' % lexer in data.decode()
- assert 'code=full-paste-contents' in data.decode()
- assert 'expiry=1week' in data.decode()
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_pdb.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_pdb.py
deleted file mode 100644
index eeddcf0ae80..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_pdb.py
+++ /dev/null
@@ -1,313 +0,0 @@
-import sys
-
-import _pytest._code
-
-
-def runpdb_and_get_report(testdir, source):
- p = testdir.makepyfile(source)
- result = testdir.runpytest_inprocess("--pdb", p)
- reports = result.reprec.getreports("pytest_runtest_logreport")
- assert len(reports) == 3, reports # setup/call/teardown
- return reports[1]
-
-
-class TestPDB:
- def pytest_funcarg__pdblist(self, request):
- monkeypatch = request.getfuncargvalue("monkeypatch")
- pdblist = []
- def mypdb(*args):
- pdblist.append(args)
- plugin = request.config.pluginmanager.getplugin('pdb')
- monkeypatch.setattr(plugin, 'post_mortem', mypdb)
- return pdblist
-
- def test_pdb_on_fail(self, testdir, pdblist):
- rep = runpdb_and_get_report(testdir, """
- def test_func():
- assert 0
- """)
- assert rep.failed
- assert len(pdblist) == 1
- tb = _pytest._code.Traceback(pdblist[0][0])
- assert tb[-1].name == "test_func"
-
- def test_pdb_on_xfail(self, testdir, pdblist):
- rep = runpdb_and_get_report(testdir, """
- import pytest
- @pytest.mark.xfail
- def test_func():
- assert 0
- """)
- assert "xfail" in rep.keywords
- assert not pdblist
-
- def test_pdb_on_skip(self, testdir, pdblist):
- rep = runpdb_and_get_report(testdir, """
- import pytest
- def test_func():
- pytest.skip("hello")
- """)
- assert rep.skipped
- assert len(pdblist) == 0
-
- def test_pdb_on_BdbQuit(self, testdir, pdblist):
- rep = runpdb_and_get_report(testdir, """
- import bdb
- def test_func():
- raise bdb.BdbQuit
- """)
- assert rep.failed
- assert len(pdblist) == 0
-
- def test_pdb_interaction(self, testdir):
- p1 = testdir.makepyfile("""
- def test_1():
- i = 0
- assert i == 1
- """)
- child = testdir.spawn_pytest("--pdb %s" % p1)
- child.expect(".*def test_1")
- child.expect(".*i = 0")
- child.expect("(Pdb)")
- child.sendeof()
- rest = child.read().decode("utf8")
- assert "1 failed" in rest
- assert "def test_1" not in rest
- if child.isalive():
- child.wait()
-
- def test_pdb_interaction_capture(self, testdir):
- p1 = testdir.makepyfile("""
- def test_1():
- print("getrekt")
- assert False
- """)
- child = testdir.spawn_pytest("--pdb %s" % p1)
- child.expect("getrekt")
- child.expect("(Pdb)")
- child.sendeof()
- rest = child.read().decode("utf8")
- assert "1 failed" in rest
- assert "getrekt" not in rest
- if child.isalive():
- child.wait()
-
- def test_pdb_interaction_exception(self, testdir):
- p1 = testdir.makepyfile("""
- import pytest
- def globalfunc():
- pass
- def test_1():
- pytest.raises(ValueError, globalfunc)
- """)
- child = testdir.spawn_pytest("--pdb %s" % p1)
- child.expect(".*def test_1")
- child.expect(".*pytest.raises.*globalfunc")
- child.expect("(Pdb)")
- child.sendline("globalfunc")
- child.expect(".*function")
- child.sendeof()
- child.expect("1 failed")
- if child.isalive():
- child.wait()
-
- def test_pdb_interaction_on_collection_issue181(self, testdir):
- p1 = testdir.makepyfile("""
- import pytest
- xxx
- """)
- child = testdir.spawn_pytest("--pdb %s" % p1)
- #child.expect(".*import pytest.*")
- child.expect("(Pdb)")
- child.sendeof()
- child.expect("1 error")
- if child.isalive():
- child.wait()
-
- def test_pdb_interaction_on_internal_error(self, testdir):
- testdir.makeconftest("""
- def pytest_runtest_protocol():
- 0/0
- """)
- p1 = testdir.makepyfile("def test_func(): pass")
- child = testdir.spawn_pytest("--pdb %s" % p1)
- #child.expect(".*import pytest.*")
- child.expect("(Pdb)")
- child.sendeof()
- if child.isalive():
- child.wait()
-
- def test_pdb_interaction_capturing_simple(self, testdir):
- p1 = testdir.makepyfile("""
- import pytest
- def test_1():
- i = 0
- print ("hello17")
- pytest.set_trace()
- x = 3
- """)
- child = testdir.spawn_pytest(str(p1))
- child.expect("test_1")
- child.expect("x = 3")
- child.expect("(Pdb)")
- child.sendeof()
- rest = child.read().decode("utf-8")
- assert "1 failed" in rest
- assert "def test_1" in rest
- assert "hello17" in rest # out is captured
- if child.isalive():
- child.wait()
-
- def test_pdb_set_trace_interception(self, testdir):
- p1 = testdir.makepyfile("""
- import pdb
- def test_1():
- pdb.set_trace()
- """)
- child = testdir.spawn_pytest(str(p1))
- child.expect("test_1")
- child.expect("(Pdb)")
- child.sendeof()
- rest = child.read().decode("utf8")
- assert "1 failed" in rest
- assert "reading from stdin while output" not in rest
- if child.isalive():
- child.wait()
-
- def test_pdb_and_capsys(self, testdir):
- p1 = testdir.makepyfile("""
- import pytest
- def test_1(capsys):
- print ("hello1")
- pytest.set_trace()
- """)
- child = testdir.spawn_pytest(str(p1))
- child.expect("test_1")
- child.send("capsys.readouterr()\n")
- child.expect("hello1")
- child.sendeof()
- child.read()
- if child.isalive():
- child.wait()
-
- def test_set_trace_capturing_afterwards(self, testdir):
- p1 = testdir.makepyfile("""
- import pdb
- def test_1():
- pdb.set_trace()
- def test_2():
- print ("hello")
- assert 0
- """)
- child = testdir.spawn_pytest(str(p1))
- child.expect("test_1")
- child.send("c\n")
- child.expect("test_2")
- child.expect("Captured")
- child.expect("hello")
- child.sendeof()
- child.read()
- if child.isalive():
- child.wait()
-
- def test_pdb_interaction_doctest(self, testdir):
- p1 = testdir.makepyfile("""
- import pytest
- def function_1():
- '''
- >>> i = 0
- >>> assert i == 1
- '''
- """)
- child = testdir.spawn_pytest("--doctest-modules --pdb %s" % p1)
- child.expect("(Pdb)")
- child.sendline('i')
- child.expect("0")
- child.expect("(Pdb)")
- child.sendeof()
- rest = child.read().decode("utf8")
- assert "1 failed" in rest
- if child.isalive():
- child.wait()
-
- def test_pdb_interaction_capturing_twice(self, testdir):
- p1 = testdir.makepyfile("""
- import pytest
- def test_1():
- i = 0
- print ("hello17")
- pytest.set_trace()
- x = 3
- print ("hello18")
- pytest.set_trace()
- x = 4
- """)
- child = testdir.spawn_pytest(str(p1))
- child.expect("test_1")
- child.expect("x = 3")
- child.expect("(Pdb)")
- child.sendline('c')
- child.expect("x = 4")
- child.sendeof()
- rest = child.read().decode("utf8")
- assert "1 failed" in rest
- assert "def test_1" in rest
- assert "hello17" in rest # out is captured
- assert "hello18" in rest # out is captured
- if child.isalive():
- child.wait()
-
- def test_pdb_used_outside_test(self, testdir):
- p1 = testdir.makepyfile("""
- import pytest
- pytest.set_trace()
- x = 5
- """)
- child = testdir.spawn("%s %s" %(sys.executable, p1))
- child.expect("x = 5")
- child.sendeof()
- child.wait()
-
- def test_pdb_used_in_generate_tests(self, testdir):
- p1 = testdir.makepyfile("""
- import pytest
- def pytest_generate_tests(metafunc):
- pytest.set_trace()
- x = 5
- def test_foo(a):
- pass
- """)
- child = testdir.spawn_pytest(str(p1))
- child.expect("x = 5")
- child.sendeof()
- child.wait()
-
- def test_pdb_collection_failure_is_shown(self, testdir):
- p1 = testdir.makepyfile("""xxx """)
- result = testdir.runpytest_subprocess("--pdb", p1)
- result.stdout.fnmatch_lines([
- "*NameError*xxx*",
- "*1 error*",
- ])
-
- def test_enter_pdb_hook_is_called(self, testdir):
- testdir.makeconftest("""
- def pytest_enter_pdb(config):
- assert config.testing_verification == 'configured'
- print 'enter_pdb_hook'
-
- def pytest_configure(config):
- config.testing_verification = 'configured'
- """)
- p1 = testdir.makepyfile("""
- import pytest
-
- def test_foo():
- pytest.set_trace()
- """)
- child = testdir.spawn_pytest(str(p1))
- child.expect("enter_pdb_hook")
- child.send('c\n')
- child.sendeof()
- if child.isalive():
- child.wait()
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_pluginmanager.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_pluginmanager.py
deleted file mode 100644
index 36847638d48..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_pluginmanager.py
+++ /dev/null
@@ -1,340 +0,0 @@
-import pytest
-import py
-import os
-
-from _pytest.config import get_config, PytestPluginManager
-from _pytest.main import EXIT_NOTESTSCOLLECTED
-
-@pytest.fixture
-def pytestpm():
- return PytestPluginManager()
-
-class TestPytestPluginInteractions:
- def test_addhooks_conftestplugin(self, testdir):
- testdir.makepyfile(newhooks="""
- def pytest_myhook(xyz):
- "new hook"
- """)
- conf = testdir.makeconftest("""
- import sys ; sys.path.insert(0, '.')
- import newhooks
- def pytest_addhooks(pluginmanager):
- pluginmanager.addhooks(newhooks)
- def pytest_myhook(xyz):
- return xyz + 1
- """)
- config = get_config()
- pm = config.pluginmanager
- pm.hook.pytest_addhooks.call_historic(
- kwargs=dict(pluginmanager=config.pluginmanager))
- config.pluginmanager._importconftest(conf)
- #print(config.pluginmanager.get_plugins())
- res = config.hook.pytest_myhook(xyz=10)
- assert res == [11]
-
- def test_addhooks_nohooks(self, testdir):
- testdir.makeconftest("""
- import sys
- def pytest_addhooks(pluginmanager):
- pluginmanager.addhooks(sys)
- """)
- res = testdir.runpytest()
- assert res.ret != 0
- res.stderr.fnmatch_lines([
- "*did not find*sys*"
- ])
-
- def test_namespace_early_from_import(self, testdir):
- p = testdir.makepyfile("""
- from pytest import Item
- from pytest import Item as Item2
- assert Item is Item2
- """)
- result = testdir.runpython(p)
- assert result.ret == 0
-
- def test_do_ext_namespace(self, testdir):
- testdir.makeconftest("""
- def pytest_namespace():
- return {'hello': 'world'}
- """)
- p = testdir.makepyfile("""
- from pytest import hello
- import pytest
- def test_hello():
- assert hello == "world"
- assert 'hello' in pytest.__all__
- """)
- reprec = testdir.inline_run(p)
- reprec.assertoutcome(passed=1)
-
- def test_do_option_postinitialize(self, testdir):
- config = testdir.parseconfigure()
- assert not hasattr(config.option, 'test123')
- p = testdir.makepyfile("""
- def pytest_addoption(parser):
- parser.addoption('--test123', action="store_true",
- default=True)
- """)
- config.pluginmanager._importconftest(p)
- assert config.option.test123
-
- def test_configure(self, testdir):
- config = testdir.parseconfig()
- l = []
- class A:
- def pytest_configure(self, config):
- l.append(self)
-
- config.pluginmanager.register(A())
- assert len(l) == 0
- config._do_configure()
- assert len(l) == 1
- config.pluginmanager.register(A()) # leads to a configured() plugin
- assert len(l) == 2
- assert l[0] != l[1]
-
- config._ensure_unconfigure()
- config.pluginmanager.register(A())
- assert len(l) == 2
-
- def test_hook_tracing(self):
- pytestpm = get_config().pluginmanager # fully initialized with plugins
- saveindent = []
- class api1:
- def pytest_plugin_registered(self):
- saveindent.append(pytestpm.trace.root.indent)
- class api2:
- def pytest_plugin_registered(self):
- saveindent.append(pytestpm.trace.root.indent)
- raise ValueError()
- l = []
- pytestpm.trace.root.setwriter(l.append)
- undo = pytestpm.enable_tracing()
- try:
- indent = pytestpm.trace.root.indent
- p = api1()
- pytestpm.register(p)
- assert pytestpm.trace.root.indent == indent
- assert len(l) >= 2
- assert 'pytest_plugin_registered' in l[0]
- assert 'finish' in l[1]
-
- l[:] = []
- with pytest.raises(ValueError):
- pytestpm.register(api2())
- assert pytestpm.trace.root.indent == indent
- assert saveindent[0] > indent
- finally:
- undo()
-
- def test_warn_on_deprecated_multicall(self, pytestpm):
- warnings = []
-
- class get_warnings:
- def pytest_logwarning(self, message):
- warnings.append(message)
-
- class Plugin:
- def pytest_configure(self, __multicall__):
- pass
-
- pytestpm.register(get_warnings())
- before = list(warnings)
- pytestpm.register(Plugin())
- assert len(warnings) == len(before) + 1
- assert "deprecated" in warnings[-1]
-
- def test_warn_on_deprecated_addhooks(self, pytestpm):
- warnings = []
-
- class get_warnings:
- def pytest_logwarning(self, code, fslocation, message, nodeid):
- warnings.append(message)
-
- class Plugin:
- def pytest_testhook():
- pass
-
- pytestpm.register(get_warnings())
- before = list(warnings)
- pytestpm.addhooks(Plugin())
- assert len(warnings) == len(before) + 1
- assert "deprecated" in warnings[-1]
-
-
-def test_namespace_has_default_and_env_plugins(testdir):
- p = testdir.makepyfile("""
- import pytest
- pytest.mark
- """)
- result = testdir.runpython(p)
- assert result.ret == 0
-
-def test_default_markers(testdir):
- result = testdir.runpytest("--markers")
- result.stdout.fnmatch_lines([
- "*tryfirst*first*",
- "*trylast*last*",
- ])
-
-
-def test_importplugin_issue375(testdir, pytestpm):
- """Don't hide import errors when importing plugins and provide
- an easy to debug message.
- """
- testdir.syspathinsert(testdir.tmpdir)
- testdir.makepyfile(qwe="import aaaa")
- with pytest.raises(ImportError) as excinfo:
- pytestpm.import_plugin("qwe")
- expected = '.*Error importing plugin "qwe": No module named \'?aaaa\'?'
- assert py.std.re.match(expected, str(excinfo.value))
-
-
-class TestPytestPluginManager:
- def test_register_imported_modules(self):
- pm = PytestPluginManager()
- mod = py.std.types.ModuleType("x.y.pytest_hello")
- pm.register(mod)
- assert pm.is_registered(mod)
- l = pm.get_plugins()
- assert mod in l
- pytest.raises(ValueError, "pm.register(mod)")
- pytest.raises(ValueError, lambda: pm.register(mod))
- #assert not pm.is_registered(mod2)
- assert pm.get_plugins() == l
-
- def test_canonical_import(self, monkeypatch):
- mod = py.std.types.ModuleType("pytest_xyz")
- monkeypatch.setitem(py.std.sys.modules, 'pytest_xyz', mod)
- pm = PytestPluginManager()
- pm.import_plugin('pytest_xyz')
- assert pm.get_plugin('pytest_xyz') == mod
- assert pm.is_registered(mod)
-
- def test_consider_module(self, testdir, pytestpm):
- testdir.syspathinsert()
- testdir.makepyfile(pytest_p1="#")
- testdir.makepyfile(pytest_p2="#")
- mod = py.std.types.ModuleType("temp")
- mod.pytest_plugins = ["pytest_p1", "pytest_p2"]
- pytestpm.consider_module(mod)
- assert pytestpm.get_plugin("pytest_p1").__name__ == "pytest_p1"
- assert pytestpm.get_plugin("pytest_p2").__name__ == "pytest_p2"
-
- def test_consider_module_import_module(self, testdir):
- pytestpm = get_config().pluginmanager
- mod = py.std.types.ModuleType("x")
- mod.pytest_plugins = "pytest_a"
- aplugin = testdir.makepyfile(pytest_a="#")
- reprec = testdir.make_hook_recorder(pytestpm)
- #syspath.prepend(aplugin.dirpath())
- py.std.sys.path.insert(0, str(aplugin.dirpath()))
- pytestpm.consider_module(mod)
- call = reprec.getcall(pytestpm.hook.pytest_plugin_registered.name)
- assert call.plugin.__name__ == "pytest_a"
-
- # check that it is not registered twice
- pytestpm.consider_module(mod)
- l = reprec.getcalls("pytest_plugin_registered")
- assert len(l) == 1
-
- def test_consider_env_fails_to_import(self, monkeypatch, pytestpm):
- monkeypatch.setenv('PYTEST_PLUGINS', 'nonexisting', prepend=",")
- with pytest.raises(ImportError):
- pytestpm.consider_env()
-
- def test_plugin_skip(self, testdir, monkeypatch):
- p = testdir.makepyfile(skipping1="""
- import pytest
- pytest.skip("hello")
- """)
- p.copy(p.dirpath("skipping2.py"))
- monkeypatch.setenv("PYTEST_PLUGINS", "skipping2")
- result = testdir.runpytest("-rw", "-p", "skipping1", syspathinsert=True)
- assert result.ret == EXIT_NOTESTSCOLLECTED
- result.stdout.fnmatch_lines([
- "WI1*skipped plugin*skipping1*hello*",
- "WI1*skipped plugin*skipping2*hello*",
- ])
-
- def test_consider_env_plugin_instantiation(self, testdir, monkeypatch, pytestpm):
- testdir.syspathinsert()
- testdir.makepyfile(xy123="#")
- monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'xy123')
- l1 = len(pytestpm.get_plugins())
- pytestpm.consider_env()
- l2 = len(pytestpm.get_plugins())
- assert l2 == l1 + 1
- assert pytestpm.get_plugin('xy123')
- pytestpm.consider_env()
- l3 = len(pytestpm.get_plugins())
- assert l2 == l3
-
- def test_pluginmanager_ENV_startup(self, testdir, monkeypatch):
- testdir.makepyfile(pytest_x500="#")
- p = testdir.makepyfile("""
- import pytest
- def test_hello(pytestconfig):
- plugin = pytestconfig.pluginmanager.get_plugin('pytest_x500')
- assert plugin is not None
- """)
- monkeypatch.setenv('PYTEST_PLUGINS', 'pytest_x500', prepend=",")
- result = testdir.runpytest(p, syspathinsert=True)
- assert result.ret == 0
- result.stdout.fnmatch_lines(["*1 passed*"])
-
- def test_import_plugin_importname(self, testdir, pytestpm):
- pytest.raises(ImportError, 'pytestpm.import_plugin("qweqwex.y")')
- pytest.raises(ImportError, 'pytestpm.import_plugin("pytest_qweqwx.y")')
-
- testdir.syspathinsert()
- pluginname = "pytest_hello"
- testdir.makepyfile(**{pluginname: ""})
- pytestpm.import_plugin("pytest_hello")
- len1 = len(pytestpm.get_plugins())
- pytestpm.import_plugin("pytest_hello")
- len2 = len(pytestpm.get_plugins())
- assert len1 == len2
- plugin1 = pytestpm.get_plugin("pytest_hello")
- assert plugin1.__name__.endswith('pytest_hello')
- plugin2 = pytestpm.get_plugin("pytest_hello")
- assert plugin2 is plugin1
-
- def test_import_plugin_dotted_name(self, testdir, pytestpm):
- pytest.raises(ImportError, 'pytestpm.import_plugin("qweqwex.y")')
- pytest.raises(ImportError, 'pytestpm.import_plugin("pytest_qweqwex.y")')
-
- testdir.syspathinsert()
- testdir.mkpydir("pkg").join("plug.py").write("x=3")
- pluginname = "pkg.plug"
- pytestpm.import_plugin(pluginname)
- mod = pytestpm.get_plugin("pkg.plug")
- assert mod.x == 3
-
- def test_consider_conftest_deps(self, testdir, pytestpm):
- mod = testdir.makepyfile("pytest_plugins='xyz'").pyimport()
- with pytest.raises(ImportError):
- pytestpm.consider_conftest(mod)
-
-
-class TestPytestPluginManagerBootstrapming:
- def test_preparse_args(self, pytestpm):
- pytest.raises(ImportError, lambda:
- pytestpm.consider_preparse(["xyz", "-p", "hello123"]))
-
- def test_plugin_prevent_register(self, pytestpm):
- pytestpm.consider_preparse(["xyz", "-p", "no:abc"])
- l1 = pytestpm.get_plugins()
- pytestpm.register(42, name="abc")
- l2 = pytestpm.get_plugins()
- assert len(l2) == len(l1)
- assert 42 not in l2
-
- def test_plugin_prevent_register_unregistered_alredy_registered(self, pytestpm):
- pytestpm.register(42, name="abc")
- l1 = pytestpm.get_plugins()
- assert 42 in l1
- pytestpm.consider_preparse(["xyz", "-p", "no:abc"])
- l2 = pytestpm.get_plugins()
- assert 42 not in l2
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_pytester.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_pytester.py
deleted file mode 100644
index 65660afdfe3..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_pytester.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import pytest
-import os
-from _pytest.pytester import HookRecorder
-from _pytest.config import PytestPluginManager
-from _pytest.main import EXIT_OK, EXIT_TESTSFAILED
-
-
-def test_make_hook_recorder(testdir):
- item = testdir.getitem("def test_func(): pass")
- recorder = testdir.make_hook_recorder(item.config.pluginmanager)
- assert not recorder.getfailures()
-
- pytest.xfail("internal reportrecorder tests need refactoring")
- class rep:
- excinfo = None
- passed = False
- failed = True
- skipped = False
- when = "call"
-
- recorder.hook.pytest_runtest_logreport(report=rep)
- failures = recorder.getfailures()
- assert failures == [rep]
- failures = recorder.getfailures()
- assert failures == [rep]
-
- class rep:
- excinfo = None
- passed = False
- failed = False
- skipped = True
- when = "call"
- rep.passed = False
- rep.skipped = True
- recorder.hook.pytest_runtest_logreport(report=rep)
-
- modcol = testdir.getmodulecol("")
- rep = modcol.config.hook.pytest_make_collect_report(collector=modcol)
- rep.passed = False
- rep.failed = True
- rep.skipped = False
- recorder.hook.pytest_collectreport(report=rep)
-
- passed, skipped, failed = recorder.listoutcomes()
- assert not passed and skipped and failed
-
- numpassed, numskipped, numfailed = recorder.countoutcomes()
- assert numpassed == 0
- assert numskipped == 1
- assert numfailed == 1
- assert len(recorder.getfailedcollections()) == 1
-
- recorder.unregister()
- recorder.clear()
- recorder.hook.pytest_runtest_logreport(report=rep)
- pytest.raises(ValueError, "recorder.getfailures()")
-
-
-def test_parseconfig(testdir):
- config1 = testdir.parseconfig()
- config2 = testdir.parseconfig()
- assert config2 != config1
- assert config1 != pytest.config
-
-def test_testdir_runs_with_plugin(testdir):
- testdir.makepyfile("""
- pytest_plugins = "pytester"
- def test_hello(testdir):
- assert 1
- """)
- result = testdir.runpytest()
- result.assert_outcomes(passed=1)
-
-
-def make_holder():
- class apiclass:
- def pytest_xyz(self, arg):
- "x"
- def pytest_xyz_noarg(self):
- "x"
-
- apimod = type(os)('api')
- def pytest_xyz(arg):
- "x"
- def pytest_xyz_noarg():
- "x"
- apimod.pytest_xyz = pytest_xyz
- apimod.pytest_xyz_noarg = pytest_xyz_noarg
- return apiclass, apimod
-
-
-@pytest.mark.parametrize("holder", make_holder())
-def test_hookrecorder_basic(holder):
- pm = PytestPluginManager()
- pm.addhooks(holder)
- rec = HookRecorder(pm)
- pm.hook.pytest_xyz(arg=123)
- call = rec.popcall("pytest_xyz")
- assert call.arg == 123
- assert call._name == "pytest_xyz"
- pytest.raises(pytest.fail.Exception, "rec.popcall('abc')")
- pm.hook.pytest_xyz_noarg()
- call = rec.popcall("pytest_xyz_noarg")
- assert call._name == "pytest_xyz_noarg"
-
-
-def test_makepyfile_unicode(testdir):
- global unichr
- try:
- unichr(65)
- except NameError:
- unichr = chr
- testdir.makepyfile(unichr(0xfffd))
-
-def test_inline_run_clean_modules(testdir):
- test_mod = testdir.makepyfile("def test_foo(): assert True")
- result = testdir.inline_run(str(test_mod))
- assert result.ret == EXIT_OK
- # rewrite module, now test should fail if module was re-imported
- test_mod.write("def test_foo(): assert False")
- result2 = testdir.inline_run(str(test_mod))
- assert result2.ret == EXIT_TESTSFAILED
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_recwarn.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_recwarn.py
deleted file mode 100644
index 87e5846c2b0..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_recwarn.py
+++ /dev/null
@@ -1,227 +0,0 @@
-import warnings
-import py
-import pytest
-from _pytest.recwarn import WarningsRecorder
-
-
-def test_recwarn_functional(testdir):
- reprec = testdir.inline_runsource("""
- import warnings
- oldwarn = warnings.showwarning
- def test_method(recwarn):
- assert warnings.showwarning != oldwarn
- warnings.warn("hello")
- warn = recwarn.pop()
- assert isinstance(warn.message, UserWarning)
- def test_finalized():
- assert warnings.showwarning == oldwarn
- """)
- res = reprec.countoutcomes()
- assert tuple(res) == (2, 0, 0), res
-
-
-class TestWarningsRecorderChecker(object):
- def test_recording(self, recwarn):
- showwarning = py.std.warnings.showwarning
- rec = WarningsRecorder()
- with rec:
- assert py.std.warnings.showwarning != showwarning
- assert not rec.list
- py.std.warnings.warn_explicit("hello", UserWarning, "xyz", 13)
- assert len(rec.list) == 1
- py.std.warnings.warn(DeprecationWarning("hello"))
- assert len(rec.list) == 2
- warn = rec.pop()
- assert str(warn.message) == "hello"
- l = rec.list
- rec.clear()
- assert len(rec.list) == 0
- assert l is rec.list
- pytest.raises(AssertionError, "rec.pop()")
-
- assert showwarning == py.std.warnings.showwarning
-
- def test_typechecking(self):
- from _pytest.recwarn import WarningsChecker
- with pytest.raises(TypeError):
- WarningsChecker(5)
- with pytest.raises(TypeError):
- WarningsChecker(('hi', RuntimeWarning))
- with pytest.raises(TypeError):
- WarningsChecker([DeprecationWarning, RuntimeWarning])
-
- def test_invalid_enter_exit(self):
- # wrap this test in WarningsRecorder to ensure warning state gets reset
- with WarningsRecorder():
- with pytest.raises(RuntimeError):
- rec = WarningsRecorder()
- rec.__exit__(None, None, None) # can't exit before entering
-
- with pytest.raises(RuntimeError):
- rec = WarningsRecorder()
- with rec:
- with rec:
- pass # can't enter twice
-
-
-class TestDeprecatedCall(object):
- """test pytest.deprecated_call()"""
-
- def dep(self, i, j=None):
- if i == 0:
- py.std.warnings.warn("is deprecated", DeprecationWarning,
- stacklevel=1)
- return 42
-
- def dep_explicit(self, i):
- if i == 0:
- py.std.warnings.warn_explicit("dep_explicit", category=DeprecationWarning,
- filename="hello", lineno=3)
-
- def test_deprecated_call_raises(self):
- with pytest.raises(AssertionError) as excinfo:
- pytest.deprecated_call(self.dep, 3, 5)
- assert str(excinfo).find("did not produce") != -1
-
- def test_deprecated_call(self):
- pytest.deprecated_call(self.dep, 0, 5)
-
- def test_deprecated_call_ret(self):
- ret = pytest.deprecated_call(self.dep, 0)
- assert ret == 42
-
- def test_deprecated_call_preserves(self):
- onceregistry = py.std.warnings.onceregistry.copy()
- filters = py.std.warnings.filters[:]
- warn = py.std.warnings.warn
- warn_explicit = py.std.warnings.warn_explicit
- self.test_deprecated_call_raises()
- self.test_deprecated_call()
- assert onceregistry == py.std.warnings.onceregistry
- assert filters == py.std.warnings.filters
- assert warn is py.std.warnings.warn
- assert warn_explicit is py.std.warnings.warn_explicit
-
- def test_deprecated_explicit_call_raises(self):
- with pytest.raises(AssertionError):
- pytest.deprecated_call(self.dep_explicit, 3)
-
- def test_deprecated_explicit_call(self):
- pytest.deprecated_call(self.dep_explicit, 0)
- pytest.deprecated_call(self.dep_explicit, 0)
-
- def test_deprecated_call_as_context_manager_no_warning(self):
- with pytest.raises(pytest.fail.Exception) as ex:
- with pytest.deprecated_call():
- self.dep(1)
- assert str(ex.value) == "DID NOT WARN"
-
- def test_deprecated_call_as_context_manager(self):
- with pytest.deprecated_call():
- self.dep(0)
-
- def test_deprecated_call_pending(self):
- def f():
- py.std.warnings.warn(PendingDeprecationWarning("hi"))
- pytest.deprecated_call(f)
-
- def test_deprecated_call_specificity(self):
- other_warnings = [Warning, UserWarning, SyntaxWarning, RuntimeWarning,
- FutureWarning, ImportWarning, UnicodeWarning]
- for warning in other_warnings:
- def f():
- py.std.warnings.warn(warning("hi"))
- with pytest.raises(AssertionError):
- pytest.deprecated_call(f)
-
- def test_deprecated_function_already_called(self, testdir):
- """deprecated_call should be able to catch a call to a deprecated
- function even if that function has already been called in the same
- module. See #1190.
- """
- testdir.makepyfile("""
- import warnings
- import pytest
-
- def deprecated_function():
- warnings.warn("deprecated", DeprecationWarning)
-
- def test_one():
- deprecated_function()
-
- def test_two():
- pytest.deprecated_call(deprecated_function)
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines('*=== 2 passed in *===')
-
-
-class TestWarns(object):
- def test_strings(self):
- # different messages, b/c Python suppresses multiple identical warnings
- source1 = "warnings.warn('w1', RuntimeWarning)"
- source2 = "warnings.warn('w2', RuntimeWarning)"
- source3 = "warnings.warn('w3', RuntimeWarning)"
- pytest.warns(RuntimeWarning, source1)
- pytest.raises(pytest.fail.Exception,
- lambda: pytest.warns(UserWarning, source2))
- pytest.warns(RuntimeWarning, source3)
-
- def test_function(self):
- pytest.warns(SyntaxWarning,
- lambda msg: warnings.warn(msg, SyntaxWarning), "syntax")
-
- def test_warning_tuple(self):
- pytest.warns((RuntimeWarning, SyntaxWarning),
- lambda: warnings.warn('w1', RuntimeWarning))
- pytest.warns((RuntimeWarning, SyntaxWarning),
- lambda: warnings.warn('w2', SyntaxWarning))
- pytest.raises(pytest.fail.Exception,
- lambda: pytest.warns(
- (RuntimeWarning, SyntaxWarning),
- lambda: warnings.warn('w3', UserWarning)))
-
- def test_as_contextmanager(self):
- with pytest.warns(RuntimeWarning):
- warnings.warn("runtime", RuntimeWarning)
-
- with pytest.raises(pytest.fail.Exception):
- with pytest.warns(RuntimeWarning):
- warnings.warn("user", UserWarning)
-
- with pytest.raises(pytest.fail.Exception):
- with pytest.warns(UserWarning):
- warnings.warn("runtime", RuntimeWarning)
-
- with pytest.warns(UserWarning):
- warnings.warn("user", UserWarning)
-
- def test_record(self):
- with pytest.warns(UserWarning) as record:
- warnings.warn("user", UserWarning)
-
- assert len(record) == 1
- assert str(record[0].message) == "user"
-
- def test_record_only(self):
- with pytest.warns(None) as record:
- warnings.warn("user", UserWarning)
- warnings.warn("runtime", RuntimeWarning)
-
- assert len(record) == 2
- assert str(record[0].message) == "user"
- assert str(record[1].message) == "runtime"
-
- def test_double_test(self, testdir):
- """If a test is run again, the warning should still be raised"""
- testdir.makepyfile('''
- import pytest
- import warnings
-
- @pytest.mark.parametrize('run', [1, 2])
- def test(run):
- with pytest.warns(RuntimeWarning):
- warnings.warn("runtime", RuntimeWarning)
- ''')
- result = testdir.runpytest()
- result.stdout.fnmatch_lines(['*2 passed in*'])
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_resultlog.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_resultlog.py
deleted file mode 100644
index 74d13f64330..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_resultlog.py
+++ /dev/null
@@ -1,236 +0,0 @@
-import os
-
-import _pytest._code
-import py
-import pytest
-from _pytest.main import Node, Item, FSCollector
-from _pytest.resultlog import generic_path, ResultLog, \
- pytest_configure, pytest_unconfigure
-
-
-def test_generic_path(testdir):
- from _pytest.main import Session
- config = testdir.parseconfig()
- session = Session(config)
- p1 = Node('a', config=config, session=session)
- #assert p1.fspath is None
- p2 = Node('B', parent=p1)
- p3 = Node('()', parent = p2)
- item = Item('c', parent = p3)
-
- res = generic_path(item)
- assert res == 'a.B().c'
-
- p0 = FSCollector('proj/test', config=config, session=session)
- p1 = FSCollector('proj/test/a', parent=p0)
- p2 = Node('B', parent=p1)
- p3 = Node('()', parent = p2)
- p4 = Node('c', parent=p3)
- item = Item('[1]', parent = p4)
-
- res = generic_path(item)
- assert res == 'test/a:B().c[1]'
-
-def test_write_log_entry():
- reslog = ResultLog(None, None)
- reslog.logfile = py.io.TextIO()
- reslog.write_log_entry('name', '.', '')
- entry = reslog.logfile.getvalue()
- assert entry[-1] == '\n'
- entry_lines = entry.splitlines()
- assert len(entry_lines) == 1
- assert entry_lines[0] == '. name'
-
- reslog.logfile = py.io.TextIO()
- reslog.write_log_entry('name', 's', 'Skipped')
- entry = reslog.logfile.getvalue()
- assert entry[-1] == '\n'
- entry_lines = entry.splitlines()
- assert len(entry_lines) == 2
- assert entry_lines[0] == 's name'
- assert entry_lines[1] == ' Skipped'
-
- reslog.logfile = py.io.TextIO()
- reslog.write_log_entry('name', 's', 'Skipped\n')
- entry = reslog.logfile.getvalue()
- assert entry[-1] == '\n'
- entry_lines = entry.splitlines()
- assert len(entry_lines) == 2
- assert entry_lines[0] == 's name'
- assert entry_lines[1] == ' Skipped'
-
- reslog.logfile = py.io.TextIO()
- longrepr = ' tb1\n tb 2\nE tb3\nSome Error'
- reslog.write_log_entry('name', 'F', longrepr)
- entry = reslog.logfile.getvalue()
- assert entry[-1] == '\n'
- entry_lines = entry.splitlines()
- assert len(entry_lines) == 5
- assert entry_lines[0] == 'F name'
- assert entry_lines[1:] == [' '+line for line in longrepr.splitlines()]
-
-
-class TestWithFunctionIntegration:
- # XXX (hpk) i think that the resultlog plugin should
- # provide a Parser object so that one can remain
- # ignorant regarding formatting details.
- def getresultlog(self, testdir, arg):
- resultlog = testdir.tmpdir.join("resultlog")
- testdir.plugins.append("resultlog")
- args = ["--resultlog=%s" % resultlog] + [arg]
- testdir.runpytest(*args)
- return [x for x in resultlog.readlines(cr=0) if x]
-
- def test_collection_report(self, testdir):
- ok = testdir.makepyfile(test_collection_ok="")
- skip = testdir.makepyfile(test_collection_skip=
- "import pytest ; pytest.skip('hello')")
- fail = testdir.makepyfile(test_collection_fail="XXX")
- lines = self.getresultlog(testdir, ok)
- assert not lines
-
- lines = self.getresultlog(testdir, skip)
- assert len(lines) == 2
- assert lines[0].startswith("S ")
- assert lines[0].endswith("test_collection_skip.py")
- assert lines[1].startswith(" ")
- assert lines[1].endswith("test_collection_skip.py:1: Skipped: hello")
-
- lines = self.getresultlog(testdir, fail)
- assert lines
- assert lines[0].startswith("F ")
- assert lines[0].endswith("test_collection_fail.py"), lines[0]
- for x in lines[1:]:
- assert x.startswith(" ")
- assert "XXX" in "".join(lines[1:])
-
- def test_log_test_outcomes(self, testdir):
- mod = testdir.makepyfile(test_mod="""
- import pytest
- def test_pass(): pass
- def test_skip(): pytest.skip("hello")
- def test_fail(): raise ValueError("FAIL")
-
- @pytest.mark.xfail
- def test_xfail(): raise ValueError("XFAIL")
- @pytest.mark.xfail
- def test_xpass(): pass
-
- """)
- lines = self.getresultlog(testdir, mod)
- assert len(lines) >= 3
- assert lines[0].startswith(". ")
- assert lines[0].endswith("test_pass")
- assert lines[1].startswith("s "), lines[1]
- assert lines[1].endswith("test_skip")
- assert lines[2].find("hello") != -1
-
- assert lines[3].startswith("F ")
- assert lines[3].endswith("test_fail")
- tb = "".join(lines[4:8])
- assert tb.find('raise ValueError("FAIL")') != -1
-
- assert lines[8].startswith('x ')
- tb = "".join(lines[8:14])
- assert tb.find('raise ValueError("XFAIL")') != -1
-
- assert lines[14].startswith('X ')
- assert len(lines) == 15
-
- @pytest.mark.parametrize("style", ("native", "long", "short"))
- def test_internal_exception(self, style):
- # they are produced for example by a teardown failing
- # at the end of the run or a failing hook invocation
- try:
- raise ValueError
- except ValueError:
- excinfo = _pytest._code.ExceptionInfo()
- reslog = ResultLog(None, py.io.TextIO())
- reslog.pytest_internalerror(excinfo.getrepr(style=style))
- entry = reslog.logfile.getvalue()
- entry_lines = entry.splitlines()
-
- assert entry_lines[0].startswith('! ')
- if style != "native":
- assert os.path.basename(__file__)[:-9] in entry_lines[0] #.pyc/class
- assert entry_lines[-1][0] == ' '
- assert 'ValueError' in entry
-
-
-def test_generic(testdir, LineMatcher):
- testdir.plugins.append("resultlog")
- testdir.makepyfile("""
- import pytest
- def test_pass():
- pass
- def test_fail():
- assert 0
- def test_skip():
- pytest.skip("")
- @pytest.mark.xfail
- def test_xfail():
- assert 0
- @pytest.mark.xfail(run=False)
- def test_xfail_norun():
- assert 0
- """)
- testdir.runpytest("--resultlog=result.log")
- lines = testdir.tmpdir.join("result.log").readlines(cr=0)
- LineMatcher(lines).fnmatch_lines([
- ". *:test_pass",
- "F *:test_fail",
- "s *:test_skip",
- "x *:test_xfail",
- "x *:test_xfail_norun",
- ])
-
-def test_makedir_for_resultlog(testdir, LineMatcher):
- """--resultlog should automatically create directories for the log file"""
- testdir.plugins.append("resultlog")
- testdir.makepyfile("""
- import pytest
- def test_pass():
- pass
- """)
- testdir.runpytest("--resultlog=path/to/result.log")
- lines = testdir.tmpdir.join("path/to/result.log").readlines(cr=0)
- LineMatcher(lines).fnmatch_lines([
- ". *:test_pass",
- ])
-
-
-def test_no_resultlog_on_slaves(testdir):
- config = testdir.parseconfig("-p", "resultlog", "--resultlog=resultlog")
-
- assert not hasattr(config, '_resultlog')
- pytest_configure(config)
- assert hasattr(config, '_resultlog')
- pytest_unconfigure(config)
- assert not hasattr(config, '_resultlog')
-
- config.slaveinput = {}
- pytest_configure(config)
- assert not hasattr(config, '_resultlog')
- pytest_unconfigure(config)
- assert not hasattr(config, '_resultlog')
-
-
-def test_failure_issue380(testdir):
- testdir.makeconftest("""
- import pytest
- class MyCollector(pytest.File):
- def collect(self):
- raise ValueError()
- def repr_failure(self, excinfo):
- return "somestring"
- def pytest_collect_file(path, parent):
- return MyCollector(parent=parent, fspath=path)
- """)
- testdir.makepyfile("""
- def test_func():
- pass
- """)
- result = testdir.runpytest("--resultlog=log")
- assert result.ret == 1
-
-
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_runner.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_runner.py
deleted file mode 100644
index 4421c5d0d29..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_runner.py
+++ /dev/null
@@ -1,634 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import with_statement
-
-import _pytest._code
-import os
-import py
-import pytest
-import sys
-from _pytest import runner, main
-
-class TestSetupState:
- def test_setup(self, testdir):
- ss = runner.SetupState()
- item = testdir.getitem("def test_func(): pass")
- l = [1]
- ss.prepare(item)
- ss.addfinalizer(l.pop, colitem=item)
- assert l
- ss._pop_and_teardown()
- assert not l
-
- def test_teardown_exact_stack_empty(self, testdir):
- item = testdir.getitem("def test_func(): pass")
- ss = runner.SetupState()
- ss.teardown_exact(item, None)
- ss.teardown_exact(item, None)
- ss.teardown_exact(item, None)
-
- def test_setup_fails_and_failure_is_cached(self, testdir):
- item = testdir.getitem("""
- def setup_module(mod):
- raise ValueError(42)
- def test_func(): pass
- """) # noqa
- ss = runner.SetupState()
- pytest.raises(ValueError, lambda: ss.prepare(item))
- pytest.raises(ValueError, lambda: ss.prepare(item))
-
- def test_teardown_multiple_one_fails(self, testdir):
- r = []
- def fin1(): r.append('fin1')
- def fin2(): raise Exception('oops')
- def fin3(): r.append('fin3')
- item = testdir.getitem("def test_func(): pass")
- ss = runner.SetupState()
- ss.addfinalizer(fin1, item)
- ss.addfinalizer(fin2, item)
- ss.addfinalizer(fin3, item)
- with pytest.raises(Exception) as err:
- ss._callfinalizers(item)
- assert err.value.args == ('oops',)
- assert r == ['fin3', 'fin1']
-
- def test_teardown_multiple_fail(self, testdir):
- # Ensure the first exception is the one which is re-raised.
- # Ideally both would be reported however.
- def fin1(): raise Exception('oops1')
- def fin2(): raise Exception('oops2')
- item = testdir.getitem("def test_func(): pass")
- ss = runner.SetupState()
- ss.addfinalizer(fin1, item)
- ss.addfinalizer(fin2, item)
- with pytest.raises(Exception) as err:
- ss._callfinalizers(item)
- assert err.value.args == ('oops2',)
-
-
-class BaseFunctionalTests:
- def test_passfunction(self, testdir):
- reports = testdir.runitem("""
- def test_func():
- pass
- """)
- rep = reports[1]
- assert rep.passed
- assert not rep.failed
- assert rep.outcome == "passed"
- assert not rep.longrepr
-
- def test_failfunction(self, testdir):
- reports = testdir.runitem("""
- def test_func():
- assert 0
- """)
- rep = reports[1]
- assert not rep.passed
- assert not rep.skipped
- assert rep.failed
- assert rep.when == "call"
- assert rep.outcome == "failed"
- #assert isinstance(rep.longrepr, ReprExceptionInfo)
-
- def test_skipfunction(self, testdir):
- reports = testdir.runitem("""
- import pytest
- def test_func():
- pytest.skip("hello")
- """)
- rep = reports[1]
- assert not rep.failed
- assert not rep.passed
- assert rep.skipped
- assert rep.outcome == "skipped"
- #assert rep.skipped.when == "call"
- #assert rep.skipped.when == "call"
- #assert rep.skipped == "%sreason == "hello"
- #assert rep.skipped.location.lineno == 3
- #assert rep.skipped.location.path
- #assert not rep.skipped.failurerepr
-
- def test_skip_in_setup_function(self, testdir):
- reports = testdir.runitem("""
- import pytest
- def setup_function(func):
- pytest.skip("hello")
- def test_func():
- pass
- """)
- print(reports)
- rep = reports[0]
- assert not rep.failed
- assert not rep.passed
- assert rep.skipped
- #assert rep.skipped.reason == "hello"
- #assert rep.skipped.location.lineno == 3
- #assert rep.skipped.location.lineno == 3
- assert len(reports) == 2
- assert reports[1].passed # teardown
-
- def test_failure_in_setup_function(self, testdir):
- reports = testdir.runitem("""
- import pytest
- def setup_function(func):
- raise ValueError(42)
- def test_func():
- pass
- """)
- rep = reports[0]
- assert not rep.skipped
- assert not rep.passed
- assert rep.failed
- assert rep.when == "setup"
- assert len(reports) == 2
-
- def test_failure_in_teardown_function(self, testdir):
- reports = testdir.runitem("""
- import pytest
- def teardown_function(func):
- raise ValueError(42)
- def test_func():
- pass
- """)
- print(reports)
- assert len(reports) == 3
- rep = reports[2]
- assert not rep.skipped
- assert not rep.passed
- assert rep.failed
- assert rep.when == "teardown"
- #assert rep.longrepr.reprcrash.lineno == 3
- #assert rep.longrepr.reprtraceback.reprentries
-
- def test_custom_failure_repr(self, testdir):
- testdir.makepyfile(conftest="""
- import pytest
- class Function(pytest.Function):
- def repr_failure(self, excinfo):
- return "hello"
- """)
- reports = testdir.runitem("""
- import pytest
- def test_func():
- assert 0
- """)
- rep = reports[1]
- assert not rep.skipped
- assert not rep.passed
- assert rep.failed
- #assert rep.outcome.when == "call"
- #assert rep.failed.where.lineno == 3
- #assert rep.failed.where.path.basename == "test_func.py"
- #assert rep.failed.failurerepr == "hello"
-
- def test_teardown_final_returncode(self, testdir):
- rec = testdir.inline_runsource("""
- def test_func():
- pass
- def teardown_function(func):
- raise ValueError(42)
- """)
- assert rec.ret == 1
-
- def test_exact_teardown_issue90(self, testdir):
- rec = testdir.inline_runsource("""
- import pytest
-
- class TestClass:
- def test_method(self):
- pass
- def teardown_class(cls):
- raise Exception()
-
- def test_func():
- import sys
- # on python2 exc_info is keept till a function exits
- # so we would end up calling test functions while
- # sys.exc_info would return the indexerror
- # from guessing the lastitem
- excinfo = sys.exc_info()
- import traceback
- assert excinfo[0] is None, \
- traceback.format_exception(*excinfo)
- def teardown_function(func):
- raise ValueError(42)
- """)
- reps = rec.getreports("pytest_runtest_logreport")
- print (reps)
- for i in range(2):
- assert reps[i].nodeid.endswith("test_method")
- assert reps[i].passed
- assert reps[2].when == "teardown"
- assert reps[2].failed
- assert len(reps) == 6
- for i in range(3,5):
- assert reps[i].nodeid.endswith("test_func")
- assert reps[i].passed
- assert reps[5].when == "teardown"
- assert reps[5].nodeid.endswith("test_func")
- assert reps[5].failed
-
- def test_failure_in_setup_function_ignores_custom_repr(self, testdir):
- testdir.makepyfile(conftest="""
- import pytest
- class Function(pytest.Function):
- def repr_failure(self, excinfo):
- assert 0
- """)
- reports = testdir.runitem("""
- def setup_function(func):
- raise ValueError(42)
- def test_func():
- pass
- """)
- assert len(reports) == 2
- rep = reports[0]
- print(rep)
- assert not rep.skipped
- assert not rep.passed
- assert rep.failed
- #assert rep.outcome.when == "setup"
- #assert rep.outcome.where.lineno == 3
- #assert rep.outcome.where.path.basename == "test_func.py"
- #assert instanace(rep.failed.failurerepr, PythonFailureRepr)
-
- def test_systemexit_does_not_bail_out(self, testdir):
- try:
- reports = testdir.runitem("""
- def test_func():
- raise SystemExit(42)
- """)
- except SystemExit:
- pytest.fail("runner did not catch SystemExit")
- rep = reports[1]
- assert rep.failed
- assert rep.when == "call"
-
- def test_exit_propagates(self, testdir):
- try:
- testdir.runitem("""
- import pytest
- def test_func():
- raise pytest.exit.Exception()
- """)
- except pytest.exit.Exception:
- pass
- else:
- pytest.fail("did not raise")
-
-class TestExecutionNonForked(BaseFunctionalTests):
- def getrunner(self):
- def f(item):
- return runner.runtestprotocol(item, log=False)
- return f
-
- def test_keyboardinterrupt_propagates(self, testdir):
- try:
- testdir.runitem("""
- def test_func():
- raise KeyboardInterrupt("fake")
- """)
- except KeyboardInterrupt:
- pass
- else:
- pytest.fail("did not raise")
-
-class TestExecutionForked(BaseFunctionalTests):
- pytestmark = pytest.mark.skipif("not hasattr(os, 'fork')")
-
- def getrunner(self):
- # XXX re-arrange this test to live in pytest-xdist
- boxed = pytest.importorskip("xdist.boxed")
- return boxed.forked_run_report
-
- def test_suicide(self, testdir):
- reports = testdir.runitem("""
- def test_func():
- import os
- os.kill(os.getpid(), 15)
- """)
- rep = reports[0]
- assert rep.failed
- assert rep.when == "???"
-
-class TestSessionReports:
- def test_collect_result(self, testdir):
- col = testdir.getmodulecol("""
- def test_func1():
- pass
- class TestClass:
- pass
- """)
- rep = runner.collect_one_node(col)
- assert not rep.failed
- assert not rep.skipped
- assert rep.passed
- locinfo = rep.location
- assert locinfo[0] == col.fspath.basename
- assert not locinfo[1]
- assert locinfo[2] == col.fspath.basename
- res = rep.result
- assert len(res) == 2
- assert res[0].name == "test_func1"
- assert res[1].name == "TestClass"
-
- def test_skip_at_module_scope(self, testdir):
- col = testdir.getmodulecol("""
- import pytest
- pytest.skip("hello")
- def test_func():
- pass
- """)
- rep = main.collect_one_node(col)
- assert not rep.failed
- assert not rep.passed
- assert rep.skipped
-
-
-reporttypes = [
- runner.BaseReport,
- runner.TestReport,
- runner.TeardownErrorReport,
- runner.CollectReport,
-]
-
-@pytest.mark.parametrize('reporttype', reporttypes, ids=[x.__name__ for x in reporttypes])
-def test_report_extra_parameters(reporttype):
- if hasattr(py.std.inspect, 'signature'):
- args = list(py.std.inspect.signature(reporttype.__init__).parameters.keys())[1:]
- else:
- args = py.std.inspect.getargspec(reporttype.__init__)[0][1:]
- basekw = dict.fromkeys(args, [])
- report = reporttype(newthing=1, **basekw)
- assert report.newthing == 1
-
-def test_callinfo():
- ci = runner.CallInfo(lambda: 0, '123')
- assert ci.when == "123"
- assert ci.result == 0
- assert "result" in repr(ci)
- ci = runner.CallInfo(lambda: 0/0, '123')
- assert ci.when == "123"
- assert not hasattr(ci, 'result')
- assert ci.excinfo
- assert "exc" in repr(ci)
-
-# design question: do we want general hooks in python files?
-# then something like the following functional tests makes sense
-@pytest.mark.xfail
-def test_runtest_in_module_ordering(testdir):
- p1 = testdir.makepyfile("""
- def pytest_runtest_setup(item): # runs after class-level!
- item.function.mylist.append("module")
- class TestClass:
- def pytest_runtest_setup(self, item):
- assert not hasattr(item.function, 'mylist')
- item.function.mylist = ['class']
- def pytest_funcarg__mylist(self, request):
- return request.function.mylist
- def pytest_runtest_call(self, item, __multicall__):
- try:
- __multicall__.execute()
- except ValueError:
- pass
- def test_hello1(self, mylist):
- assert mylist == ['class', 'module'], mylist
- raise ValueError()
- def test_hello2(self, mylist):
- assert mylist == ['class', 'module'], mylist
- def pytest_runtest_teardown(item):
- del item.function.mylist
- """)
- result = testdir.runpytest(p1)
- result.stdout.fnmatch_lines([
- "*2 passed*"
- ])
-
-
-def test_outcomeexception_exceptionattributes():
- outcome = runner.OutcomeException('test')
- assert outcome.args[0] == outcome.msg
-
-def test_pytest_exit():
- try:
- pytest.exit("hello")
- except pytest.exit.Exception:
- excinfo = _pytest._code.ExceptionInfo()
- assert excinfo.errisinstance(KeyboardInterrupt)
-
-def test_pytest_fail():
- try:
- pytest.fail("hello")
- except pytest.fail.Exception:
- excinfo = _pytest._code.ExceptionInfo()
- s = excinfo.exconly(tryshort=True)
- assert s.startswith("Failed")
-
-def test_pytest_fail_notrace(testdir):
- testdir.makepyfile("""
- import pytest
- def test_hello():
- pytest.fail("hello", pytrace=False)
- def teardown_function(function):
- pytest.fail("world", pytrace=False)
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "world",
- "hello",
- ])
- assert 'def teardown_function' not in result.stdout.str()
-
-
-@pytest.mark.parametrize('str_prefix', ['u', ''])
-def test_pytest_fail_notrace_non_ascii(testdir, str_prefix):
- """Fix pytest.fail with pytrace=False with non-ascii characters (#1178).
-
- This tests with native and unicode strings containing non-ascii chars.
- """
- testdir.makepyfile(u"""
- # coding: utf-8
- import pytest
-
- def test_hello():
- pytest.fail(%s'oh oh: ☺', pytrace=False)
- """ % str_prefix)
- result = testdir.runpytest()
- if sys.version_info[0] >= 3:
- result.stdout.fnmatch_lines(['*test_hello*', "oh oh: ☺"])
- else:
- result.stdout.fnmatch_lines(['*test_hello*', "oh oh: *"])
- assert 'def test_hello' not in result.stdout.str()
-
-
-def test_pytest_no_tests_collected_exit_status(testdir):
- result = testdir.runpytest()
- result.stdout.fnmatch_lines('*collected 0 items*')
- assert result.ret == main.EXIT_NOTESTSCOLLECTED
-
- testdir.makepyfile(test_foo="""
- def test_foo():
- assert 1
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines('*collected 1 items*')
- result.stdout.fnmatch_lines('*1 passed*')
- assert result.ret == main.EXIT_OK
-
- result = testdir.runpytest('-k nonmatch')
- result.stdout.fnmatch_lines('*collected 1 items*')
- result.stdout.fnmatch_lines('*1 deselected*')
- assert result.ret == main.EXIT_NOTESTSCOLLECTED
-
-
-def test_exception_printing_skip():
- try:
- pytest.skip("hello")
- except pytest.skip.Exception:
- excinfo = _pytest._code.ExceptionInfo()
- s = excinfo.exconly(tryshort=True)
- assert s.startswith("Skipped")
-
-def test_importorskip(monkeypatch):
- importorskip = pytest.importorskip
- def f():
- importorskip("asdlkj")
- try:
- sys = importorskip("sys") # noqa
- assert sys == py.std.sys
- #path = pytest.importorskip("os.path")
- #assert path == py.std.os.path
- excinfo = pytest.raises(pytest.skip.Exception, f)
- path = py.path.local(excinfo.getrepr().reprcrash.path)
- # check that importorskip reports the actual call
- # in this test the test_runner.py file
- assert path.purebasename == "test_runner"
- pytest.raises(SyntaxError, "pytest.importorskip('x y z')")
- pytest.raises(SyntaxError, "pytest.importorskip('x=y')")
- mod = py.std.types.ModuleType("hello123")
- mod.__version__ = "1.3"
- monkeypatch.setitem(sys.modules, "hello123", mod)
- pytest.raises(pytest.skip.Exception, """
- pytest.importorskip("hello123", minversion="1.3.1")
- """)
- mod2 = pytest.importorskip("hello123", minversion="1.3")
- assert mod2 == mod
- except pytest.skip.Exception:
- print(_pytest._code.ExceptionInfo())
- pytest.fail("spurious skip")
-
-def test_importorskip_imports_last_module_part():
- ospath = pytest.importorskip("os.path")
- assert os.path == ospath
-
-def test_importorskip_dev_module(monkeypatch):
- try:
- mod = py.std.types.ModuleType("mockmodule")
- mod.__version__ = '0.13.0.dev-43290'
- monkeypatch.setitem(sys.modules, 'mockmodule', mod)
- mod2 = pytest.importorskip('mockmodule', minversion='0.12.0')
- assert mod2 == mod
- pytest.raises(pytest.skip.Exception, """
- pytest.importorskip('mockmodule1', minversion='0.14.0')""")
- except pytest.skip.Exception:
- print(_pytest._code.ExceptionInfo())
- pytest.fail("spurious skip")
-
-
-def test_pytest_cmdline_main(testdir):
- p = testdir.makepyfile("""
- import pytest
- def test_hello():
- assert 1
- if __name__ == '__main__':
- pytest.cmdline.main([__file__])
- """)
- import subprocess
- popen = subprocess.Popen([sys.executable, str(p)], stdout=subprocess.PIPE)
- popen.communicate()
- ret = popen.wait()
- assert ret == 0
-
-
-def test_unicode_in_longrepr(testdir):
- testdir.makeconftest("""
- import py
- def pytest_runtest_makereport(__multicall__):
- rep = __multicall__.execute()
- if rep.when == "call":
- rep.longrepr = py.builtin._totext("\\xc3\\xa4", "utf8")
- return rep
- """)
- testdir.makepyfile("""
- def test_out():
- assert 0
- """)
- result = testdir.runpytest()
- assert result.ret == 1
- assert "UnicodeEncodeError" not in result.stderr.str()
-
-
-def test_failure_in_setup(testdir):
- testdir.makepyfile("""
- def setup_module():
- 0/0
- def test_func():
- pass
- """)
- result = testdir.runpytest("--tb=line")
- assert "def setup_module" not in result.stdout.str()
-
-
-def test_makereport_getsource(testdir):
- testdir.makepyfile("""
- def test_foo():
- if False: pass
- else: assert False
- """)
- result = testdir.runpytest()
- assert 'INTERNALERROR' not in result.stdout.str()
- result.stdout.fnmatch_lines(['*else: assert False*'])
-
-
-def test_makereport_getsource_dynamic_code(testdir, monkeypatch):
- """Test that exception in dynamically generated code doesn't break getting the source line."""
- import inspect
- original_findsource = inspect.findsource
- def findsource(obj, *args, **kwargs):
- # Can be triggered by dynamically created functions
- if obj.__name__ == 'foo':
- raise IndexError()
- return original_findsource(obj, *args, **kwargs)
- monkeypatch.setattr(inspect, 'findsource', findsource)
-
- testdir.makepyfile("""
- import pytest
-
- @pytest.fixture
- def foo(missing):
- pass
-
- def test_fix(foo):
- assert False
- """)
- result = testdir.runpytest('-vv')
- assert 'INTERNALERROR' not in result.stdout.str()
- result.stdout.fnmatch_lines(["*test_fix*", "*fixture*'missing'*not found*"])
-
-
-def test_store_except_info_on_eror():
- """ Test that upon test failure, the exception info is stored on
- sys.last_traceback and friends.
- """
- # Simulate item that raises a specific exception
- class ItemThatRaises:
- def runtest(self):
- raise IndexError('TEST')
- try:
- runner.pytest_runtest_call(ItemThatRaises())
- except IndexError:
- pass
- # Check that exception info is stored on sys
- assert sys.last_type is IndexError
- assert sys.last_value.args[0] == 'TEST'
- assert sys.last_traceback
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_runner_xunit.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_runner_xunit.py
deleted file mode 100644
index f32a1311ba8..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_runner_xunit.py
+++ /dev/null
@@ -1,252 +0,0 @@
-#
-# test correct setup/teardowns at
-# module, class, and instance level
-
-def test_module_and_function_setup(testdir):
- reprec = testdir.inline_runsource("""
- modlevel = []
- def setup_module(module):
- assert not modlevel
- module.modlevel.append(42)
-
- def teardown_module(module):
- modlevel.pop()
-
- def setup_function(function):
- function.answer = 17
-
- def teardown_function(function):
- del function.answer
-
- def test_modlevel():
- assert modlevel[0] == 42
- assert test_modlevel.answer == 17
-
- class TestFromClass:
- def test_module(self):
- assert modlevel[0] == 42
- assert not hasattr(test_modlevel, 'answer')
- """)
- rep = reprec.matchreport("test_modlevel")
- assert rep.passed
- rep = reprec.matchreport("test_module")
- assert rep.passed
-
-def test_module_setup_failure_no_teardown(testdir):
- reprec = testdir.inline_runsource("""
- l = []
- def setup_module(module):
- l.append(1)
- 0/0
-
- def test_nothing():
- pass
-
- def teardown_module(module):
- l.append(2)
- """)
- reprec.assertoutcome(failed=1)
- calls = reprec.getcalls("pytest_runtest_setup")
- assert calls[0].item.module.l == [1]
-
-def test_setup_function_failure_no_teardown(testdir):
- reprec = testdir.inline_runsource("""
- modlevel = []
- def setup_function(function):
- modlevel.append(1)
- 0/0
-
- def teardown_function(module):
- modlevel.append(2)
-
- def test_func():
- pass
- """)
- calls = reprec.getcalls("pytest_runtest_setup")
- assert calls[0].item.module.modlevel == [1]
-
-def test_class_setup(testdir):
- reprec = testdir.inline_runsource("""
- class TestSimpleClassSetup:
- clslevel = []
- def setup_class(cls):
- cls.clslevel.append(23)
-
- def teardown_class(cls):
- cls.clslevel.pop()
-
- def test_classlevel(self):
- assert self.clslevel[0] == 23
-
- class TestInheritedClassSetupStillWorks(TestSimpleClassSetup):
- def test_classlevel_anothertime(self):
- assert self.clslevel == [23]
-
- def test_cleanup():
- assert not TestSimpleClassSetup.clslevel
- assert not TestInheritedClassSetupStillWorks.clslevel
- """)
- reprec.assertoutcome(passed=1+2+1)
-
-def test_class_setup_failure_no_teardown(testdir):
- reprec = testdir.inline_runsource("""
- class TestSimpleClassSetup:
- clslevel = []
- def setup_class(cls):
- 0/0
-
- def teardown_class(cls):
- cls.clslevel.append(1)
-
- def test_classlevel(self):
- pass
-
- def test_cleanup():
- assert not TestSimpleClassSetup.clslevel
- """)
- reprec.assertoutcome(failed=1, passed=1)
-
-def test_method_setup(testdir):
- reprec = testdir.inline_runsource("""
- class TestSetupMethod:
- def setup_method(self, meth):
- self.methsetup = meth
- def teardown_method(self, meth):
- del self.methsetup
-
- def test_some(self):
- assert self.methsetup == self.test_some
-
- def test_other(self):
- assert self.methsetup == self.test_other
- """)
- reprec.assertoutcome(passed=2)
-
-def test_method_setup_failure_no_teardown(testdir):
- reprec = testdir.inline_runsource("""
- class TestMethodSetup:
- clslevel = []
- def setup_method(self, method):
- self.clslevel.append(1)
- 0/0
-
- def teardown_method(self, method):
- self.clslevel.append(2)
-
- def test_method(self):
- pass
-
- def test_cleanup():
- assert TestMethodSetup.clslevel == [1]
- """)
- reprec.assertoutcome(failed=1, passed=1)
-
-def test_method_generator_setup(testdir):
- reprec = testdir.inline_runsource("""
- class TestSetupTeardownOnInstance:
- def setup_class(cls):
- cls.classsetup = True
-
- def setup_method(self, method):
- self.methsetup = method
-
- def test_generate(self):
- assert self.classsetup
- assert self.methsetup == self.test_generate
- yield self.generated, 5
- yield self.generated, 2
-
- def generated(self, value):
- assert self.classsetup
- assert self.methsetup == self.test_generate
- assert value == 5
- """)
- reprec.assertoutcome(passed=1, failed=1)
-
-def test_func_generator_setup(testdir):
- reprec = testdir.inline_runsource("""
- import sys
-
- def setup_module(mod):
- print ("setup_module")
- mod.x = []
-
- def setup_function(fun):
- print ("setup_function")
- x.append(1)
-
- def teardown_function(fun):
- print ("teardown_function")
- x.pop()
-
- def test_one():
- assert x == [1]
- def check():
- print ("check")
- sys.stderr.write("e\\n")
- assert x == [1]
- yield check
- assert x == [1]
- """)
- rep = reprec.matchreport("test_one", names="pytest_runtest_logreport")
- assert rep.passed
-
-def test_method_setup_uses_fresh_instances(testdir):
- reprec = testdir.inline_runsource("""
- class TestSelfState1:
- memory = []
- def test_hello(self):
- self.memory.append(self)
-
- def test_afterhello(self):
- assert self != self.memory[0]
- """)
- reprec.assertoutcome(passed=2, failed=0)
-
-def test_setup_that_skips_calledagain(testdir):
- p = testdir.makepyfile("""
- import pytest
- def setup_module(mod):
- pytest.skip("x")
- def test_function1():
- pass
- def test_function2():
- pass
- """)
- reprec = testdir.inline_run(p)
- reprec.assertoutcome(skipped=2)
-
-def test_setup_fails_again_on_all_tests(testdir):
- p = testdir.makepyfile("""
- import pytest
- def setup_module(mod):
- raise ValueError(42)
- def test_function1():
- pass
- def test_function2():
- pass
- """)
- reprec = testdir.inline_run(p)
- reprec.assertoutcome(failed=2)
-
-def test_setup_funcarg_setup_when_outer_scope_fails(testdir):
- p = testdir.makepyfile("""
- import pytest
- def setup_module(mod):
- raise ValueError(42)
- def pytest_funcarg__hello(request):
- raise ValueError("xyz43")
- def test_function1(hello):
- pass
- def test_function2(hello):
- pass
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*function1*",
- "*ValueError*42*",
- "*function2*",
- "*ValueError*42*",
- "*2 error*"
- ])
- assert "xyz43" not in result.stdout.str()
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_session.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_session.py
deleted file mode 100644
index 76f804b4f99..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_session.py
+++ /dev/null
@@ -1,244 +0,0 @@
-import pytest
-
-from _pytest.main import EXIT_NOTESTSCOLLECTED
-
-class SessionTests:
- def test_basic_testitem_events(self, testdir):
- tfile = testdir.makepyfile("""
- def test_one():
- pass
- def test_one_one():
- assert 0
- def test_other():
- raise ValueError(23)
- class TestClass:
- def test_two(self, someargs):
- pass
- """)
- reprec = testdir.inline_run(tfile)
- passed, skipped, failed = reprec.listoutcomes()
- assert len(skipped) == 0
- assert len(passed) == 1
- assert len(failed) == 3
- end = lambda x: x.nodeid.split("::")[-1]
- assert end(failed[0]) == "test_one_one"
- assert end(failed[1]) == "test_other"
- itemstarted = reprec.getcalls("pytest_itemcollected")
- assert len(itemstarted) == 4
- # XXX check for failing funcarg setup
- #colreports = reprec.getcalls("pytest_collectreport")
- #assert len(colreports) == 4
- #assert colreports[1].report.failed
-
- def test_nested_import_error(self, testdir):
- tfile = testdir.makepyfile("""
- import import_fails
- def test_this():
- assert import_fails.a == 1
- """, import_fails="""
- import does_not_work
- a = 1
- """)
- reprec = testdir.inline_run(tfile)
- l = reprec.getfailedcollections()
- assert len(l) == 1
- out = l[0].longrepr.reprcrash.message
- assert out.find('does_not_work') != -1
-
- def test_raises_output(self, testdir):
- reprec = testdir.inline_runsource("""
- import pytest
- def test_raises_doesnt():
- pytest.raises(ValueError, int, "3")
- """)
- passed, skipped, failed = reprec.listoutcomes()
- assert len(failed) == 1
- out = failed[0].longrepr.reprcrash.message
- if not out.find("DID NOT RAISE") != -1:
- print(out)
- pytest.fail("incorrect raises() output")
-
- def test_generator_yields_None(self, testdir):
- reprec = testdir.inline_runsource("""
- def test_1():
- yield None
- """)
- failures = reprec.getfailedcollections()
- out = failures[0].longrepr.reprcrash.message
- i = out.find('TypeError')
- assert i != -1
-
- def test_syntax_error_module(self, testdir):
- reprec = testdir.inline_runsource("this is really not python")
- l = reprec.getfailedcollections()
- assert len(l) == 1
- out = str(l[0].longrepr)
- assert out.find(str('not python')) != -1
-
- def test_exit_first_problem(self, testdir):
- reprec = testdir.inline_runsource("""
- def test_one(): assert 0
- def test_two(): assert 0
- """, '--exitfirst')
- passed, skipped, failed = reprec.countoutcomes()
- assert failed == 1
- assert passed == skipped == 0
-
- def test_maxfail(self, testdir):
- reprec = testdir.inline_runsource("""
- def test_one(): assert 0
- def test_two(): assert 0
- def test_three(): assert 0
- """, '--maxfail=2')
- passed, skipped, failed = reprec.countoutcomes()
- assert failed == 2
- assert passed == skipped == 0
-
- def test_broken_repr(self, testdir):
- p = testdir.makepyfile("""
- import pytest
- class BrokenRepr1:
- foo=0
- def __repr__(self):
- raise Exception("Ha Ha fooled you, I'm a broken repr().")
-
- class TestBrokenClass:
- def test_explicit_bad_repr(self):
- t = BrokenRepr1()
- pytest.raises(Exception, 'repr(t)')
-
- def test_implicit_bad_repr1(self):
- t = BrokenRepr1()
- assert t.foo == 1
-
- """)
- reprec = testdir.inline_run(p)
- passed, skipped, failed = reprec.listoutcomes()
- assert len(failed) == 1
- out = failed[0].longrepr.reprcrash.message
- assert out.find("""[Exception("Ha Ha fooled you, I'm a broken repr().") raised in repr()]""") != -1 #'
-
- def test_skip_file_by_conftest(self, testdir):
- testdir.makepyfile(conftest="""
- import pytest
- def pytest_collect_file():
- pytest.skip("intentional")
- """, test_file="""
- def test_one(): pass
- """)
- try:
- reprec = testdir.inline_run(testdir.tmpdir)
- except pytest.skip.Exception:
- pytest.fail("wrong skipped caught")
- reports = reprec.getreports("pytest_collectreport")
- assert len(reports) == 1
- assert reports[0].skipped
-
-class TestNewSession(SessionTests):
-
- def test_order_of_execution(self, testdir):
- reprec = testdir.inline_runsource("""
- l = []
- def test_1():
- l.append(1)
- def test_2():
- l.append(2)
- def test_3():
- assert l == [1,2]
- class Testmygroup:
- reslist = l
- def test_1(self):
- self.reslist.append(1)
- def test_2(self):
- self.reslist.append(2)
- def test_3(self):
- self.reslist.append(3)
- def test_4(self):
- assert self.reslist == [1,2,1,2,3]
- """)
- passed, skipped, failed = reprec.countoutcomes()
- assert failed == skipped == 0
- assert passed == 7
- # also test listnames() here ...
-
- def test_collect_only_with_various_situations(self, testdir):
- p = testdir.makepyfile(
- test_one="""
- def test_one():
- raise ValueError()
-
- class TestX:
- def test_method_one(self):
- pass
-
- class TestY(TestX):
- pass
- """,
- test_two="""
- import pytest
- pytest.skip('xxx')
- """,
- test_three="xxxdsadsadsadsa",
- __init__=""
- )
- reprec = testdir.inline_run('--collect-only', p.dirpath())
-
- itemstarted = reprec.getcalls("pytest_itemcollected")
- assert len(itemstarted) == 3
- assert not reprec.getreports("pytest_runtest_logreport")
- started = reprec.getcalls("pytest_collectstart")
- finished = reprec.getreports("pytest_collectreport")
- assert len(started) == len(finished)
- assert len(started) == 8 # XXX extra TopCollector
- colfail = [x for x in finished if x.failed]
- colskipped = [x for x in finished if x.skipped]
- assert len(colfail) == 1
- assert len(colskipped) == 1
-
- def test_minus_x_import_error(self, testdir):
- testdir.makepyfile(__init__="")
- testdir.makepyfile(test_one="xxxx", test_two="yyyy")
- reprec = testdir.inline_run("-x", testdir.tmpdir)
- finished = reprec.getreports("pytest_collectreport")
- colfail = [x for x in finished if x.failed]
- assert len(colfail) == 1
-
-
-def test_plugin_specify(testdir):
- pytest.raises(ImportError, """
- testdir.parseconfig("-p", "nqweotexistent")
- """)
- #pytest.raises(ImportError,
- # "config.do_configure(config)"
- #)
-
-def test_plugin_already_exists(testdir):
- config = testdir.parseconfig("-p", "terminal")
- assert config.option.plugins == ['terminal']
- config._do_configure()
- config._ensure_unconfigure()
-
-def test_exclude(testdir):
- hellodir = testdir.mkdir("hello")
- hellodir.join("test_hello.py").write("x y syntaxerror")
- hello2dir = testdir.mkdir("hello2")
- hello2dir.join("test_hello2.py").write("x y syntaxerror")
- testdir.makepyfile(test_ok="def test_pass(): pass")
- result = testdir.runpytest("--ignore=hello", "--ignore=hello2")
- assert result.ret == 0
- result.stdout.fnmatch_lines(["*1 passed*"])
-
-def test_sessionfinish_with_start(testdir):
- testdir.makeconftest("""
- import os
- l = []
- def pytest_sessionstart():
- l.append(os.getcwd())
- os.chdir("..")
-
- def pytest_sessionfinish():
- assert l[0] == os.getcwd()
-
- """)
- res = testdir.runpytest("--collect-only")
- assert res.ret == EXIT_NOTESTSCOLLECTED
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_skipping.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_skipping.py
deleted file mode 100644
index 3464974e0c2..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_skipping.py
+++ /dev/null
@@ -1,917 +0,0 @@
-import pytest
-import sys
-
-from _pytest.skipping import MarkEvaluator, folded_skips, pytest_runtest_setup
-from _pytest.runner import runtestprotocol
-
-
-class TestEvaluator:
- def test_no_marker(self, testdir):
- item = testdir.getitem("def test_func(): pass")
- evalskipif = MarkEvaluator(item, 'skipif')
- assert not evalskipif
- assert not evalskipif.istrue()
-
- def test_marked_no_args(self, testdir):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.xyz
- def test_func():
- pass
- """)
- ev = MarkEvaluator(item, 'xyz')
- assert ev
- assert ev.istrue()
- expl = ev.getexplanation()
- assert expl == ""
- assert not ev.get("run", False)
-
- def test_marked_one_arg(self, testdir):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.xyz("hasattr(os, 'sep')")
- def test_func():
- pass
- """)
- ev = MarkEvaluator(item, 'xyz')
- assert ev
- assert ev.istrue()
- expl = ev.getexplanation()
- assert expl == "condition: hasattr(os, 'sep')"
-
- @pytest.mark.skipif('sys.version_info[0] >= 3')
- def test_marked_one_arg_unicode(self, testdir):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.xyz(u"hasattr(os, 'sep')")
- def test_func():
- pass
- """)
- ev = MarkEvaluator(item, 'xyz')
- assert ev
- assert ev.istrue()
- expl = ev.getexplanation()
- assert expl == "condition: hasattr(os, 'sep')"
-
- def test_marked_one_arg_with_reason(self, testdir):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.xyz("hasattr(os, 'sep')", attr=2, reason="hello world")
- def test_func():
- pass
- """)
- ev = MarkEvaluator(item, 'xyz')
- assert ev
- assert ev.istrue()
- expl = ev.getexplanation()
- assert expl == "hello world"
- assert ev.get("attr") == 2
-
- def test_marked_one_arg_twice(self, testdir):
- lines = [
- '''@pytest.mark.skipif("not hasattr(os, 'murks')")''',
- '''@pytest.mark.skipif("hasattr(os, 'murks')")'''
- ]
- for i in range(0, 2):
- item = testdir.getitem("""
- import pytest
- %s
- %s
- def test_func():
- pass
- """ % (lines[i], lines[(i+1) %2]))
- ev = MarkEvaluator(item, 'skipif')
- assert ev
- assert ev.istrue()
- expl = ev.getexplanation()
- assert expl == "condition: not hasattr(os, 'murks')"
-
- def test_marked_one_arg_twice2(self, testdir):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.skipif("hasattr(os, 'murks')")
- @pytest.mark.skipif("not hasattr(os, 'murks')")
- def test_func():
- pass
- """)
- ev = MarkEvaluator(item, 'skipif')
- assert ev
- assert ev.istrue()
- expl = ev.getexplanation()
- assert expl == "condition: not hasattr(os, 'murks')"
-
- def test_marked_skip_with_not_string(self, testdir):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.skipif(False)
- def test_func():
- pass
- """)
- ev = MarkEvaluator(item, 'skipif')
- exc = pytest.raises(pytest.fail.Exception, ev.istrue)
- assert """Failed: you need to specify reason=STRING when using booleans as conditions.""" in exc.value.msg
-
- def test_skipif_class(self, testdir):
- item, = testdir.getitems("""
- import pytest
- class TestClass:
- pytestmark = pytest.mark.skipif("config._hackxyz")
- def test_func(self):
- pass
- """)
- item.config._hackxyz = 3
- ev = MarkEvaluator(item, 'skipif')
- assert ev.istrue()
- expl = ev.getexplanation()
- assert expl == "condition: config._hackxyz"
-
-
-class TestXFail:
-
- @pytest.mark.parametrize('strict', [True, False])
- def test_xfail_simple(self, testdir, strict):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.xfail(strict=%s)
- def test_func():
- assert 0
- """ % strict)
- reports = runtestprotocol(item, log=False)
- assert len(reports) == 3
- callreport = reports[1]
- assert callreport.skipped
- assert callreport.wasxfail == ""
-
- def test_xfail_xpassed(self, testdir):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.xfail
- def test_func():
- assert 1
- """)
- reports = runtestprotocol(item, log=False)
- assert len(reports) == 3
- callreport = reports[1]
- assert callreport.failed
- assert callreport.wasxfail == ""
-
- def test_xfail_run_anyway(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.xfail
- def test_func():
- assert 0
- def test_func2():
- pytest.xfail("hello")
- """)
- result = testdir.runpytest("--runxfail")
- result.stdout.fnmatch_lines([
- "*def test_func():*",
- "*assert 0*",
- "*1 failed*1 pass*",
- ])
-
- def test_xfail_evalfalse_but_fails(self, testdir):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.xfail('False')
- def test_func():
- assert 0
- """)
- reports = runtestprotocol(item, log=False)
- callreport = reports[1]
- assert callreport.failed
- assert not hasattr(callreport, "wasxfail")
- assert 'xfail' in callreport.keywords
-
- def test_xfail_not_report_default(self, testdir):
- p = testdir.makepyfile(test_one="""
- import pytest
- @pytest.mark.xfail
- def test_this():
- assert 0
- """)
- testdir.runpytest(p, '-v')
- #result.stdout.fnmatch_lines([
- # "*HINT*use*-r*"
- #])
-
- def test_xfail_not_run_xfail_reporting(self, testdir):
- p = testdir.makepyfile(test_one="""
- import pytest
- @pytest.mark.xfail(run=False, reason="noway")
- def test_this():
- assert 0
- @pytest.mark.xfail("True", run=False)
- def test_this_true():
- assert 0
- @pytest.mark.xfail("False", run=False, reason="huh")
- def test_this_false():
- assert 1
- """)
- result = testdir.runpytest(p, '--report=xfailed', )
- result.stdout.fnmatch_lines([
- "*test_one*test_this*",
- "*NOTRUN*noway",
- "*test_one*test_this_true*",
- "*NOTRUN*condition:*True*",
- "*1 passed*",
- ])
-
- def test_xfail_not_run_no_setup_run(self, testdir):
- p = testdir.makepyfile(test_one="""
- import pytest
- @pytest.mark.xfail(run=False, reason="hello")
- def test_this():
- assert 0
- def setup_module(mod):
- raise ValueError(42)
- """)
- result = testdir.runpytest(p, '--report=xfailed', )
- result.stdout.fnmatch_lines([
- "*test_one*test_this*",
- "*NOTRUN*hello",
- "*1 xfailed*",
- ])
-
- def test_xfail_xpass(self, testdir):
- p = testdir.makepyfile(test_one="""
- import pytest
- @pytest.mark.xfail
- def test_that():
- assert 1
- """)
- result = testdir.runpytest(p, '-rX')
- result.stdout.fnmatch_lines([
- "*XPASS*test_that*",
- "*1 xpassed*"
- ])
- assert result.ret == 0
-
- def test_xfail_imperative(self, testdir):
- p = testdir.makepyfile("""
- import pytest
- def test_this():
- pytest.xfail("hello")
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*1 xfailed*",
- ])
- result = testdir.runpytest(p, "-rx")
- result.stdout.fnmatch_lines([
- "*XFAIL*test_this*",
- "*reason:*hello*",
- ])
- result = testdir.runpytest(p, "--runxfail")
- result.stdout.fnmatch_lines("*1 pass*")
-
- def test_xfail_imperative_in_setup_function(self, testdir):
- p = testdir.makepyfile("""
- import pytest
- def setup_function(function):
- pytest.xfail("hello")
-
- def test_this():
- assert 0
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*1 xfailed*",
- ])
- result = testdir.runpytest(p, "-rx")
- result.stdout.fnmatch_lines([
- "*XFAIL*test_this*",
- "*reason:*hello*",
- ])
- result = testdir.runpytest(p, "--runxfail")
- result.stdout.fnmatch_lines("""
- *def test_this*
- *1 fail*
- """)
-
- def xtest_dynamic_xfail_set_during_setup(self, testdir):
- p = testdir.makepyfile("""
- import pytest
- def setup_function(function):
- pytest.mark.xfail(function)
- def test_this():
- assert 0
- def test_that():
- assert 1
- """)
- result = testdir.runpytest(p, '-rxX')
- result.stdout.fnmatch_lines([
- "*XFAIL*test_this*",
- "*XPASS*test_that*",
- ])
-
- def test_dynamic_xfail_no_run(self, testdir):
- p = testdir.makepyfile("""
- import pytest
- def pytest_funcarg__arg(request):
- request.applymarker(pytest.mark.xfail(run=False))
- def test_this(arg):
- assert 0
- """)
- result = testdir.runpytest(p, '-rxX')
- result.stdout.fnmatch_lines([
- "*XFAIL*test_this*",
- "*NOTRUN*",
- ])
-
- def test_dynamic_xfail_set_during_funcarg_setup(self, testdir):
- p = testdir.makepyfile("""
- import pytest
- def pytest_funcarg__arg(request):
- request.applymarker(pytest.mark.xfail)
- def test_this2(arg):
- assert 0
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*1 xfailed*",
- ])
-
-
- @pytest.mark.parametrize('expected, actual, matchline',
- [('TypeError', 'TypeError', "*1 xfailed*"),
- ('(AttributeError, TypeError)', 'TypeError', "*1 xfailed*"),
- ('TypeError', 'IndexError', "*1 failed*"),
- ('(AttributeError, TypeError)', 'IndexError', "*1 failed*"),
- ])
- def test_xfail_raises(self, expected, actual, matchline, testdir):
- p = testdir.makepyfile("""
- import pytest
- @pytest.mark.xfail(raises=%s)
- def test_raises():
- raise %s()
- """ % (expected, actual))
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- matchline,
- ])
-
- def test_strict_sanity(self, testdir):
- """sanity check for xfail(strict=True): a failing test should behave
- exactly like a normal xfail.
- """
- p = testdir.makepyfile("""
- import pytest
- @pytest.mark.xfail(reason='unsupported feature', strict=True)
- def test_foo():
- assert 0
- """)
- result = testdir.runpytest(p, '-rxX')
- result.stdout.fnmatch_lines([
- '*XFAIL*',
- '*unsupported feature*',
- ])
- assert result.ret == 0
-
- @pytest.mark.parametrize('strict', [True, False])
- def test_strict_xfail(self, testdir, strict):
- p = testdir.makepyfile("""
- import pytest
-
- @pytest.mark.xfail(reason='unsupported feature', strict=%s)
- def test_foo():
- with open('foo_executed', 'w'): pass # make sure test executes
- """ % strict)
- result = testdir.runpytest(p, '-rxX')
- if strict:
- result.stdout.fnmatch_lines([
- '*test_foo*',
- '*XPASS(strict)*unsupported feature*',
- ])
- else:
- result.stdout.fnmatch_lines([
- '*test_strict_xfail*',
- 'XPASS test_strict_xfail.py::test_foo unsupported feature',
- ])
- assert result.ret == (1 if strict else 0)
- assert testdir.tmpdir.join('foo_executed').isfile()
-
- @pytest.mark.parametrize('strict', [True, False])
- def test_strict_xfail_condition(self, testdir, strict):
- p = testdir.makepyfile("""
- import pytest
-
- @pytest.mark.xfail(False, reason='unsupported feature', strict=%s)
- def test_foo():
- pass
- """ % strict)
- result = testdir.runpytest(p, '-rxX')
- result.stdout.fnmatch_lines('*1 passed*')
- assert result.ret == 0
-
- @pytest.mark.parametrize('strict_val', ['true', 'false'])
- def test_strict_xfail_default_from_file(self, testdir, strict_val):
- testdir.makeini('''
- [pytest]
- xfail_strict = %s
- ''' % strict_val)
- p = testdir.makepyfile("""
- import pytest
- @pytest.mark.xfail(reason='unsupported feature')
- def test_foo():
- pass
- """)
- result = testdir.runpytest(p, '-rxX')
- strict = strict_val == 'true'
- result.stdout.fnmatch_lines('*1 failed*' if strict else '*1 xpassed*')
- assert result.ret == (1 if strict else 0)
-
-
-class TestXFailwithSetupTeardown:
- def test_failing_setup_issue9(self, testdir):
- testdir.makepyfile("""
- import pytest
- def setup_function(func):
- assert 0
-
- @pytest.mark.xfail
- def test_func():
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*1 xfail*",
- ])
-
- def test_failing_teardown_issue9(self, testdir):
- testdir.makepyfile("""
- import pytest
- def teardown_function(func):
- assert 0
-
- @pytest.mark.xfail
- def test_func():
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*1 xfail*",
- ])
-
-
-class TestSkip:
- def test_skip_class(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.skip
- class TestSomething(object):
- def test_foo(self):
- pass
- def test_bar(self):
- pass
-
- def test_baz():
- pass
- """)
- rec = testdir.inline_run()
- rec.assertoutcome(skipped=2, passed=1)
-
- def test_skips_on_false_string(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.skip('False')
- def test_foo():
- pass
- """)
- rec = testdir.inline_run()
- rec.assertoutcome(skipped=1)
-
- def test_arg_as_reason(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.skip('testing stuff')
- def test_bar():
- pass
- """)
- result = testdir.runpytest('-rs')
- result.stdout.fnmatch_lines([
- "*testing stuff*",
- "*1 skipped*",
- ])
-
- def test_skip_no_reason(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.skip
- def test_foo():
- pass
- """)
- result = testdir.runpytest('-rs')
- result.stdout.fnmatch_lines([
- "*unconditional skip*",
- "*1 skipped*",
- ])
-
- def test_skip_with_reason(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.skip(reason="for lolz")
- def test_bar():
- pass
- """)
- result = testdir.runpytest('-rs')
- result.stdout.fnmatch_lines([
- "*for lolz*",
- "*1 skipped*",
- ])
-
- def test_only_skips_marked_test(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.skip
- def test_foo():
- pass
- @pytest.mark.skip(reason="nothing in particular")
- def test_bar():
- pass
- def test_baz():
- assert True
- """)
- result = testdir.runpytest('-rs')
- result.stdout.fnmatch_lines([
- "*nothing in particular*",
- "*1 passed*2 skipped*",
- ])
-
-class TestSkipif:
- def test_skipif_conditional(self, testdir):
- item = testdir.getitem("""
- import pytest
- @pytest.mark.skipif("hasattr(os, 'sep')")
- def test_func():
- pass
- """) # noqa
- x = pytest.raises(pytest.skip.Exception, lambda:
- pytest_runtest_setup(item))
- assert x.value.msg == "condition: hasattr(os, 'sep')"
-
- @pytest.mark.parametrize('params', [
- '"hasattr(sys, \'platform\')"',
- 'True, reason="invalid platform"',
- ])
- def test_skipif_reporting(self, testdir, params):
- p = testdir.makepyfile(test_foo="""
- import pytest
- @pytest.mark.skipif(%(params)s)
- def test_that():
- assert 0
- """ % dict(params=params))
- result = testdir.runpytest(p, '-s', '-rs')
- result.stdout.fnmatch_lines([
- "*SKIP*1*test_foo.py*platform*",
- "*1 skipped*"
- ])
- assert result.ret == 0
-
- @pytest.mark.parametrize('marker, msg1, msg2', [
- ('skipif', 'SKIP', 'skipped'),
- ('xfail', 'XPASS', 'xpassed'),
- ])
- def test_skipif_reporting_multiple(self, testdir, marker, msg1, msg2):
- testdir.makepyfile(test_foo="""
- import pytest
- @pytest.mark.{marker}(False, reason='first_condition')
- @pytest.mark.{marker}(True, reason='second_condition')
- def test_foobar():
- assert 1
- """.format(marker=marker))
- result = testdir.runpytest('-s', '-rsxX')
- result.stdout.fnmatch_lines([
- "*{msg1}*test_foo.py*second_condition*".format(msg1=msg1),
- "*1 {msg2}*".format(msg2=msg2),
- ])
- assert result.ret == 0
-
-
-def test_skip_not_report_default(testdir):
- p = testdir.makepyfile(test_one="""
- import pytest
- def test_this():
- pytest.skip("hello")
- """)
- result = testdir.runpytest(p, '-v')
- result.stdout.fnmatch_lines([
- #"*HINT*use*-r*",
- "*1 skipped*",
- ])
-
-
-def test_skipif_class(testdir):
- p = testdir.makepyfile("""
- import pytest
-
- class TestClass:
- pytestmark = pytest.mark.skipif("True")
- def test_that(self):
- assert 0
- def test_though(self):
- assert 0
- """)
- result = testdir.runpytest(p)
- result.stdout.fnmatch_lines([
- "*2 skipped*"
- ])
-
-
-def test_skip_reasons_folding():
- path = 'xyz'
- lineno = 3
- message = "justso"
- longrepr = (path, lineno, message)
-
- class X:
- pass
- ev1 = X()
- ev1.when = "execute"
- ev1.skipped = True
- ev1.longrepr = longrepr
-
- ev2 = X()
- ev2.longrepr = longrepr
- ev2.skipped = True
-
- l = folded_skips([ev1, ev2])
- assert len(l) == 1
- num, fspath, lineno, reason = l[0]
- assert num == 2
- assert fspath == path
- assert lineno == lineno
- assert reason == message
-
-def test_skipped_reasons_functional(testdir):
- testdir.makepyfile(
- test_one="""
- from conftest import doskip
- def setup_function(func):
- doskip()
- def test_func():
- pass
- class TestClass:
- def test_method(self):
- doskip()
- """,
- test_two = """
- from conftest import doskip
- doskip()
- """,
- conftest = """
- import pytest
- def doskip():
- pytest.skip('test')
- """
- )
- result = testdir.runpytest('--report=skipped')
- result.stdout.fnmatch_lines([
- "*SKIP*3*conftest.py:3: test",
- ])
- assert result.ret == 0
-
-def test_reportchars(testdir):
- testdir.makepyfile("""
- import pytest
- def test_1():
- assert 0
- @pytest.mark.xfail
- def test_2():
- assert 0
- @pytest.mark.xfail
- def test_3():
- pass
- def test_4():
- pytest.skip("four")
- """)
- result = testdir.runpytest("-rfxXs")
- result.stdout.fnmatch_lines([
- "FAIL*test_1*",
- "XFAIL*test_2*",
- "XPASS*test_3*",
- "SKIP*four*",
- ])
-
-def test_reportchars_error(testdir):
- testdir.makepyfile(
- conftest="""
- def pytest_runtest_teardown():
- assert 0
- """,
- test_simple="""
- def test_foo():
- pass
- """)
- result = testdir.runpytest('-rE')
- result.stdout.fnmatch_lines([
- 'ERROR*test_foo*',
- ])
-
-def test_reportchars_all(testdir):
- testdir.makepyfile("""
- import pytest
- def test_1():
- assert 0
- @pytest.mark.xfail
- def test_2():
- assert 0
- @pytest.mark.xfail
- def test_3():
- pass
- def test_4():
- pytest.skip("four")
- """)
- result = testdir.runpytest("-ra")
- result.stdout.fnmatch_lines([
- "FAIL*test_1*",
- "SKIP*four*",
- "XFAIL*test_2*",
- "XPASS*test_3*",
- ])
-
-def test_reportchars_all_error(testdir):
- testdir.makepyfile(
- conftest="""
- def pytest_runtest_teardown():
- assert 0
- """,
- test_simple="""
- def test_foo():
- pass
- """)
- result = testdir.runpytest('-ra')
- result.stdout.fnmatch_lines([
- 'ERROR*test_foo*',
- ])
-
-@pytest.mark.xfail("hasattr(sys, 'pypy_version_info')")
-def test_errors_in_xfail_skip_expressions(testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.skipif("asd")
- def test_nameerror():
- pass
- @pytest.mark.xfail("syntax error")
- def test_syntax():
- pass
-
- def test_func():
- pass
- """)
- result = testdir.runpytest()
- markline = " ^"
- if sys.platform.startswith("java"):
- # XXX report this to java
- markline = "*" + markline[8:]
- result.stdout.fnmatch_lines([
- "*ERROR*test_nameerror*",
- "*evaluating*skipif*expression*",
- "*asd*",
- "*ERROR*test_syntax*",
- "*evaluating*xfail*expression*",
- " syntax error",
- markline,
- "SyntaxError: invalid syntax",
- "*1 pass*2 error*",
- ])
-
-def test_xfail_skipif_with_globals(testdir):
- testdir.makepyfile("""
- import pytest
- x = 3
- @pytest.mark.skipif("x == 3")
- def test_skip1():
- pass
- @pytest.mark.xfail("x == 3")
- def test_boolean():
- assert 0
- """)
- result = testdir.runpytest("-rsx")
- result.stdout.fnmatch_lines([
- "*SKIP*x == 3*",
- "*XFAIL*test_boolean*",
- "*x == 3*",
- ])
-
-def test_direct_gives_error(testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.skipif(True)
- def test_skip1():
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*1 error*",
- ])
-
-
-def test_default_markers(testdir):
- result = testdir.runpytest("--markers")
- result.stdout.fnmatch_lines([
- "*skipif(*condition)*skip*",
- "*xfail(*condition, reason=None, run=True, raises=None)*expected failure*",
- ])
-
-def test_xfail_test_setup_exception(testdir):
- testdir.makeconftest("""
- def pytest_runtest_setup():
- 0 / 0
- """)
- p = testdir.makepyfile("""
- import pytest
- @pytest.mark.xfail
- def test_func():
- assert 0
- """)
- result = testdir.runpytest(p)
- assert result.ret == 0
- assert 'xfailed' in result.stdout.str()
- assert 'xpassed' not in result.stdout.str()
-
-def test_imperativeskip_on_xfail_test(testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.xfail
- def test_that_fails():
- assert 0
-
- @pytest.mark.skipif("True")
- def test_hello():
- pass
- """)
- testdir.makeconftest("""
- import pytest
- def pytest_runtest_setup(item):
- pytest.skip("abc")
- """)
- result = testdir.runpytest("-rsxX")
- result.stdout.fnmatch_lines_random("""
- *SKIP*abc*
- *SKIP*condition: True*
- *2 skipped*
- """)
-
-class TestBooleanCondition:
- def test_skipif(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.skipif(True, reason="True123")
- def test_func1():
- pass
- @pytest.mark.skipif(False, reason="True123")
- def test_func2():
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines("""
- *1 passed*1 skipped*
- """)
-
- def test_skipif_noreason(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.skipif(True)
- def test_func():
- pass
- """)
- result = testdir.runpytest("-rs")
- result.stdout.fnmatch_lines("""
- *1 error*
- """)
-
- def test_xfail(self, testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.xfail(True, reason="True123")
- def test_func():
- assert 0
- """)
- result = testdir.runpytest("-rxs")
- result.stdout.fnmatch_lines("""
- *XFAIL*
- *True123*
- *1 xfail*
- """)
-
-
-def test_xfail_item(testdir):
- # Ensure pytest.xfail works with non-Python Item
- testdir.makeconftest("""
- import pytest
-
- class MyItem(pytest.Item):
- nodeid = 'foo'
- def runtest(self):
- pytest.xfail("Expected Failure")
-
- def pytest_collect_file(path, parent):
- return MyItem("foo", parent)
- """)
- result = testdir.inline_run()
- passed, skipped, failed = result.listoutcomes()
- assert not failed
- xfailed = [r for r in skipped if hasattr(r, 'wasxfail')]
- assert xfailed
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_terminal.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_terminal.py
deleted file mode 100644
index b43d6b37923..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_terminal.py
+++ /dev/null
@@ -1,880 +0,0 @@
-"""
-terminal reporting of the full testing process.
-"""
-import collections
-import sys
-
-import _pytest._pluggy as pluggy
-import _pytest._code
-import py
-import pytest
-from _pytest import runner
-from _pytest.main import EXIT_NOTESTSCOLLECTED
-from _pytest.terminal import TerminalReporter, repr_pythonversion, getreportopt
-from _pytest.terminal import build_summary_stats_line, _plugin_nameversions
-
-
-def basic_run_report(item):
- runner.call_and_report(item, "setup", log=False)
- return runner.call_and_report(item, "call", log=False)
-
-DistInfo = collections.namedtuple('DistInfo', ['project_name', 'version'])
-
-
-class Option:
- def __init__(self, verbose=False, fulltrace=False):
- self.verbose = verbose
- self.fulltrace = fulltrace
-
- @property
- def args(self):
- l = []
- if self.verbose:
- l.append('-v')
- if self.fulltrace:
- l.append('--fulltrace')
- return l
-
-def pytest_generate_tests(metafunc):
- if "option" in metafunc.fixturenames:
- metafunc.addcall(id="default",
- funcargs={'option': Option(verbose=False)})
- metafunc.addcall(id="verbose",
- funcargs={'option': Option(verbose=True)})
- metafunc.addcall(id="quiet",
- funcargs={'option': Option(verbose= -1)})
- metafunc.addcall(id="fulltrace",
- funcargs={'option': Option(fulltrace=True)})
-
-
-@pytest.mark.parametrize('input,expected', [
- ([DistInfo(project_name='test', version=1)], ['test-1']),
- ([DistInfo(project_name='pytest-test', version=1)], ['test-1']),
- ([
- DistInfo(project_name='test', version=1),
- DistInfo(project_name='test', version=1)
- ], ['test-1']),
-], ids=['normal', 'prefix-strip', 'deduplicate'])
-def test_plugin_nameversion(input, expected):
- pluginlist = [(None, x) for x in input]
- result = _plugin_nameversions(pluginlist)
- assert result == expected
-
-
-class TestTerminal:
- def test_pass_skip_fail(self, testdir, option):
- testdir.makepyfile("""
- import pytest
- def test_ok():
- pass
- def test_skip():
- pytest.skip("xx")
- def test_func():
- assert 0
- """)
- result = testdir.runpytest(*option.args)
- if option.verbose:
- result.stdout.fnmatch_lines([
- "*test_pass_skip_fail.py::test_ok PASS*",
- "*test_pass_skip_fail.py::test_skip SKIP*",
- "*test_pass_skip_fail.py::test_func FAIL*",
- ])
- else:
- result.stdout.fnmatch_lines([
- "*test_pass_skip_fail.py .sF"
- ])
- result.stdout.fnmatch_lines([
- " def test_func():",
- "> assert 0",
- "E assert 0",
- ])
-
- def test_internalerror(self, testdir, linecomp):
- modcol = testdir.getmodulecol("def test_one(): pass")
- rep = TerminalReporter(modcol.config, file=linecomp.stringio)
- excinfo = pytest.raises(ValueError, "raise ValueError('hello')")
- rep.pytest_internalerror(excinfo.getrepr())
- linecomp.assert_contains_lines([
- "INTERNALERROR> *ValueError*hello*"
- ])
-
- def test_writeline(self, testdir, linecomp):
- modcol = testdir.getmodulecol("def test_one(): pass")
- rep = TerminalReporter(modcol.config, file=linecomp.stringio)
- rep.write_fspath_result(modcol.nodeid, ".")
- rep.write_line("hello world")
- lines = linecomp.stringio.getvalue().split('\n')
- assert not lines[0]
- assert lines[1].endswith(modcol.name + " .")
- assert lines[2] == "hello world"
-
- def test_show_runtest_logstart(self, testdir, linecomp):
- item = testdir.getitem("def test_func(): pass")
- tr = TerminalReporter(item.config, file=linecomp.stringio)
- item.config.pluginmanager.register(tr)
- location = item.reportinfo()
- tr.config.hook.pytest_runtest_logstart(nodeid=item.nodeid,
- location=location, fspath=str(item.fspath))
- linecomp.assert_contains_lines([
- "*test_show_runtest_logstart.py*"
- ])
-
- def test_runtest_location_shown_before_test_starts(self, testdir):
- testdir.makepyfile("""
- def test_1():
- import time
- time.sleep(20)
- """)
- child = testdir.spawn_pytest("")
- child.expect(".*test_runtest_location.*py")
- child.sendeof()
- child.kill(15)
-
- def test_itemreport_subclasses_show_subclassed_file(self, testdir):
- testdir.makepyfile(test_p1="""
- class BaseTests:
- def test_p1(self):
- pass
- class TestClass(BaseTests):
- pass
- """)
- p2 = testdir.makepyfile(test_p2="""
- from test_p1 import BaseTests
- class TestMore(BaseTests):
- pass
- """)
- result = testdir.runpytest(p2)
- result.stdout.fnmatch_lines([
- "*test_p2.py .",
- "*1 passed*",
- ])
- result = testdir.runpytest("-v", p2)
- result.stdout.fnmatch_lines([
- "*test_p2.py::TestMore::test_p1* <- *test_p1.py*PASSED",
- ])
-
- def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir):
- a = testdir.mkpydir("a123")
- a.join("test_hello123.py").write(_pytest._code.Source("""
- class TestClass:
- def test_method(self):
- pass
- """))
- result = testdir.runpytest("-v")
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*a123/test_hello123.py*PASS*",
- ])
- assert " <- " not in result.stdout.str()
-
- def test_keyboard_interrupt(self, testdir, option):
- testdir.makepyfile("""
- def test_foobar():
- assert 0
- def test_spamegg():
- import py; pytest.skip('skip me please!')
- def test_interrupt_me():
- raise KeyboardInterrupt # simulating the user
- """)
-
- result = testdir.runpytest(*option.args, no_reraise_ctrlc=True)
- result.stdout.fnmatch_lines([
- " def test_foobar():",
- "> assert 0",
- "E assert 0",
- "*_keyboard_interrupt.py:6: KeyboardInterrupt*",
- ])
- if option.fulltrace:
- result.stdout.fnmatch_lines([
- "*raise KeyboardInterrupt # simulating the user*",
- ])
- else:
- result.stdout.fnmatch_lines([
- "to show a full traceback on KeyboardInterrupt use --fulltrace"
- ])
- result.stdout.fnmatch_lines(['*KeyboardInterrupt*'])
-
- def test_keyboard_in_sessionstart(self, testdir):
- testdir.makeconftest("""
- def pytest_sessionstart():
- raise KeyboardInterrupt
- """)
- testdir.makepyfile("""
- def test_foobar():
- pass
- """)
-
- result = testdir.runpytest(no_reraise_ctrlc=True)
- assert result.ret == 2
- result.stdout.fnmatch_lines(['*KeyboardInterrupt*'])
-
-
-class TestCollectonly:
- def test_collectonly_basic(self, testdir):
- testdir.makepyfile("""
- def test_func():
- pass
- """)
- result = testdir.runpytest("--collect-only",)
- result.stdout.fnmatch_lines([
- "<Module 'test_collectonly_basic.py'>",
- " <Function 'test_func'>",
- ])
-
- def test_collectonly_skipped_module(self, testdir):
- testdir.makepyfile("""
- import pytest
- pytest.skip("hello")
- """)
- result = testdir.runpytest("--collect-only", "-rs")
- result.stdout.fnmatch_lines([
- "SKIP*hello*",
- "*1 skip*",
- ])
-
- def test_collectonly_failed_module(self, testdir):
- testdir.makepyfile("""raise ValueError(0)""")
- result = testdir.runpytest("--collect-only")
- result.stdout.fnmatch_lines([
- "*raise ValueError*",
- "*1 error*",
- ])
-
- def test_collectonly_fatal(self, testdir):
- testdir.makeconftest("""
- def pytest_collectstart(collector):
- assert 0, "urgs"
- """)
- result = testdir.runpytest("--collect-only")
- result.stdout.fnmatch_lines([
- "*INTERNAL*args*"
- ])
- assert result.ret == 3
-
- def test_collectonly_simple(self, testdir):
- p = testdir.makepyfile("""
- def test_func1():
- pass
- class TestClass:
- def test_method(self):
- pass
- """)
- result = testdir.runpytest("--collect-only", p)
- #assert stderr.startswith("inserting into sys.path")
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*<Module '*.py'>",
- "* <Function 'test_func1'*>",
- "* <Class 'TestClass'>",
- #"* <Instance '()'>",
- "* <Function 'test_method'*>",
- ])
-
- def test_collectonly_error(self, testdir):
- p = testdir.makepyfile("import Errlkjqweqwe")
- result = testdir.runpytest("--collect-only", p)
- assert result.ret == 1
- result.stdout.fnmatch_lines(_pytest._code.Source("""
- *ERROR*
- *import Errlk*
- *ImportError*
- *1 error*
- """).strip())
-
- def test_collectonly_missing_path(self, testdir):
- """this checks issue 115,
- failure in parseargs will cause session
- not to have the items attribute
- """
- result = testdir.runpytest("--collect-only", "uhm_missing_path")
- assert result.ret == 4
- result.stderr.fnmatch_lines([
- '*ERROR: file not found*',
- ])
-
- def test_collectonly_quiet(self, testdir):
- testdir.makepyfile("def test_foo(): pass")
- result = testdir.runpytest("--collect-only", "-q")
- result.stdout.fnmatch_lines([
- '*test_foo*',
- ])
-
- def test_collectonly_more_quiet(self, testdir):
- testdir.makepyfile(test_fun="def test_foo(): pass")
- result = testdir.runpytest("--collect-only", "-qq")
- result.stdout.fnmatch_lines([
- '*test_fun.py: 1*',
- ])
-
-
-def test_repr_python_version(monkeypatch):
- try:
- monkeypatch.setattr(sys, 'version_info', (2, 5, 1, 'final', 0))
- assert repr_pythonversion() == "2.5.1-final-0"
- py.std.sys.version_info = x = (2, 3)
- assert repr_pythonversion() == str(x)
- finally:
- monkeypatch.undo() # do this early as pytest can get confused
-
-class TestFixtureReporting:
- def test_setup_fixture_error(self, testdir):
- testdir.makepyfile("""
- def setup_function(function):
- print ("setup func")
- assert 0
- def test_nada():
- pass
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*ERROR at setup of test_nada*",
- "*setup_function(function):*",
- "*setup func*",
- "*assert 0*",
- "*1 error*",
- ])
- assert result.ret != 0
-
- def test_teardown_fixture_error(self, testdir):
- testdir.makepyfile("""
- def test_nada():
- pass
- def teardown_function(function):
- print ("teardown func")
- assert 0
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*ERROR at teardown*",
- "*teardown_function(function):*",
- "*assert 0*",
- "*Captured stdout*",
- "*teardown func*",
- "*1 passed*1 error*",
- ])
-
- def test_teardown_fixture_error_and_test_failure(self, testdir):
- testdir.makepyfile("""
- def test_fail():
- assert 0, "failingfunc"
-
- def teardown_function(function):
- print ("teardown func")
- assert False
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*ERROR at teardown of test_fail*",
- "*teardown_function(function):*",
- "*assert False*",
- "*Captured stdout*",
- "*teardown func*",
-
- "*test_fail*",
- "*def test_fail():",
- "*failingfunc*",
- "*1 failed*1 error*",
- ])
-
-class TestTerminalFunctional:
- def test_deselected(self, testdir):
- testpath = testdir.makepyfile("""
- def test_one():
- pass
- def test_two():
- pass
- def test_three():
- pass
- """
- )
- result = testdir.runpytest("-k", "test_two:", testpath)
- result.stdout.fnmatch_lines([
- "*test_deselected.py ..",
- "=* 1 test*deselected by*test_two:*=",
- ])
- assert result.ret == 0
-
- def test_no_skip_summary_if_failure(self, testdir):
- testdir.makepyfile("""
- import pytest
- def test_ok():
- pass
- def test_fail():
- assert 0
- def test_skip():
- pytest.skip("dontshow")
- """)
- result = testdir.runpytest()
- assert result.stdout.str().find("skip test summary") == -1
- assert result.ret == 1
-
- def test_passes(self, testdir):
- p1 = testdir.makepyfile("""
- def test_passes():
- pass
- class TestClass:
- def test_method(self):
- pass
- """)
- old = p1.dirpath().chdir()
- try:
- result = testdir.runpytest()
- finally:
- old.chdir()
- result.stdout.fnmatch_lines([
- "test_passes.py ..",
- "* 2 pass*",
- ])
- assert result.ret == 0
-
- def test_header_trailer_info(self, testdir):
- testdir.makepyfile("""
- def test_passes():
- pass
- """)
- result = testdir.runpytest()
- verinfo = ".".join(map(str, py.std.sys.version_info[:3]))
- result.stdout.fnmatch_lines([
- "*===== test session starts ====*",
- "platform %s -- Python %s*pytest-%s*py-%s*pluggy-%s" % (
- py.std.sys.platform, verinfo,
- pytest.__version__, py.__version__, pluggy.__version__),
- "*test_header_trailer_info.py .",
- "=* 1 passed*in *.[0-9][0-9] seconds *=",
- ])
- if pytest.config.pluginmanager.list_plugin_distinfo():
- result.stdout.fnmatch_lines([
- "plugins: *",
- ])
-
- def test_showlocals(self, testdir):
- p1 = testdir.makepyfile("""
- def test_showlocals():
- x = 3
- y = "x" * 5000
- assert 0
- """)
- result = testdir.runpytest(p1, '-l')
- result.stdout.fnmatch_lines([
- #"_ _ * Locals *",
- "x* = 3",
- "y* = 'xxxxxx*"
- ])
-
- def test_verbose_reporting(self, testdir, pytestconfig):
- p1 = testdir.makepyfile("""
- import pytest
- def test_fail():
- raise ValueError()
- def test_pass():
- pass
- class TestClass:
- def test_skip(self):
- pytest.skip("hello")
- def test_gen():
- def check(x):
- assert x == 1
- yield check, 0
- """)
- result = testdir.runpytest(p1, '-v')
- result.stdout.fnmatch_lines([
- "*test_verbose_reporting.py::test_fail *FAIL*",
- "*test_verbose_reporting.py::test_pass *PASS*",
- "*test_verbose_reporting.py::TestClass::test_skip *SKIP*",
- "*test_verbose_reporting.py::test_gen*0* *FAIL*",
- ])
- assert result.ret == 1
-
- if not pytestconfig.pluginmanager.get_plugin("xdist"):
- pytest.skip("xdist plugin not installed")
-
- result = testdir.runpytest(p1, '-v', '-n 1')
- result.stdout.fnmatch_lines([
- "*FAIL*test_verbose_reporting.py::test_fail*",
- ])
- assert result.ret == 1
-
- def test_quiet_reporting(self, testdir):
- p1 = testdir.makepyfile("def test_pass(): pass")
- result = testdir.runpytest(p1, '-q')
- s = result.stdout.str()
- assert 'test session starts' not in s
- assert p1.basename not in s
- assert "===" not in s
- assert "passed" in s
-
- def test_more_quiet_reporting(self, testdir):
- p1 = testdir.makepyfile("def test_pass(): pass")
- result = testdir.runpytest(p1, '-qq')
- s = result.stdout.str()
- assert 'test session starts' not in s
- assert p1.basename not in s
- assert "===" not in s
- assert "passed" not in s
-
-
-def test_fail_extra_reporting(testdir):
- testdir.makepyfile("def test_this(): assert 0")
- result = testdir.runpytest()
- assert 'short test summary' not in result.stdout.str()
- result = testdir.runpytest('-rf')
- result.stdout.fnmatch_lines([
- "*test summary*",
- "FAIL*test_fail_extra_reporting*",
- ])
-
-def test_fail_reporting_on_pass(testdir):
- testdir.makepyfile("def test_this(): assert 1")
- result = testdir.runpytest('-rf')
- assert 'short test summary' not in result.stdout.str()
-
-def test_pass_extra_reporting(testdir):
- testdir.makepyfile("def test_this(): assert 1")
- result = testdir.runpytest()
- assert 'short test summary' not in result.stdout.str()
- result = testdir.runpytest('-rp')
- result.stdout.fnmatch_lines([
- "*test summary*",
- "PASS*test_pass_extra_reporting*",
- ])
-
-def test_pass_reporting_on_fail(testdir):
- testdir.makepyfile("def test_this(): assert 0")
- result = testdir.runpytest('-rp')
- assert 'short test summary' not in result.stdout.str()
-
-def test_pass_output_reporting(testdir):
- testdir.makepyfile("""
- def test_pass_output():
- print("Four score and seven years ago...")
- """)
- result = testdir.runpytest()
- assert 'Four score and seven years ago...' not in result.stdout.str()
- result = testdir.runpytest('-rP')
- result.stdout.fnmatch_lines([
- "Four score and seven years ago...",
- ])
-
-def test_color_yes(testdir):
- testdir.makepyfile("def test_this(): assert 1")
- result = testdir.runpytest('--color=yes')
- assert 'test session starts' in result.stdout.str()
- assert '\x1b[1m' in result.stdout.str()
-
-
-def test_color_no(testdir):
- testdir.makepyfile("def test_this(): assert 1")
- result = testdir.runpytest('--color=no')
- assert 'test session starts' in result.stdout.str()
- assert '\x1b[1m' not in result.stdout.str()
-
-
-@pytest.mark.parametrize('verbose', [True, False])
-def test_color_yes_collection_on_non_atty(testdir, verbose):
- """skip collect progress report when working on non-terminals.
- #1397
- """
- testdir.makepyfile("""
- import pytest
- @pytest.mark.parametrize('i', range(10))
- def test_this(i):
- assert 1
- """)
- args = ['--color=yes']
- if verbose:
- args.append('-vv')
- result = testdir.runpytest(*args)
- assert 'test session starts' in result.stdout.str()
- assert '\x1b[1m' in result.stdout.str()
- assert 'collecting 10 items' not in result.stdout.str()
- if verbose:
- assert 'collecting ...' in result.stdout.str()
- assert 'collected 10 items' in result.stdout.str()
-
-
-def test_getreportopt():
- class config:
- class option:
- reportchars = ""
- config.option.report = "xfailed"
- assert getreportopt(config) == "x"
-
- config.option.report = "xfailed,skipped"
- assert getreportopt(config) == "xs"
-
- config.option.report = "skipped,xfailed"
- assert getreportopt(config) == "sx"
-
- config.option.report = "skipped"
- config.option.reportchars = "sf"
- assert getreportopt(config) == "sf"
-
- config.option.reportchars = "sfx"
- assert getreportopt(config) == "sfx"
-
-def test_terminalreporter_reportopt_addopts(testdir):
- testdir.makeini("[pytest]\naddopts=-rs")
- testdir.makepyfile("""
- def pytest_funcarg__tr(request):
- tr = request.config.pluginmanager.getplugin("terminalreporter")
- return tr
- def test_opt(tr):
- assert tr.hasopt('skipped')
- assert not tr.hasopt('qwe')
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*1 passed*"
- ])
-
-def test_tbstyle_short(testdir):
- p = testdir.makepyfile("""
- def pytest_funcarg__arg(request):
- return 42
- def test_opt(arg):
- x = 0
- assert x
- """)
- result = testdir.runpytest("--tb=short")
- s = result.stdout.str()
- assert 'arg = 42' not in s
- assert 'x = 0' not in s
- result.stdout.fnmatch_lines([
- "*%s:5*" % p.basename,
- " assert x",
- "E assert*",
- ])
- result = testdir.runpytest()
- s = result.stdout.str()
- assert 'x = 0' in s
- assert 'assert x' in s
-
-def test_traceconfig(testdir, monkeypatch):
- result = testdir.runpytest("--traceconfig")
- result.stdout.fnmatch_lines([
- "*active plugins*"
- ])
- assert result.ret == EXIT_NOTESTSCOLLECTED
-
-
-class TestGenericReporting:
- """ this test class can be subclassed with a different option
- provider to run e.g. distributed tests.
- """
- def test_collect_fail(self, testdir, option):
- testdir.makepyfile("import xyz\n")
- result = testdir.runpytest(*option.args)
- result.stdout.fnmatch_lines([
- "? import xyz",
- "E ImportError: No module named *xyz*",
- "*1 error*",
- ])
-
- def test_maxfailures(self, testdir, option):
- testdir.makepyfile("""
- def test_1():
- assert 0
- def test_2():
- assert 0
- def test_3():
- assert 0
- """)
- result = testdir.runpytest("--maxfail=2", *option.args)
- result.stdout.fnmatch_lines([
- "*def test_1():*",
- "*def test_2():*",
- "*!! Interrupted: stopping after 2 failures*!!*",
- "*2 failed*",
- ])
-
-
- def test_tb_option(self, testdir, option):
- testdir.makepyfile("""
- import pytest
- def g():
- raise IndexError
- def test_func():
- print (6*7)
- g() # --calling--
- """)
- for tbopt in ["long", "short", "no"]:
- print('testing --tb=%s...' % tbopt)
- result = testdir.runpytest('--tb=%s' % tbopt)
- s = result.stdout.str()
- if tbopt == "long":
- assert 'print (6*7)' in s
- else:
- assert 'print (6*7)' not in s
- if tbopt != "no":
- assert '--calling--' in s
- assert 'IndexError' in s
- else:
- assert 'FAILURES' not in s
- assert '--calling--' not in s
- assert 'IndexError' not in s
-
- def test_tb_crashline(self, testdir, option):
- p = testdir.makepyfile("""
- import pytest
- def g():
- raise IndexError
- def test_func1():
- print (6*7)
- g() # --calling--
- def test_func2():
- assert 0, "hello"
- """)
- result = testdir.runpytest("--tb=line")
- bn = p.basename
- result.stdout.fnmatch_lines([
- "*%s:3: IndexError*" % bn,
- "*%s:8: AssertionError: hello*" % bn,
- ])
- s = result.stdout.str()
- assert "def test_func2" not in s
-
- def test_pytest_report_header(self, testdir, option):
- testdir.makeconftest("""
- def pytest_sessionstart(session):
- session.config._somevalue = 42
- def pytest_report_header(config):
- return "hello: %s" % config._somevalue
- """)
- testdir.mkdir("a").join("conftest.py").write("""
-def pytest_report_header(config, startdir):
- return ["line1", str(startdir)]
-""")
- result = testdir.runpytest("a")
- result.stdout.fnmatch_lines([
- "*hello: 42*",
- "line1",
- str(testdir.tmpdir),
- ])
-
-@pytest.mark.xfail("not hasattr(os, 'dup')")
-def test_fdopen_kept_alive_issue124(testdir):
- testdir.makepyfile("""
- import os, sys
- k = []
- def test_open_file_and_keep_alive(capfd):
- stdout = os.fdopen(1, 'w', 1)
- k.append(stdout)
-
- def test_close_kept_alive_file():
- stdout = k.pop()
- stdout.close()
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*2 passed*"
- ])
-
-def test_tbstyle_native_setup_error(testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture
- def setup_error_fixture():
- raise Exception("error in exception")
-
- def test_error_fixture(setup_error_fixture):
- pass
- """)
- result = testdir.runpytest("--tb=native")
- result.stdout.fnmatch_lines([
- '*File *test_tbstyle_native_setup_error.py", line *, in setup_error_fixture*'
- ])
-
-def test_terminal_summary(testdir):
- testdir.makeconftest("""
- def pytest_terminal_summary(terminalreporter):
- w = terminalreporter
- w.section("hello")
- w.line("world")
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines("""
- *==== hello ====*
- world
- """)
-
-
-def test_terminal_summary_warnings_are_displayed(testdir):
- """Test that warnings emitted during pytest_terminal_summary are displayed.
- (#1305).
- """
- testdir.makeconftest("""
- def pytest_terminal_summary(terminalreporter):
- config = terminalreporter.config
- config.warn('C1', 'internal warning')
- """)
- result = testdir.runpytest('-rw')
- result.stdout.fnmatch_lines([
- '*C1*internal warning',
- '*== 1 pytest-warnings in *',
- ])
-
-
-@pytest.mark.parametrize("exp_color, exp_line, stats_arg", [
- # The method under test only cares about the length of each
- # dict value, not the actual contents, so tuples of anything
- # suffice
-
- # Important statuses -- the highest priority of these always wins
- ("red", "1 failed", {"failed": (1,)}),
- ("red", "1 failed, 1 passed", {"failed": (1,), "passed": (1,)}),
-
- ("red", "1 error", {"error": (1,)}),
- ("red", "1 passed, 1 error", {"error": (1,), "passed": (1,)}),
-
- # (a status that's not known to the code)
- ("yellow", "1 weird", {"weird": (1,)}),
- ("yellow", "1 passed, 1 weird", {"weird": (1,), "passed": (1,)}),
-
- ("yellow", "1 pytest-warnings", {"warnings": (1,)}),
- ("yellow", "1 passed, 1 pytest-warnings", {"warnings": (1,),
- "passed": (1,)}),
-
- ("green", "5 passed", {"passed": (1,2,3,4,5)}),
-
-
- # "Boring" statuses. These have no effect on the color of the summary
- # line. Thus, if *every* test has a boring status, the summary line stays
- # at its default color, i.e. yellow, to warn the user that the test run
- # produced no useful information
- ("yellow", "1 skipped", {"skipped": (1,)}),
- ("green", "1 passed, 1 skipped", {"skipped": (1,), "passed": (1,)}),
-
- ("yellow", "1 deselected", {"deselected": (1,)}),
- ("green", "1 passed, 1 deselected", {"deselected": (1,), "passed": (1,)}),
-
- ("yellow", "1 xfailed", {"xfailed": (1,)}),
- ("green", "1 passed, 1 xfailed", {"xfailed": (1,), "passed": (1,)}),
-
- ("yellow", "1 xpassed", {"xpassed": (1,)}),
- ("green", "1 passed, 1 xpassed", {"xpassed": (1,), "passed": (1,)}),
-
- # Likewise if no tests were found at all
- ("yellow", "no tests ran", {}),
-
- # Test the empty-key special case
- ("yellow", "no tests ran", {"": (1,)}),
- ("green", "1 passed", {"": (1,), "passed": (1,)}),
-
-
- # A couple more complex combinations
- ("red", "1 failed, 2 passed, 3 xfailed",
- {"passed": (1,2), "failed": (1,), "xfailed": (1,2,3)}),
-
- ("green", "1 passed, 2 skipped, 3 deselected, 2 xfailed",
- {"passed": (1,),
- "skipped": (1,2),
- "deselected": (1,2,3),
- "xfailed": (1,2)}),
-])
-def test_summary_stats(exp_line, exp_color, stats_arg):
- print("Based on stats: %s" % stats_arg)
- print("Expect summary: \"%s\"; with color \"%s\"" % (exp_line, exp_color))
- (line, color) = build_summary_stats_line(stats_arg)
- print("Actually got: \"%s\"; with color \"%s\"" % (line, color))
- assert line == exp_line
- assert color == exp_color
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_tmpdir.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_tmpdir.py
deleted file mode 100644
index d514e722e1f..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_tmpdir.py
+++ /dev/null
@@ -1,183 +0,0 @@
-import sys
-import py
-import pytest
-
-from _pytest.tmpdir import tmpdir
-
-def test_funcarg(testdir):
- testdir.makepyfile("""
- def pytest_generate_tests(metafunc):
- metafunc.addcall(id='a')
- metafunc.addcall(id='b')
- def test_func(tmpdir): pass
- """)
- from _pytest.tmpdir import TempdirFactory
- reprec = testdir.inline_run()
- calls = reprec.getcalls("pytest_runtest_setup")
- item = calls[0].item
- config = item.config
- tmpdirhandler = TempdirFactory(config)
- item._initrequest()
- p = tmpdir(item._request, tmpdirhandler)
- assert p.check()
- bn = p.basename.strip("0123456789")
- assert bn.endswith("test_func_a_")
- item.name = "qwe/\\abc"
- p = tmpdir(item._request, tmpdirhandler)
- assert p.check()
- bn = p.basename.strip("0123456789")
- assert bn == "qwe__abc"
-
-def test_ensuretemp(recwarn):
- #pytest.deprecated_call(pytest.ensuretemp, 'hello')
- d1 = pytest.ensuretemp('hello')
- d2 = pytest.ensuretemp('hello')
- assert d1 == d2
- assert d1.check(dir=1)
-
-class TestTempdirHandler:
- def test_mktemp(self, testdir):
- from _pytest.tmpdir import TempdirFactory
- config = testdir.parseconfig()
- config.option.basetemp = testdir.mkdir("hello")
- t = TempdirFactory(config)
- tmp = t.mktemp("world")
- assert tmp.relto(t.getbasetemp()) == "world0"
- tmp = t.mktemp("this")
- assert tmp.relto(t.getbasetemp()).startswith("this")
- tmp2 = t.mktemp("this")
- assert tmp2.relto(t.getbasetemp()).startswith("this")
- assert tmp2 != tmp
-
-class TestConfigTmpdir:
- def test_getbasetemp_custom_removes_old(self, testdir):
- mytemp = testdir.tmpdir.join("xyz")
- p = testdir.makepyfile("""
- def test_1(tmpdir):
- pass
- """)
- testdir.runpytest(p, '--basetemp=%s' % mytemp)
- mytemp.check()
- mytemp.ensure("hello")
-
- testdir.runpytest(p, '--basetemp=%s' % mytemp)
- mytemp.check()
- assert not mytemp.join("hello").check()
-
-
-def test_basetemp(testdir):
- mytemp = testdir.tmpdir.mkdir("mytemp")
- p = testdir.makepyfile("""
- import pytest
- def test_1():
- pytest.ensuretemp("hello")
- """)
- result = testdir.runpytest(p, '--basetemp=%s' % mytemp)
- assert result.ret == 0
- assert mytemp.join('hello').check()
-
-@pytest.mark.skipif(not hasattr(py.path.local, 'mksymlinkto'),
- reason="symlink not available on this platform")
-def test_tmpdir_always_is_realpath(testdir):
- # the reason why tmpdir should be a realpath is that
- # when you cd to it and do "os.getcwd()" you will anyway
- # get the realpath. Using the symlinked path can thus
- # easily result in path-inequality
- # XXX if that proves to be a problem, consider using
- # os.environ["PWD"]
- realtemp = testdir.tmpdir.mkdir("myrealtemp")
- linktemp = testdir.tmpdir.join("symlinktemp")
- linktemp.mksymlinkto(realtemp)
- p = testdir.makepyfile("""
- def test_1(tmpdir):
- import os
- assert os.path.realpath(str(tmpdir)) == str(tmpdir)
- """)
- result = testdir.runpytest("-s", p, '--basetemp=%s/bt' % linktemp)
- assert not result.ret
-
-
-def test_tmpdir_too_long_on_parametrization(testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.mark.parametrize("arg", ["1"*1000])
- def test_some(arg, tmpdir):
- tmpdir.ensure("hello")
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
-
-def test_tmpdir_factory(testdir):
- testdir.makepyfile("""
- import pytest
- @pytest.fixture(scope='session')
- def session_dir(tmpdir_factory):
- return tmpdir_factory.mktemp('data', numbered=False)
- def test_some(session_dir):
- session_dir.isdir()
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
-
-def test_tmpdir_fallback_tox_env(testdir, monkeypatch):
- """Test that tmpdir works even if environment variables required by getpass
- module are missing (#1010).
- """
- monkeypatch.delenv('USER', raising=False)
- monkeypatch.delenv('USERNAME', raising=False)
- testdir.makepyfile("""
- import pytest
- def test_some(tmpdir):
- assert tmpdir.isdir()
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
-
-@pytest.fixture
-def break_getuser(monkeypatch):
- monkeypatch.setattr('os.getuid', lambda: -1)
- # taken from python 2.7/3.4
- for envvar in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
- monkeypatch.delenv(envvar, raising=False)
-
-
-@pytest.mark.usefixtures("break_getuser")
-@pytest.mark.skipif(sys.platform.startswith('win'), reason='no os.getuid on windows')
-def test_tmpdir_fallback_uid_not_found(testdir):
- """Test that tmpdir works even if the current process's user id does not
- correspond to a valid user.
- """
-
- testdir.makepyfile("""
- import pytest
- def test_some(tmpdir):
- assert tmpdir.isdir()
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
-
-@pytest.mark.usefixtures("break_getuser")
-@pytest.mark.skipif(sys.platform.startswith('win'), reason='no os.getuid on windows')
-def test_get_user_uid_not_found():
- """Test that get_user() function works even if the current process's
- user id does not correspond to a valid user (e.g. running pytest in a
- Docker container with 'docker run -u'.
- """
- from _pytest.tmpdir import get_user
- assert get_user() is None
-
-
-@pytest.mark.skipif(not sys.platform.startswith('win'), reason='win only')
-def test_get_user(monkeypatch):
- """Test that get_user() function works even if environment variables
- required by getpass module are missing from the environment on Windows
- (#1010).
- """
- from _pytest.tmpdir import get_user
- monkeypatch.delenv('USER', raising=False)
- monkeypatch.delenv('USERNAME', raising=False)
- assert get_user() is None
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/test_unittest.py b/tests/wpt/web-platform-tests/tools/pytest/testing/test_unittest.py
deleted file mode 100644
index 144aad79bf4..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/test_unittest.py
+++ /dev/null
@@ -1,737 +0,0 @@
-from _pytest.main import EXIT_NOTESTSCOLLECTED
-import pytest
-
-def test_simple_unittest(testdir):
- testpath = testdir.makepyfile("""
- import unittest
- class MyTestCase(unittest.TestCase):
- def testpassing(self):
- self.assertEquals('foo', 'foo')
- def test_failing(self):
- self.assertEquals('foo', 'bar')
- """)
- reprec = testdir.inline_run(testpath)
- assert reprec.matchreport("testpassing").passed
- assert reprec.matchreport("test_failing").failed
-
-def test_runTest_method(testdir):
- testdir.makepyfile("""
- import unittest
- class MyTestCaseWithRunTest(unittest.TestCase):
- def runTest(self):
- self.assertEquals('foo', 'foo')
- class MyTestCaseWithoutRunTest(unittest.TestCase):
- def runTest(self):
- self.assertEquals('foo', 'foo')
- def test_something(self):
- pass
- """)
- result = testdir.runpytest("-v")
- result.stdout.fnmatch_lines("""
- *MyTestCaseWithRunTest::runTest*
- *MyTestCaseWithoutRunTest::test_something*
- *2 passed*
- """)
-
-def test_isclasscheck_issue53(testdir):
- testpath = testdir.makepyfile("""
- import unittest
- class _E(object):
- def __getattr__(self, tag):
- pass
- E = _E()
- """)
- result = testdir.runpytest(testpath)
- assert result.ret == EXIT_NOTESTSCOLLECTED
-
-def test_setup(testdir):
- testpath = testdir.makepyfile("""
- import unittest
- class MyTestCase(unittest.TestCase):
- def setUp(self):
- self.foo = 1
- def setup_method(self, method):
- self.foo2 = 1
- def test_both(self):
- self.assertEquals(1, self.foo)
- assert self.foo2 == 1
- def teardown_method(self, method):
- assert 0, "42"
-
- """)
- reprec = testdir.inline_run("-s", testpath)
- assert reprec.matchreport("test_both", when="call").passed
- rep = reprec.matchreport("test_both", when="teardown")
- assert rep.failed and '42' in str(rep.longrepr)
-
-def test_setUpModule(testdir):
- testpath = testdir.makepyfile("""
- l = []
-
- def setUpModule():
- l.append(1)
-
- def tearDownModule():
- del l[0]
-
- def test_hello():
- assert l == [1]
-
- def test_world():
- assert l == [1]
- """)
- result = testdir.runpytest(testpath)
- result.stdout.fnmatch_lines([
- "*2 passed*",
- ])
-
-def test_setUpModule_failing_no_teardown(testdir):
- testpath = testdir.makepyfile("""
- l = []
-
- def setUpModule():
- 0/0
-
- def tearDownModule():
- l.append(1)
-
- def test_hello():
- pass
- """)
- reprec = testdir.inline_run(testpath)
- reprec.assertoutcome(passed=0, failed=1)
- call = reprec.getcalls("pytest_runtest_setup")[0]
- assert not call.item.module.l
-
-def test_new_instances(testdir):
- testpath = testdir.makepyfile("""
- import unittest
- class MyTestCase(unittest.TestCase):
- def test_func1(self):
- self.x = 2
- def test_func2(self):
- assert not hasattr(self, 'x')
- """)
- reprec = testdir.inline_run(testpath)
- reprec.assertoutcome(passed=2)
-
-def test_teardown(testdir):
- testpath = testdir.makepyfile("""
- import unittest
- class MyTestCase(unittest.TestCase):
- l = []
- def test_one(self):
- pass
- def tearDown(self):
- self.l.append(None)
- class Second(unittest.TestCase):
- def test_check(self):
- self.assertEquals(MyTestCase.l, [None])
- """)
- reprec = testdir.inline_run(testpath)
- passed, skipped, failed = reprec.countoutcomes()
- assert failed == 0, failed
- assert passed == 2
- assert passed + skipped + failed == 2
-
-@pytest.mark.skipif("sys.version_info < (2,7)")
-def test_unittest_skip_issue148(testdir):
- testpath = testdir.makepyfile("""
- import unittest
-
- @unittest.skip("hello")
- class MyTestCase(unittest.TestCase):
- @classmethod
- def setUpClass(self):
- xxx
- def test_one(self):
- pass
- @classmethod
- def tearDownClass(self):
- xxx
- """)
- reprec = testdir.inline_run(testpath)
- reprec.assertoutcome(skipped=1)
-
-def test_method_and_teardown_failing_reporting(testdir):
- testdir.makepyfile("""
- import unittest, pytest
- class TC(unittest.TestCase):
- def tearDown(self):
- assert 0, "down1"
- def test_method(self):
- assert False, "down2"
- """)
- result = testdir.runpytest("-s")
- assert result.ret == 1
- result.stdout.fnmatch_lines([
- "*tearDown*",
- "*assert 0*",
- "*test_method*",
- "*assert False*",
- "*1 failed*1 error*",
- ])
-
-def test_setup_failure_is_shown(testdir):
- testdir.makepyfile("""
- import unittest
- import pytest
- class TC(unittest.TestCase):
- def setUp(self):
- assert 0, "down1"
- def test_method(self):
- print ("never42")
- xyz
- """)
- result = testdir.runpytest("-s")
- assert result.ret == 1
- result.stdout.fnmatch_lines([
- "*setUp*",
- "*assert 0*down1*",
- "*1 failed*",
- ])
- assert 'never42' not in result.stdout.str()
-
-def test_setup_setUpClass(testdir):
- testpath = testdir.makepyfile("""
- import unittest
- import pytest
- class MyTestCase(unittest.TestCase):
- x = 0
- @classmethod
- def setUpClass(cls):
- cls.x += 1
- def test_func1(self):
- assert self.x == 1
- def test_func2(self):
- assert self.x == 1
- @classmethod
- def tearDownClass(cls):
- cls.x -= 1
- def test_teareddown():
- assert MyTestCase.x == 0
- """)
- reprec = testdir.inline_run(testpath)
- reprec.assertoutcome(passed=3)
-
-def test_setup_class(testdir):
- testpath = testdir.makepyfile("""
- import unittest
- import pytest
- class MyTestCase(unittest.TestCase):
- x = 0
- def setup_class(cls):
- cls.x += 1
- def test_func1(self):
- assert self.x == 1
- def test_func2(self):
- assert self.x == 1
- def teardown_class(cls):
- cls.x -= 1
- def test_teareddown():
- assert MyTestCase.x == 0
- """)
- reprec = testdir.inline_run(testpath)
- reprec.assertoutcome(passed=3)
-
-
-@pytest.mark.parametrize("type", ['Error', 'Failure'])
-def test_testcase_adderrorandfailure_defers(testdir, type):
- testdir.makepyfile("""
- from unittest import TestCase
- import pytest
- class MyTestCase(TestCase):
- def run(self, result):
- excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0)
- try:
- result.add%s(self, excinfo._excinfo)
- except KeyboardInterrupt:
- raise
- except:
- pytest.fail("add%s should not raise")
- def test_hello(self):
- pass
- """ % (type, type))
- result = testdir.runpytest()
- assert 'should not raise' not in result.stdout.str()
-
-@pytest.mark.parametrize("type", ['Error', 'Failure'])
-def test_testcase_custom_exception_info(testdir, type):
- testdir.makepyfile("""
- from unittest import TestCase
- import py, pytest
- import _pytest._code
- class MyTestCase(TestCase):
- def run(self, result):
- excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0)
- # we fake an incompatible exception info
- from _pytest.monkeypatch import monkeypatch
- mp = monkeypatch()
- def t(*args):
- mp.undo()
- raise TypeError()
- mp.setattr(_pytest._code, 'ExceptionInfo', t)
- try:
- excinfo = excinfo._excinfo
- result.add%(type)s(self, excinfo)
- finally:
- mp.undo()
- def test_hello(self):
- pass
- """ % locals())
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "NOTE: Incompatible Exception Representation*",
- "*ZeroDivisionError*",
- "*1 failed*",
- ])
-
-def test_testcase_totally_incompatible_exception_info(testdir):
- item, = testdir.getitems("""
- from unittest import TestCase
- class MyTestCase(TestCase):
- def test_hello(self):
- pass
- """)
- item.addError(None, 42)
- excinfo = item._excinfo.pop(0)
- assert 'ERROR: Unknown Incompatible' in str(excinfo.getrepr())
-
-def test_module_level_pytestmark(testdir):
- testpath = testdir.makepyfile("""
- import unittest
- import pytest
- pytestmark = pytest.mark.xfail
- class MyTestCase(unittest.TestCase):
- def test_func1(self):
- assert 0
- """)
- reprec = testdir.inline_run(testpath, "-s")
- reprec.assertoutcome(skipped=1)
-
-
-def test_trial_testcase_skip_property(testdir):
- pytest.importorskip('twisted.trial.unittest')
- testpath = testdir.makepyfile("""
- from twisted.trial import unittest
- class MyTestCase(unittest.TestCase):
- skip = 'dont run'
- def test_func(self):
- pass
- """)
- reprec = testdir.inline_run(testpath, "-s")
- reprec.assertoutcome(skipped=1)
-
-
-def test_trial_testfunction_skip_property(testdir):
- pytest.importorskip('twisted.trial.unittest')
- testpath = testdir.makepyfile("""
- from twisted.trial import unittest
- class MyTestCase(unittest.TestCase):
- def test_func(self):
- pass
- test_func.skip = 'dont run'
- """)
- reprec = testdir.inline_run(testpath, "-s")
- reprec.assertoutcome(skipped=1)
-
-
-def test_trial_testcase_todo_property(testdir):
- pytest.importorskip('twisted.trial.unittest')
- testpath = testdir.makepyfile("""
- from twisted.trial import unittest
- class MyTestCase(unittest.TestCase):
- todo = 'dont run'
- def test_func(self):
- assert 0
- """)
- reprec = testdir.inline_run(testpath, "-s")
- reprec.assertoutcome(skipped=1)
-
-
-def test_trial_testfunction_todo_property(testdir):
- pytest.importorskip('twisted.trial.unittest')
- testpath = testdir.makepyfile("""
- from twisted.trial import unittest
- class MyTestCase(unittest.TestCase):
- def test_func(self):
- assert 0
- test_func.todo = 'dont run'
- """)
- reprec = testdir.inline_run(testpath, "-s")
- reprec.assertoutcome(skipped=1)
-
-
-class TestTrialUnittest:
- def setup_class(cls):
- cls.ut = pytest.importorskip("twisted.trial.unittest")
-
- def test_trial_testcase_runtest_not_collected(self, testdir):
- testdir.makepyfile("""
- from twisted.trial.unittest import TestCase
-
- class TC(TestCase):
- def test_hello(self):
- pass
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
- testdir.makepyfile("""
- from twisted.trial.unittest import TestCase
-
- class TC(TestCase):
- def runTest(self):
- pass
- """)
- reprec = testdir.inline_run()
- reprec.assertoutcome(passed=1)
-
- def test_trial_exceptions_with_skips(self, testdir):
- testdir.makepyfile("""
- from twisted.trial import unittest
- import pytest
- class TC(unittest.TestCase):
- def test_hello(self):
- pytest.skip("skip_in_method")
- @pytest.mark.skipif("sys.version_info != 1")
- def test_hello2(self):
- pass
- @pytest.mark.xfail(reason="iwanto")
- def test_hello3(self):
- assert 0
- def test_hello4(self):
- pytest.xfail("i2wanto")
- def test_trial_skip(self):
- pass
- test_trial_skip.skip = "trialselfskip"
-
- def test_trial_todo(self):
- assert 0
- test_trial_todo.todo = "mytodo"
-
- def test_trial_todo_success(self):
- pass
- test_trial_todo_success.todo = "mytodo"
-
- class TC2(unittest.TestCase):
- def setup_class(cls):
- pytest.skip("skip_in_setup_class")
- def test_method(self):
- pass
- """)
- result = testdir.runpytest("-rxs")
- assert result.ret == 0
- result.stdout.fnmatch_lines_random([
- "*XFAIL*test_trial_todo*",
- "*trialselfskip*",
- "*skip_in_setup_class*",
- "*iwanto*",
- "*i2wanto*",
- "*sys.version_info*",
- "*skip_in_method*",
- "*4 skipped*3 xfail*1 xpass*",
- ])
-
- def test_trial_error(self, testdir):
- testdir.makepyfile("""
- from twisted.trial.unittest import TestCase
- from twisted.internet.defer import Deferred
- from twisted.internet import reactor
-
- class TC(TestCase):
- def test_one(self):
- crash
-
- def test_two(self):
- def f(_):
- crash
-
- d = Deferred()
- d.addCallback(f)
- reactor.callLater(0.3, d.callback, None)
- return d
-
- def test_three(self):
- def f():
- pass # will never get called
- reactor.callLater(0.3, f)
- # will crash at teardown
-
- def test_four(self):
- def f(_):
- reactor.callLater(0.3, f)
- crash
-
- d = Deferred()
- d.addCallback(f)
- reactor.callLater(0.3, d.callback, None)
- return d
- # will crash both at test time and at teardown
- """)
- result = testdir.runpytest()
- result.stdout.fnmatch_lines([
- "*ERRORS*",
- "*DelayedCalls*",
- "*test_four*",
- "*NameError*crash*",
- "*test_one*",
- "*NameError*crash*",
- "*test_three*",
- "*DelayedCalls*",
- "*test_two*",
- "*crash*",
- ])
-
- def test_trial_pdb(self, testdir):
- p = testdir.makepyfile("""
- from twisted.trial import unittest
- import pytest
- class TC(unittest.TestCase):
- def test_hello(self):
- assert 0, "hellopdb"
- """)
- child = testdir.spawn_pytest(p)
- child.expect("hellopdb")
- child.sendeof()
-
-def test_djangolike_testcase(testdir):
- # contributed from Morten Breekevold
- testdir.makepyfile("""
- from unittest import TestCase, main
-
- class DjangoLikeTestCase(TestCase):
-
- def setUp(self):
- print ("setUp()")
-
- def test_presetup_has_been_run(self):
- print ("test_thing()")
- self.assertTrue(hasattr(self, 'was_presetup'))
-
- def tearDown(self):
- print ("tearDown()")
-
- def __call__(self, result=None):
- try:
- self._pre_setup()
- except (KeyboardInterrupt, SystemExit):
- raise
- except Exception:
- import sys
- result.addError(self, sys.exc_info())
- return
- super(DjangoLikeTestCase, self).__call__(result)
- try:
- self._post_teardown()
- except (KeyboardInterrupt, SystemExit):
- raise
- except Exception:
- import sys
- result.addError(self, sys.exc_info())
- return
-
- def _pre_setup(self):
- print ("_pre_setup()")
- self.was_presetup = True
-
- def _post_teardown(self):
- print ("_post_teardown()")
- """)
- result = testdir.runpytest("-s")
- assert result.ret == 0
- result.stdout.fnmatch_lines([
- "*_pre_setup()*",
- "*setUp()*",
- "*test_thing()*",
- "*tearDown()*",
- "*_post_teardown()*",
- ])
-
-
-def test_unittest_not_shown_in_traceback(testdir):
- testdir.makepyfile("""
- import unittest
- class t(unittest.TestCase):
- def test_hello(self):
- x = 3
- self.assertEquals(x, 4)
- """)
- res = testdir.runpytest()
- assert "failUnlessEqual" not in res.stdout.str()
-
-def test_unorderable_types(testdir):
- testdir.makepyfile("""
- import unittest
- class TestJoinEmpty(unittest.TestCase):
- pass
-
- def make_test():
- class Test(unittest.TestCase):
- pass
- Test.__name__ = "TestFoo"
- return Test
- TestFoo = make_test()
- """)
- result = testdir.runpytest()
- assert "TypeError" not in result.stdout.str()
- assert result.ret == EXIT_NOTESTSCOLLECTED
-
-def test_unittest_typerror_traceback(testdir):
- testdir.makepyfile("""
- import unittest
- class TestJoinEmpty(unittest.TestCase):
- def test_hello(self, arg1):
- pass
- """)
- result = testdir.runpytest()
- assert "TypeError" in result.stdout.str()
- assert result.ret == 1
-
-@pytest.mark.skipif("sys.version_info < (2,7)")
-def test_unittest_unexpected_failure(testdir):
- testdir.makepyfile("""
- import unittest
- class MyTestCase(unittest.TestCase):
- @unittest.expectedFailure
- def test_func1(self):
- assert 0
- @unittest.expectedFailure
- def test_func2(self):
- assert 1
- """)
- result = testdir.runpytest("-rxX")
- result.stdout.fnmatch_lines([
- "*XFAIL*MyTestCase*test_func1*",
- "*XPASS*MyTestCase*test_func2*",
- "*1 xfailed*1 xpass*",
- ])
-
-
-@pytest.mark.parametrize('fix_type, stmt', [
- ('fixture', 'return'),
- ('yield_fixture', 'yield'),
-])
-def test_unittest_setup_interaction(testdir, fix_type, stmt):
- testdir.makepyfile("""
- import unittest
- import pytest
- class MyTestCase(unittest.TestCase):
- @pytest.{fix_type}(scope="class", autouse=True)
- def perclass(self, request):
- request.cls.hello = "world"
- {stmt}
- @pytest.{fix_type}(scope="function", autouse=True)
- def perfunction(self, request):
- request.instance.funcname = request.function.__name__
- {stmt}
-
- def test_method1(self):
- assert self.funcname == "test_method1"
- assert self.hello == "world"
-
- def test_method2(self):
- assert self.funcname == "test_method2"
-
- def test_classattr(self):
- assert self.__class__.hello == "world"
- """.format(fix_type=fix_type, stmt=stmt))
- result = testdir.runpytest()
- result.stdout.fnmatch_lines("*3 passed*")
-
-
-def test_non_unittest_no_setupclass_support(testdir):
- testpath = testdir.makepyfile("""
- class TestFoo:
- x = 0
-
- @classmethod
- def setUpClass(cls):
- cls.x = 1
-
- def test_method1(self):
- assert self.x == 0
-
- @classmethod
- def tearDownClass(cls):
- cls.x = 1
-
- def test_not_teareddown():
- assert TestFoo.x == 0
-
- """)
- reprec = testdir.inline_run(testpath)
- reprec.assertoutcome(passed=2)
-
-
-def test_no_teardown_if_setupclass_failed(testdir):
- testpath = testdir.makepyfile("""
- import unittest
-
- class MyTestCase(unittest.TestCase):
- x = 0
-
- @classmethod
- def setUpClass(cls):
- cls.x = 1
- assert False
-
- def test_func1(self):
- cls.x = 10
-
- @classmethod
- def tearDownClass(cls):
- cls.x = 100
-
- def test_notTornDown():
- assert MyTestCase.x == 1
- """)
- reprec = testdir.inline_run(testpath)
- reprec.assertoutcome(passed=1, failed=1)
-
-
-def test_issue333_result_clearing(testdir):
- testdir.makeconftest("""
- def pytest_runtest_call(__multicall__, item):
- __multicall__.execute()
- assert 0
- """)
- testdir.makepyfile("""
- import unittest
- class TestIt(unittest.TestCase):
- def test_func(self):
- 0/0
- """)
-
- reprec = testdir.inline_run()
- reprec.assertoutcome(failed=1)
-
-@pytest.mark.skipif("sys.version_info < (2,7)")
-def test_unittest_raise_skip_issue748(testdir):
- testdir.makepyfile(test_foo="""
- import unittest
-
- class MyTestCase(unittest.TestCase):
- def test_one(self):
- raise unittest.SkipTest('skipping due to reasons')
- """)
- result = testdir.runpytest("-v", '-rs')
- result.stdout.fnmatch_lines("""
- *SKIP*[1]*test_foo.py*skipping due to reasons*
- *1 skipped*
- """)
-
-@pytest.mark.skipif("sys.version_info < (2,7)")
-def test_unittest_skip_issue1169(testdir):
- testdir.makepyfile(test_foo="""
- import unittest
-
- class MyTestCase(unittest.TestCase):
- @unittest.skip("skipping due to reasons")
- def test_skip(self):
- self.fail()
- """)
- result = testdir.runpytest("-v", '-rs')
- result.stdout.fnmatch_lines("""
- *SKIP*[1]*skipping due to reasons*
- *1 skipped*
- """)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/tox.ini b/tests/wpt/web-platform-tests/tools/pytest/tox.ini
deleted file mode 100644
index 5f65446e42b..00000000000
--- a/tests/wpt/web-platform-tests/tools/pytest/tox.ini
+++ /dev/null
@@ -1,160 +0,0 @@
-[tox]
-minversion=2.0
-distshare={homedir}/.tox/distshare
-envlist=
- linting,py26,py27,py33,py34,py35,pypy,
- {py27,py35}-{pexpect,xdist,trial},
- py27-nobyte,doctesting,py27-cxfreeze
-
-[testenv]
-commands= py.test --lsof -rfsxX {posargs:testing}
-passenv = USER USERNAME
-deps=
- nose
- mock
- requests
-
-[testenv:py26]
-commands= py.test --lsof -rfsxX {posargs:testing}
-deps=
- nose
- mock<1.1 # last supported version for py26
-
-[testenv:py27-subprocess]
-changedir=.
-basepython=python2.7
-deps=pytest-xdist>=1.13
- mock
- nose
-commands=
- py.test -n3 -rfsxX --runpytest=subprocess {posargs:testing}
-
-[testenv:genscript]
-commands= py.test --genscript=pytest1
-
-[testenv:linting]
-basepython = python2.7
-deps = flake8
- restructuredtext_lint
-commands = flake8 pytest.py _pytest testing
- rst-lint CHANGELOG.rst
-
-[testenv:py27-xdist]
-deps=pytest-xdist>=1.13
- mock
- nose
-commands=
- py.test -n1 -rfsxX {posargs:testing}
-
-[testenv:py35-xdist]
-deps={[testenv:py27-xdist]deps}
-commands=
- py.test -n3 -rfsxX {posargs:testing}
-
-[testenv:py27-pexpect]
-changedir=testing
-platform=linux|darwin
-deps=pexpect
-commands=
- py.test -rfsxX test_pdb.py test_terminal.py test_unittest.py
-
-[testenv:py35-pexpect]
-changedir=testing
-platform=linux|darwin
-deps={[testenv:py27-pexpect]deps}
-commands=
- py.test -rfsxX test_pdb.py test_terminal.py test_unittest.py
-
-[testenv:py27-nobyte]
-deps=pytest-xdist>=1.13
-distribute=true
-setenv=
- PYTHONDONTWRITEBYTECODE=1
-commands=
- py.test -n3 -rfsxX {posargs:testing}
-
-[testenv:py27-trial]
-deps=twisted
-commands=
- py.test -rsxf {posargs:testing/test_unittest.py}
-
-[testenv:py35-trial]
-platform=linux|darwin
-deps={[testenv:py27-trial]deps}
-commands=
- py.test -rsxf {posargs:testing/test_unittest.py}
-
-[testenv:doctest]
-commands=py.test --doctest-modules _pytest
-deps=
-
-[testenv:doc]
-basepython=python
-changedir=doc/en
-deps=sphinx
- PyYAML
-
-commands=
- make clean
- make html
-
-[testenv:doctesting]
-basepython = python3.4
-changedir=doc/en
-deps=PyYAML
-commands= py.test -rfsxX {posargs}
-
-[testenv:regen]
-changedir=doc/en
-basepython = python3.4
-deps=sphinx
- PyYAML
- regendoc>=0.6.1
-whitelist_externals=
- rm
- make
-commands=
- rm -rf /tmp/doc-exec*
- make regen
-
-[testenv:jython]
-changedir=testing
-commands=
- {envpython} {envbindir}/py.test-jython -rfsxX {posargs}
-
-[testenv:py27-cxfreeze]
-changedir=testing/cx_freeze
-platform=linux|darwin
-commands=
- {envpython} install_cx_freeze.py
- {envpython} runtests_setup.py build --build-exe build
- {envpython} tox_run.py
-
-
-[testenv:coveralls]
-passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH COVERALLS_REPO_TOKEN
-usedevelop=True
-basepython=python3.4
-changedir=.
-deps =
- {[testenv]deps}
- coveralls
-commands=
- coverage run --source=_pytest -m pytest testing
- coverage report -m
- coveralls
-
-[pytest]
-minversion=2.0
-plugins=pytester
-#--pyargs --doctest-modules --ignore=.tox
-addopts= -rxsX -p pytester --ignore=testing/cx_freeze
-rsyncdirs=tox.ini pytest.py _pytest testing
-python_files=test_*.py *_test.py testing/*/*.py
-python_classes=Test Acceptance
-python_functions=test
-norecursedirs = .tox ja .hg cx_freeze_source
-
-
-[flake8]
-ignore =E401,E225,E261,E128,E124,E301,E302,E121,E303,W391,E501,E231,E126,E701,E265,E241,E251,E226,E101,W191,E131,E203,E122,E123,E271,E712,E222,E127,E125,E221,W292,E111,E113,E293,E262,W293,E129,E702,E201,E272,E202,E704,E731,E402
diff --git a/tests/wpt/web-platform-tests/tools/serve/serve.py b/tests/wpt/web-platform-tests/tools/serve/serve.py
index 74b9b7e55b7..c77badc2682 100644
--- a/tests/wpt/web-platform-tests/tools/serve/serve.py
+++ b/tests/wpt/web-platform-tests/tools/serve/serve.py
@@ -383,19 +383,19 @@ class ServerProc(object):
self.daemon = None
self.stop = Event()
- def start(self, init_func, host, port, paths, routes, bind_hostname, external_config,
+ def start(self, init_func, host, port, paths, routes, bind_hostname, config,
ssl_config, **kwargs):
self.proc = Process(target=self.create_daemon,
args=(init_func, host, port, paths, routes, bind_hostname,
- external_config, ssl_config),
+ config, ssl_config),
kwargs=kwargs)
self.proc.daemon = True
self.proc.start()
def create_daemon(self, init_func, host, port, paths, routes, bind_hostname,
- external_config, ssl_config, **kwargs):
+ config, ssl_config, **kwargs):
try:
- self.daemon = init_func(host, port, paths, routes, bind_hostname, external_config,
+ self.daemon = init_func(host, port, paths, routes, bind_hostname, config,
ssl_config, **kwargs)
except socket.error:
print("Socket error on port %s" % port, file=sys.stderr)
@@ -446,7 +446,8 @@ def check_subdomains(host, paths, bind_hostname, ssl_config, aliases):
time.sleep(1)
if not connected:
- logger.critical("Failed to connect to test server on http://%s:%s You may need to edit /etc/hosts or similar" % (host, port))
+ logger.critical("Failed to connect to test server on http://%s:%s. "
+ "You may need to edit /etc/hosts or similar, see README.md." % (host, port))
sys.exit(1)
for subdomain, (punycode, host) in subdomains.iteritems():
@@ -454,7 +455,8 @@ def check_subdomains(host, paths, bind_hostname, ssl_config, aliases):
try:
urllib2.urlopen("http://%s:%d/" % (domain, port))
except Exception as e:
- logger.critical("Failed probing domain %s. You may need to edit /etc/hosts or similar." % domain)
+ logger.critical("Failed probing domain %s. "
+ "You may need to edit /etc/hosts or similar, see README.md." % domain)
sys.exit(1)
wrapper.wait()
@@ -466,7 +468,7 @@ def get_subdomains(host):
for subdomain in subdomains}
-def start_servers(host, ports, paths, routes, bind_hostname, external_config, ssl_config,
+def start_servers(host, ports, paths, routes, bind_hostname, config, ssl_config,
**kwargs):
servers = defaultdict(list)
for scheme, ports in ports.iteritems():
@@ -482,13 +484,13 @@ def start_servers(host, ports, paths, routes, bind_hostname, external_config, ss
server_proc = ServerProc()
server_proc.start(init_func, host, port, paths, routes, bind_hostname,
- external_config, ssl_config, **kwargs)
+ config, ssl_config, **kwargs)
servers[scheme].append((port, server_proc))
return servers
-def start_http_server(host, port, paths, routes, bind_hostname, external_config, ssl_config,
+def start_http_server(host, port, paths, routes, bind_hostname, config, ssl_config,
**kwargs):
return wptserve.WebTestHttpd(host=host,
port=port,
@@ -496,14 +498,14 @@ def start_http_server(host, port, paths, routes, bind_hostname, external_config,
routes=routes,
rewrites=rewrites,
bind_hostname=bind_hostname,
- config=external_config,
+ config=config,
use_ssl=False,
key_file=None,
certificate=None,
latency=kwargs.get("latency"))
-def start_https_server(host, port, paths, routes, bind_hostname, external_config, ssl_config,
+def start_https_server(host, port, paths, routes, bind_hostname, config, ssl_config,
**kwargs):
return wptserve.WebTestHttpd(host=host,
port=port,
@@ -511,7 +513,7 @@ def start_https_server(host, port, paths, routes, bind_hostname, external_config
routes=routes,
rewrites=rewrites,
bind_hostname=bind_hostname,
- config=external_config,
+ config=config,
use_ssl=True,
key_file=ssl_config["key_path"],
certificate=ssl_config["cert_path"],
@@ -584,7 +586,7 @@ class WebSocketDaemon(object):
self.server = None
-def start_ws_server(host, port, paths, routes, bind_hostname, external_config, ssl_config,
+def start_ws_server(host, port, paths, routes, bind_hostname, config, ssl_config,
**kwargs):
return WebSocketDaemon(host,
str(port),
@@ -595,7 +597,7 @@ def start_ws_server(host, port, paths, routes, bind_hostname, external_config, s
ssl_config = None)
-def start_wss_server(host, port, paths, routes, bind_hostname, external_config, ssl_config,
+def start_wss_server(host, port, paths, routes, bind_hostname, config, ssl_config,
**kwargs):
return WebSocketDaemon(host,
str(port),
@@ -637,12 +639,22 @@ def normalise_config(config, ports):
for scheme, ports_used in ports.iteritems():
ports_[scheme] = ports_used
- return {"host": host,
- "domains": domains,
- "ports": ports_}
+ # make a (shallow) copy of the config and update that, so that the
+ # normalized config can be used in place of the original one.
+ config_ = config.copy()
+ config_["host"] = host
+ config_["domains"] = domains
+ config_["ports"] = ports_
+ return config_
-def get_ssl_config(config, external_domains, ssl_environment):
+def get_paths(config):
+ return {"doc_root": config["doc_root"],
+ "ws_doc_root": config["ws_doc_root"]}
+
+
+def get_ssl_config(config, ssl_environment):
+ external_domains = config["domains"].values()
key_path, cert_path = ssl_environment.host_cert_path(external_domains)
return {"key_path": key_path,
"cert_path": cert_path,
@@ -650,24 +662,15 @@ def get_ssl_config(config, external_domains, ssl_environment):
def start(config, ssl_environment, routes, **kwargs):
host = config["host"]
- domains = get_subdomains(host)
ports = get_ports(config, ssl_environment)
+ paths = get_paths(config)
bind_hostname = config["bind_hostname"]
+ ssl_config = get_ssl_config(config, ssl_environment)
- paths = {"doc_root": config["doc_root"],
- "ws_doc_root": config["ws_doc_root"]}
-
- external_config = normalise_config(config, ports)
-
- ssl_config = get_ssl_config(config, external_config["domains"].values(), ssl_environment)
-
- if config["check_subdomains"]:
- check_subdomains(host, paths, bind_hostname, ssl_config, config["aliases"])
-
- servers = start_servers(host, ports, paths, routes, bind_hostname, external_config,
+ servers = start_servers(host, ports, paths, routes, bind_hostname, config,
ssl_config, **kwargs)
- return external_config, servers
+ return servers
def iter_procs(servers):
@@ -778,13 +781,23 @@ def run(**kwargs):
setup_logger(config["log_level"])
- stash_address = None
- if config["bind_hostname"]:
- stash_address = (config["host"], get_port())
+ with get_ssl_environment(config) as ssl_env:
+ ports = get_ports(config, ssl_env)
+ config = normalise_config(config, ports)
+ host = config["host"]
+ bind_hostname = config["bind_hostname"]
+
+ if config["check_subdomains"]:
+ paths = get_paths(config)
+ ssl_config = get_ssl_config(config, ssl_env)
+ check_subdomains(host, paths, bind_hostname, ssl_config, config["aliases"])
+
+ stash_address = None
+ if bind_hostname:
+ stash_address = (host, get_port())
- with stash.StashServer(stash_address, authkey=str(uuid.uuid4())):
- with get_ssl_environment(config) as ssl_env:
- config_, servers = start(config, ssl_env, build_routes(config["aliases"]), **kwargs)
+ with stash.StashServer(stash_address, authkey=str(uuid.uuid4())):
+ servers = start(config, ssl_env, build_routes(config["aliases"]), **kwargs)
try:
while any(item.is_alive() for item in iter_procs(servers)):
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/.gitignore b/tests/wpt/web-platform-tests/tools/third_party/attrs/.gitignore
new file mode 100644
index 00000000000..a4ca3f8cef7
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/.gitignore
@@ -0,0 +1,9 @@
+.tox
+.coverage*
+*.pyc
+*.egg-info
+docs/_build/
+htmlcov
+dist
+.cache
+.hypothesis \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/.travis.yml b/tests/wpt/web-platform-tests/tools/third_party/attrs/.travis.yml
new file mode 100644
index 00000000000..4f1cf2f60b0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/.travis.yml
@@ -0,0 +1,56 @@
+dist: trusty
+sudo: false
+cache:
+ directories:
+ - $HOME/.cache/pip
+
+language: python
+
+
+matrix:
+ include:
+ - python: "2.7"
+ env: TOXENV=py27
+ - python: "3.4"
+ env: TOXENV=py34
+ - python: "3.5"
+ env: TOXENV=py35
+ - python: "3.6"
+ env: TOXENV=py36
+ - python: "pypy2.7-5.8.0"
+ env: TOXENV=pypy
+ - python: "pypy3.5-5.8.0"
+ env: TOXENV=pypy3
+
+ # Meta
+ - python: "3.6"
+ env: TOXENV=flake8
+ - python: "3.6"
+ env: TOXENV=manifest
+ - python: "3.6"
+ env: TOXENV=docs
+ - python: "3.6"
+ env: TOXENV=readme
+ - python: "3.6"
+ env: TOXENV=changelog
+
+
+install:
+ - pip install tox
+
+
+script:
+ - tox
+
+
+before_install:
+ - pip install codecov
+
+
+after_success:
+ - tox -e coverage-report
+ - codecov
+
+
+notifications:
+ email: false
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/AUTHORS.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/AUTHORS.rst
new file mode 100644
index 00000000000..73eae2154d5
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/AUTHORS.rst
@@ -0,0 +1,11 @@
+Credits
+=======
+
+``attrs`` is written and maintained by `Hynek Schlawack <https://hynek.me/>`_.
+
+The development is kindly supported by `Variomedia AG <https://www.variomedia.de/>`_.
+
+A full list of contributors can be found in `GitHub's overview <https://github.com/python-attrs/attrs/graphs/contributors>`_.
+
+It’s the spiritual successor of `characteristic <https://characteristic.readthedocs.io/>`_ and aspires to fix some of it clunkiness and unfortunate decisions.
+Both were inspired by Twisted’s `FancyEqMixin <https://twistedmatrix.com/documents/current/api/twisted.python.util.FancyEqMixin.html>`_ but both are implemented using class decorators because `sub-classing is bad for you <https://www.youtube.com/watch?v=3MNVP9-hglc>`_, m’kay?
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/CHANGELOG.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/CHANGELOG.rst
new file mode 100644
index 00000000000..55cd4591357
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/CHANGELOG.rst
@@ -0,0 +1,326 @@
+Changelog
+=========
+
+Versions follow `CalVer <http://calver.org>`_ with a strict backwards compatibility policy.
+The third digit is only for regressions.
+
+Changes for the upcoming release can be found in the `"changelog.d" directory <https://github.com/python-attrs/attrs/tree/master/changelog.d>`_ in our repository.
+
+..
+ Do *NOT* add changelog entries here!
+
+ This changelog is managed by towncrier and is compiled at release time.
+
+ See http://www.attrs.org/en/latest/contributing.html#changelog for details.""" # noqa
+
+.. towncrier release notes start
+
+
+17.3.0 (2017-11-08)
+-------------------
+
+Backward-incompatible Changes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Attributes are not defined on the class body anymore.
+
+ This means that if you define a class ``C`` with an attribute ``x``, the class will *not* have an attribute ``x`` for introspection anymore.
+ Instead of ``C.x``, use ``attr.fields(C).x`` or look at ``C.__attrs_attrs__``.
+ The old behavior has been deprecated since version 16.1.
+ (`#253 <https://github.com/python-attrs/attrs/issues/253>`_)
+
+
+Changes
+^^^^^^^
+
+- ``super()`` and ``__class__`` now work on Python 3 when ``slots=True``.
+ (`#102 <https://github.com/python-attrs/attrs/issues/102>`_, `#226 <https://github.com/python-attrs/attrs/issues/226>`_, `#269 <https://github.com/python-attrs/attrs/issues/269>`_, `#270 <https://github.com/python-attrs/attrs/issues/270>`_, `#272 <https://github.com/python-attrs/attrs/issues/272>`_)
+- Added ``type`` argument to ``attr.ib()`` and corresponding ``type`` attribute to ``attr.Attribute``.
+
+ This change paves the way for automatic type checking and serialization (though as of this release ``attrs`` does not make use of it).
+ In Python 3.6 or higher, the value of ``attr.Attribute.type`` can alternately be set using variable type annotations
+ (see `PEP 526 <https://www.python.org/dev/peps/pep-0526/>`_). (`#151 <https://github.com/python-attrs/attrs/issues/151>`_, `#214 <https://github.com/python-attrs/attrs/issues/214>`_, `#215 <https://github.com/python-attrs/attrs/issues/215>`_, `#239 <https://github.com/python-attrs/attrs/issues/239>`_)
+- The combination of ``str=True`` and ``slots=True`` now works on Python 2.
+ (`#198 <https://github.com/python-attrs/attrs/issues/198>`_)
+- ``attr.Factory`` is hashable again. (`#204
+ <https://github.com/python-attrs/attrs/issues/204>`_)
+- Subclasses now can overwrite attribute definitions of their superclass.
+
+ That means that you can -- for example -- change the default value for an attribute by redefining it.
+ (`#221 <https://github.com/python-attrs/attrs/issues/221>`_, `#229 <https://github.com/python-attrs/attrs/issues/229>`_)
+- Added new option ``auto_attribs`` to ``@attr.s`` that allows to collect annotated fields without setting them to ``attr.ib()``.
+
+ Setting a field to an ``attr.ib()`` is still possible to supply options like validators.
+ Setting it to any other value is treated like it was passed as ``attr.ib(default=value)`` -- passing an instance of ``attr.Factory`` also works as expected.
+ (`#262 <https://github.com/python-attrs/attrs/issues/262>`_, `#277 <https://github.com/python-attrs/attrs/issues/277>`_)
+- Instances of classes created using ``attr.make_class()`` can now be pickled.
+ (`#282 <https://github.com/python-attrs/attrs/issues/282>`_)
+
+
+----
+
+
+17.2.0 (2017-05-24)
+-------------------
+
+
+Changes:
+^^^^^^^^
+
+- Validators are hashable again.
+ Note that validators may become frozen in the future, pending availability of no-overhead frozen classes.
+ `#192 <https://github.com/python-attrs/attrs/issues/192>`_
+
+
+----
+
+
+17.1.0 (2017-05-16)
+-------------------
+
+To encourage more participation, the project has also been moved into a `dedicated GitHub organization <https://github.com/python-attrs/>`_ and everyone is most welcome to join!
+
+``attrs`` also has a logo now!
+
+.. image:: http://www.attrs.org/en/latest/_static/attrs_logo.png
+ :alt: attrs logo
+
+
+Backward-incompatible Changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- ``attrs`` will set the ``__hash__()`` method to ``None`` by default now.
+ The way hashes were handled before was in conflict with `Python's specification <https://docs.python.org/3/reference/datamodel.html#object.__hash__>`_.
+ This *may* break some software although this breakage is most likely just surfacing of latent bugs.
+ You can always make ``attrs`` create the ``__hash__()`` method using ``@attr.s(hash=True)``.
+ See `#136`_ for the rationale of this change.
+
+ .. warning::
+
+ Please *do not* upgrade blindly and *do* test your software!
+ *Especially* if you use instances as dict keys or put them into sets!
+
+- Correspondingly, ``attr.ib``'s ``hash`` argument is ``None`` by default too and mirrors the ``cmp`` argument as it should.
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+- ``attr.assoc()`` is now deprecated in favor of ``attr.evolve()`` and will stop working in 2018.
+
+
+Changes:
+^^^^^^^^
+
+- Fix default hashing behavior.
+ Now *hash* mirrors the value of *cmp* and classes are unhashable by default.
+ `#136`_
+ `#142 <https://github.com/python-attrs/attrs/issues/142>`_
+- Added ``attr.evolve()`` that, given an instance of an ``attrs`` class and field changes as keyword arguments, will instantiate a copy of the given instance with the changes applied.
+ ``evolve()`` replaces ``assoc()``, which is now deprecated.
+ ``evolve()`` is significantly faster than ``assoc()``, and requires the class have an initializer that can take the field values as keyword arguments (like ``attrs`` itself can generate).
+ `#116 <https://github.com/python-attrs/attrs/issues/116>`_
+ `#124 <https://github.com/python-attrs/attrs/pull/124>`_
+ `#135 <https://github.com/python-attrs/attrs/pull/135>`_
+- ``FrozenInstanceError`` is now raised when trying to delete an attribute from a frozen class.
+ `#118 <https://github.com/python-attrs/attrs/pull/118>`_
+- Frozen-ness of classes is now inherited.
+ `#128 <https://github.com/python-attrs/attrs/pull/128>`_
+- ``__attrs_post_init__()`` is now run if validation is disabled.
+ `#130 <https://github.com/python-attrs/attrs/pull/130>`_
+- Added ``attr.validators.in_(options)`` that, given the allowed `options`, checks whether the attribute value is in it.
+ This can be used to check constants, enums, mappings, etc.
+ `#181 <https://github.com/python-attrs/attrs/pull/181>`_
+- Added ``attr.validators.and_()`` that composes multiple validators into one.
+ `#161 <https://github.com/python-attrs/attrs/issues/161>`_
+- For convenience, the ``validator`` argument of ``@attr.s`` now can take a ``list`` of validators that are wrapped using ``and_()``.
+ `#138 <https://github.com/python-attrs/attrs/issues/138>`_
+- Accordingly, ``attr.validators.optional()`` now can take a ``list`` of validators too.
+ `#161 <https://github.com/python-attrs/attrs/issues/161>`_
+- Validators can now be defined conveniently inline by using the attribute as a decorator.
+ Check out the `examples <http://www.attrs.org/en/stable/examples.html#validators>`_ to see it in action!
+ `#143 <https://github.com/python-attrs/attrs/issues/143>`_
+- ``attr.Factory()`` now has a ``takes_self`` argument that makes the initializer to pass the partially initialized instance into the factory.
+ In other words you can define attribute defaults based on other attributes.
+ `#165`_
+ `#189 <https://github.com/python-attrs/attrs/issues/189>`_
+- Default factories can now also be defined inline using decorators.
+ They are *always* passed the partially initialized instance.
+ `#165`_
+- Conversion can now be made optional using ``attr.converters.optional()``.
+ `#105 <https://github.com/python-attrs/attrs/issues/105>`_
+ `#173 <https://github.com/python-attrs/attrs/pull/173>`_
+- ``attr.make_class()`` now accepts the keyword argument ``bases`` which allows for subclassing.
+ `#152 <https://github.com/python-attrs/attrs/pull/152>`_
+- Metaclasses are now preserved with ``slots=True``.
+ `#155 <https://github.com/python-attrs/attrs/pull/155>`_
+
+.. _`#136`: https://github.com/python-attrs/attrs/issues/136
+.. _`#165`: https://github.com/python-attrs/attrs/issues/165
+
+
+----
+
+
+16.3.0 (2016-11-24)
+-------------------
+
+Changes:
+^^^^^^^^
+
+- Attributes now can have user-defined metadata which greatly improves ``attrs``'s extensibility.
+ `#96 <https://github.com/python-attrs/attrs/pull/96>`_
+- Allow for a ``__attrs_post_init__()`` method that -- if defined -- will get called at the end of the ``attrs``-generated ``__init__()`` method.
+ `#111 <https://github.com/python-attrs/attrs/pull/111>`_
+- Added ``@attr.s(str=True)`` that will optionally create a ``__str__()`` method that is identical to ``__repr__()``.
+ This is mainly useful with ``Exception``\ s and other classes that rely on a useful ``__str__()`` implementation but overwrite the default one through a poor own one.
+ Default Python class behavior is to use ``__repr__()`` as ``__str__()`` anyways.
+
+ If you tried using ``attrs`` with ``Exception``\ s and were puzzled by the tracebacks: this option is for you.
+- ``__name__`` is not overwritten with ``__qualname__`` for ``attr.s(slots=True)`` classes anymore.
+ `#99 <https://github.com/python-attrs/attrs/issues/99>`_
+
+
+----
+
+
+16.2.0 (2016-09-17)
+-------------------
+
+Changes:
+^^^^^^^^
+
+- Added ``attr.astuple()`` that -- similarly to ``attr.asdict()`` -- returns the instance as a tuple.
+ `#77 <https://github.com/python-attrs/attrs/issues/77>`_
+- Converts now work with frozen classes.
+ `#76 <https://github.com/python-attrs/attrs/issues/76>`_
+- Instantiation of ``attrs`` classes with converters is now significantly faster.
+ `#80 <https://github.com/python-attrs/attrs/pull/80>`_
+- Pickling now works with ``__slots__`` classes.
+ `#81 <https://github.com/python-attrs/attrs/issues/81>`_
+- ``attr.assoc()`` now works with ``__slots__`` classes.
+ `#84 <https://github.com/python-attrs/attrs/issues/84>`_
+- The tuple returned by ``attr.fields()`` now also allows to access the ``Attribute`` instances by name.
+ Yes, we've subclassed ``tuple`` so you don't have to!
+ Therefore ``attr.fields(C).x`` is equivalent to the deprecated ``C.x`` and works with ``__slots__`` classes.
+ `#88 <https://github.com/python-attrs/attrs/issues/88>`_
+
+
+----
+
+
+16.1.0 (2016-08-30)
+-------------------
+
+Backward-incompatible Changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- All instances where function arguments were called ``cl`` have been changed to the more Pythonic ``cls``.
+ Since it was always the first argument, it's doubtful anyone ever called those function with in the keyword form.
+ If so, sorry for any breakage but there's no practical deprecation path to solve this ugly wart.
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+- Accessing ``Attribute`` instances on class objects is now deprecated and will stop working in 2017.
+ If you need introspection please use the ``__attrs_attrs__`` attribute or the ``attr.fields()`` function that carry them too.
+ In the future, the attributes that are defined on the class body and are usually overwritten in your ``__init__`` method are simply removed after ``@attr.s`` has been applied.
+
+ This will remove the confusing error message if you write your own ``__init__`` and forget to initialize some attribute.
+ Instead you will get a straightforward ``AttributeError``.
+ In other words: decorated classes will work more like plain Python classes which was always ``attrs``'s goal.
+- The serious business aliases ``attr.attributes`` and ``attr.attr`` have been deprecated in favor of ``attr.attrs`` and ``attr.attrib`` which are much more consistent and frankly obvious in hindsight.
+ They will be purged from documentation immediately but there are no plans to actually remove them.
+
+
+Changes:
+^^^^^^^^
+
+- ``attr.asdict()``\ 's ``dict_factory`` arguments is now propagated on recursion.
+ `#45 <https://github.com/python-attrs/attrs/issues/45>`_
+- ``attr.asdict()``, ``attr.has()`` and ``attr.fields()`` are significantly faster.
+ `#48 <https://github.com/python-attrs/attrs/issues/48>`_
+ `#51 <https://github.com/python-attrs/attrs/issues/51>`_
+- Add ``attr.attrs`` and ``attr.attrib`` as a more consistent aliases for ``attr.s`` and ``attr.ib``.
+- Add ``frozen`` option to ``attr.s`` that will make instances best-effort immutable.
+ `#60 <https://github.com/python-attrs/attrs/issues/60>`_
+- ``attr.asdict()`` now takes ``retain_collection_types`` as an argument.
+ If ``True``, it does not convert attributes of type ``tuple`` or ``set`` to ``list``.
+ `#69 <https://github.com/python-attrs/attrs/issues/69>`_
+
+
+----
+
+
+16.0.0 (2016-05-23)
+-------------------
+
+Backward-incompatible Changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Python 3.3 and 2.6 aren't supported anymore.
+ They may work by chance but any effort to keep them working has ceased.
+
+ The last Python 2.6 release was on October 29, 2013 and isn't supported by the CPython core team anymore.
+ Major Python packages like Django and Twisted dropped Python 2.6 a while ago already.
+
+ Python 3.3 never had a significant user base and wasn't part of any distribution's LTS release.
+
+Changes:
+^^^^^^^^
+
+- ``__slots__`` have arrived!
+ Classes now can automatically be `slots <https://docs.python.org/3/reference/datamodel.html#slots>`_-style (and save your precious memory) just by passing ``slots=True``.
+ `#35 <https://github.com/python-attrs/attrs/issues/35>`_
+- Allow the case of initializing attributes that are set to ``init=False``.
+ This allows for clean initializer parameter lists while being able to initialize attributes to default values.
+ `#32 <https://github.com/python-attrs/attrs/issues/32>`_
+- ``attr.asdict()`` can now produce arbitrary mappings instead of Python ``dict``\ s when provided with a ``dict_factory`` argument.
+ `#40 <https://github.com/python-attrs/attrs/issues/40>`_
+- Multiple performance improvements.
+
+
+----
+
+
+15.2.0 (2015-12-08)
+-------------------
+
+Changes:
+^^^^^^^^
+
+- Added a ``convert`` argument to ``attr.ib``, which allows specifying a function to run on arguments.
+ This allows for simple type conversions, e.g. with ``attr.ib(convert=int)``.
+ `#26 <https://github.com/python-attrs/attrs/issues/26>`_
+- Speed up object creation when attribute validators are used.
+ `#28 <https://github.com/python-attrs/attrs/issues/28>`_
+
+
+----
+
+
+15.1.0 (2015-08-20)
+-------------------
+
+Changes:
+^^^^^^^^
+
+- Added ``attr.validators.optional()`` that wraps other validators allowing attributes to be ``None``.
+ `#16 <https://github.com/python-attrs/attrs/issues/16>`_
+- Multi-level inheritance now works.
+ `#24 <https://github.com/python-attrs/attrs/issues/24>`_
+- ``__repr__()`` now works with non-redecorated subclasses.
+ `#20 <https://github.com/python-attrs/attrs/issues/20>`_
+
+
+----
+
+
+15.0.0 (2015-04-15)
+-------------------
+
+Changes:
+^^^^^^^^
+
+Initial release.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/CODE_OF_CONDUCT.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/CODE_OF_CONDUCT.rst
new file mode 100644
index 00000000000..4f4b8ee9cd9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/CODE_OF_CONDUCT.rst
@@ -0,0 +1,55 @@
+Contributor Covenant Code of Conduct
+====================================
+
+Our Pledge
+----------
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+Our Standards
+-------------
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+Our Responsibilities
+--------------------
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+Scope
+-----
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
+Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
+Representation of a project may be further defined and clarified by project maintainers.
+
+Enforcement
+-----------
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hs@ox.cx.
+All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
+The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+Attribution
+-----------
+
+This Code of Conduct is adapted from the `Contributor Covenant <http://contributor-covenant.org>`_, version 1.4, available at http://contributor-covenant.org/version/1/4.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/CONTRIBUTING.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/CONTRIBUTING.rst
new file mode 100644
index 00000000000..9822f8926a8
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/CONTRIBUTING.rst
@@ -0,0 +1,220 @@
+How To Contribute
+=================
+
+First off, thank you for considering contributing to ``attrs``!
+It's people like *you* who make it is such a great tool for everyone.
+
+This document is mainly to help you to get started by codifying tribal knowledge and expectations and make it more accessible to everyone.
+But don't be afraid to open half-finished PRs and ask questions if something is unclear!
+
+
+Support
+-------
+
+In case you'd like to help out but don't want to deal with GitHub, there's a great opportunity:
+help your fellow developers on `StackOverflow <https://stackoverflow.com/questions/tagged/python-attrs>`_!
+
+The offical tag is ``python-attrs`` and helping out in support frees us up for improving ``attrs`` instead!
+
+
+Workflow
+--------
+
+- No contribution is too small!
+ Please submit as many fixes for typos and grammar bloopers as you can!
+- Try to limit each pull request to *one* change only.
+- *Always* add tests and docs for your code.
+ This is a hard rule; patches with missing tests or documentation can't be merged.
+- Make sure your changes pass our CI_.
+ You won't get any feedback until it's green unless you ask for it.
+- Once you've addressed review feedback, make sure to bump the pull request with a short note, so we know you're done.
+- Don’t break `backward compatibility`_.
+
+
+Code
+----
+
+- Obey `PEP 8`_ and `PEP 257`_.
+ We use the ``"""``\ -on-separate-lines style for docstrings:
+
+ .. code-block:: python
+
+ def func(x):
+ """
+ Do something.
+
+ :param str x: A very important parameter.
+
+ :rtype: str
+ """
+- If you add or change public APIs, tag the docstring using ``.. versionadded:: 16.0.0 WHAT`` or ``.. versionchanged:: 16.2.0 WHAT``.
+- Prefer double quotes (``"``) over single quotes (``'``) unless the string contains double quotes itself.
+
+
+Tests
+-----
+
+- Write your asserts as ``expected == actual`` to line them up nicely:
+
+ .. code-block:: python
+
+ x = f()
+
+ assert 42 == x.some_attribute
+ assert "foo" == x._a_private_attribute
+
+- To run the test suite, all you need is a recent tox_.
+ It will ensure the test suite runs with all dependencies against all Python versions just as it will on Travis CI.
+ If you lack some Python versions, you can can always limit the environments like ``tox -e py27,py35`` (in that case you may want to look into pyenv_, which makes it very easy to install many different Python versions in parallel).
+- Write `good test docstrings`_.
+- To ensure new features work well with the rest of the system, they should be also added to our `Hypothesis`_ testing strategy which you find in ``tests/util.py``.
+
+
+Documentation
+-------------
+
+- Use `semantic newlines`_ in reStructuredText_ files (files ending in ``.rst``):
+
+ .. code-block:: rst
+
+ This is a sentence.
+ This is another sentence.
+
+- If you start a new section, add two blank lines before and one blank line after the header except if two headers follow immediately after each other:
+
+ .. code-block:: rst
+
+ Last line of previous section.
+
+
+ Header of New Top Section
+ -------------------------
+
+ Header of New Section
+ ^^^^^^^^^^^^^^^^^^^^^
+
+ First line of new section.
+- If you add a new feature, demonstrate its awesomeness in the `examples page`_!
+
+
+Changelog
+^^^^^^^^^
+
+If your change is noteworthy, there needs to be a changelog entry, so our users can learn about it!
+
+To avoid merge conflicts, we use the towncrier_ package to manage our changelog.
+``towncrier`` uses independent files for each pull request -- so called *news fragments* -- instead of one monolithic changelog file.
+On release those news fragments are compiled into our ``CHANGELOG.rst``.
+
+You don't need to install ``towncrier`` yourself, you just have to abide to a few simple rules:
+
+- For each pull request, add a new file into ``changelog.d`` with a filename adhering to the ``pr#.(change|deprecation|breaking).rst`` schema:
+ For example ``changelog.d/42.change.rst`` for a non-breaking change, that is proposed in pull request number 42.
+- As with other docs, please use `semantic newlines`_ within news fragments.
+- Wrap symbols like modules, functions, or classes into double backticks so they are rendered in a monospaced font.
+- If you mention functions or other callables, add parantheses at the end of their names: ``attr.func()`` or ``attr.Class.method()``.
+ This makes the changelog a lot more readable.
+- Prefer simple past or constructions with "now".
+ For example:
+
+ + Added ``attr.validators.func()``.
+ + ``attr.func()`` now doesn't crash the Large Hadron Collider anymore.
+- If you want to reference multiple issues, copy the news fragment to another filename.
+ ``towncrier`` will merge all news fragments with identical contents into one entry with multiple links to the respective pull requests.
+
+Example entries:
+
+ .. code-block:: rst
+
+ Added ``attr.validators.func()``.
+ The feature really *is* awesome.
+
+or:
+
+ .. code-block:: rst
+
+ ``attr.func()`` now doesn't crash the Large Hadron Collider anymore.
+ The bug really *was* nasty.
+
+----
+
+``tox -e changelog`` will render the current changelog to the terminal if you have any doubts.
+
+
+Local Development Environment
+-----------------------------
+
+You can (and should) run our test suite using tox_.
+However you’ll probably want a more traditional environment too.
+We highly recommend to develop using the latest Python 3 release because ``attrs`` tries to take advantage of modern features whenever possible.
+
+First create a `virtual environment <https://virtualenv.pypa.io/>`_.
+It’s out of scope for this document to list all the ways to manage virtual environments in Python but if you don’t have already a pet way, take some time to look at tools like `pew <https://github.com/berdario/pew>`_, `virtualfish <http://virtualfish.readthedocs.io/>`_, and `virtualenvwrapper <http://virtualenvwrapper.readthedocs.io/>`_.
+
+Next get an up to date checkout of the ``attrs`` repository:
+
+.. code-block:: bash
+
+ git checkout git@github.com:python-attrs/attrs.git
+
+Change into the newly created directory and **after activating your virtual environment** install an editable version of ``attrs``:
+
+.. code-block:: bash
+
+ cd attrs
+ pip install -e .
+
+If you run the virtual environment’s Python and try to ``import attr`` it should work!
+
+To run the test suite, you'll need our development dependencies which can be installed using
+
+.. code-block:: bash
+
+ pip install -r dev-requirements.txt
+
+At this point
+
+.. code-block:: bash
+
+ python -m pytest
+
+should work and pass!
+
+
+Governance
+----------
+
+``attrs`` is maintained by `team of volunteers`_ that is always open for new members that share our vision of a fast, lean, and magic-free library that empowers programmers to write better code with less effort.
+If you'd like to join, just get a pull request merged and ask to be added in the very same pull request!
+
+**The simple rule is that everyone is welcome to review/merge pull requests of others but nobody is allowed to merge their own code.**
+
+`Hynek Schlawack`_ acts reluctantly as the BDFL_ and has the final say over design decisions.
+
+
+****
+
+Please note that this project is released with a Contributor `Code of Conduct`_.
+By participating in this project you agree to abide by its terms.
+Please report any harm to `Hynek Schlawack`_ in any way you find appropriate.
+
+Thank you for considering contributing to ``attrs``!
+
+
+.. _`Hynek Schlawack`: https://hynek.me/about/
+.. _`PEP 8`: https://www.python.org/dev/peps/pep-0008/
+.. _`PEP 257`: https://www.python.org/dev/peps/pep-0257/
+.. _`good test docstrings`: https://jml.io/pages/test-docstrings.html
+.. _`Code of Conduct`: https://github.com/python-attrs/attrs/blob/master/CODE_OF_CONDUCT.rst
+.. _changelog: https://github.com/python-attrs/attrs/blob/master/CHANGELOG.rst
+.. _`backward compatibility`: http://www.attrs.org/en/latest/backward-compatibility.html
+.. _tox: https://tox.readthedocs.io/
+.. _pyenv: https://github.com/pyenv/pyenv
+.. _reStructuredText: http://www.sphinx-doc.org/en/stable/rest.html
+.. _semantic newlines: http://rhodesmill.org/brandon/2012/one-sentence-per-line/
+.. _examples page: https://github.com/python-attrs/attrs/blob/master/docs/examples.rst
+.. _Hypothesis: https://hypothesis.readthedocs.io/
+.. _CI: https://travis-ci.org/python-attrs/attrs/
+.. _`team of volunteers`: https://github.com/python-attrs
+.. _BDFL: https://en.wikipedia.org/wiki/Benevolent_dictator_for_life
+.. _towncrier: https://pypi.org/project/towncrier
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/LICENSE b/tests/wpt/web-platform-tests/tools/third_party/attrs/LICENSE
new file mode 100644
index 00000000000..7ae3df93097
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Hynek Schlawack
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/MANIFEST.in b/tests/wpt/web-platform-tests/tools/third_party/attrs/MANIFEST.in
new file mode 100644
index 00000000000..03a948ef2c4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/MANIFEST.in
@@ -0,0 +1,19 @@
+include LICENSE *.rst *.toml
+
+# Don't package GitHub-specific files.
+exclude *.md .travis.yml
+
+# Tests
+include tox.ini .coveragerc conftest.py dev-requirements.txt docs-requirements.txt
+recursive-include tests *.py
+
+# Documentation
+include docs/Makefile docs/docutils.conf
+recursive-include docs *.png
+recursive-include docs *.svg
+recursive-include docs *.py
+recursive-include docs *.rst
+prune docs/_build
+
+# Changelog news fragments -- is empty on releases.
+prune changelog.d
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/PULL_REQUEST_TEMPLATE.md b/tests/wpt/web-platform-tests/tools/third_party/attrs/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000000..34c468e6ae5
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,12 @@
+# Pull Request Check List
+
+This is just a reminder about the most common mistakes. Please make sure that you tick all *appropriate* boxes. But please read our [contribution guide](http://www.attrs.org/en/latest/contributing.html) at least once, it will save you unnecessary review cycles!
+
+- [ ] Added **tests** for changed code.
+- [ ] New features have been added to our [Hypothesis testing strategy](https://github.com/python-attrs/attrs/blob/master/tests/utils.py).
+- [ ] Updated **documentation** for changed code.
+- [ ] Documentation in `.rst` files is written using [semantic newlines](http://rhodesmill.org/brandon/2012/one-sentence-per-line/).
+- [ ] Changed/added classes/methods/functions have appropriate `versionadded`, `versionchanged`, or `deprecated` [directives](http://www.sphinx-doc.org/en/stable/markup/para.html#directive-versionadded).
+- [ ] Changes (and possible deprecations) have news fragments in [`changelog.d`](https://github.com/python-attrs/attrs/blob/master/changelog.d).
+
+If you have *any* questions to *any* of the points above, just **submit and ask**! This checklist is here to *help* you, not to deter you from contributing!
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/README.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/README.rst
new file mode 100644
index 00000000000..84d6aa76531
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/README.rst
@@ -0,0 +1,129 @@
+.. image:: http://www.attrs.org/en/latest/_static/attrs_logo.png
+ :alt: attrs Logo
+
+======================================
+``attrs``: Classes Without Boilerplate
+======================================
+
+.. image:: https://readthedocs.org/projects/attrs/badge/?version=stable
+ :target: http://www.attrs.org/en/stable/?badge=stable
+ :alt: Documentation Status
+
+.. image:: https://travis-ci.org/python-attrs/attrs.svg?branch=master
+ :target: https://travis-ci.org/python-attrs/attrs
+ :alt: CI Status
+
+.. image:: https://codecov.io/github/python-attrs/attrs/branch/master/graph/badge.svg
+ :target: https://codecov.io/github/python-attrs/attrs
+ :alt: Test Coverage
+
+.. teaser-begin
+
+``attrs`` is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka `dunder <https://nedbatchelder.com/blog/200605/dunder.html>`_ methods).
+
+Its main goal is to help you to write **concise** and **correct** software without slowing down your code.
+
+.. -spiel-end-
+
+For that, it gives you a class decorator and a way to declaratively define the attributes on that class:
+
+.. -code-begin-
+
+.. code-block:: pycon
+
+ >>> import attr
+
+ >>> @attr.s
+ ... class SomeClass(object):
+ ... a_number = attr.ib(default=42)
+ ... list_of_numbers = attr.ib(default=attr.Factory(list))
+ ...
+ ... def hard_math(self, another_number):
+ ... return self.a_number + sum(self.list_of_numbers) * another_number
+
+
+ >>> sc = SomeClass(1, [1, 2, 3])
+ >>> sc
+ SomeClass(a_number=1, list_of_numbers=[1, 2, 3])
+
+ >>> sc.hard_math(3)
+ 19
+ >>> sc == SomeClass(1, [1, 2, 3])
+ True
+ >>> sc != SomeClass(2, [3, 2, 1])
+ True
+
+ >>> attr.asdict(sc)
+ {'a_number': 1, 'list_of_numbers': [1, 2, 3]}
+
+ >>> SomeClass()
+ SomeClass(a_number=42, list_of_numbers=[])
+
+ >>> C = attr.make_class("C", ["a", "b"])
+ >>> C("foo", "bar")
+ C(a='foo', b='bar')
+
+
+After *declaring* your attributes ``attrs`` gives you:
+
+- a concise and explicit overview of the class's attributes,
+- a nice human-readable ``__repr__``,
+- a complete set of comparison methods,
+- an initializer,
+- and much more,
+
+*without* writing dull boilerplate code again and again and *without* runtime performance penalties.
+
+This gives you the power to use actual classes with actual types in your code instead of confusing ``tuple``\ s or `confusingly behaving <http://www.attrs.org/en/stable/why.html#namedtuples>`_ ``namedtuple``\ s.
+Which in turn encourages you to write *small classes* that do `one thing well <https://www.destroyallsoftware.com/talks/boundaries>`_.
+Never again violate the `single responsibility principle <https://en.wikipedia.org/wiki/Single_responsibility_principle>`_ just because implementing ``__init__`` et al is a painful drag.
+
+
+.. -testimonials-
+
+Testimonials
+============
+
+**Amber Hawkie Brown**, Twisted Release Manager and Computer Owl:
+
+ Writing a fully-functional class using attrs takes me less time than writing this testimonial.
+
+
+**Glyph Lefkowitz**, creator of `Twisted <https://twistedmatrix.com/>`_, `Automat <https://pypi.python.org/pypi/Automat>`_, and other open source software, in `The One Python Library Everyone Needs <https://glyph.twistedmatrix.com/2016/08/attrs.html>`_:
+
+ I’m looking forward to is being able to program in Python-with-attrs everywhere.
+ It exerts a subtle, but positive, design influence in all the codebases I’ve see it used in.
+
+
+**Kenneth Reitz**, author of `requests <http://www.python-requests.org/>`_, Python Overlord at Heroku, `on paper no less <https://twitter.com/hynek/status/866817877650751488>`_:
+
+ attrs—classes for humans. I like it.
+
+
+**Łukasz Langa**, prolific CPython core developer and Production Engineer at Facebook:
+
+ I'm increasingly digging your attr.ocity. Good job!
+
+
+.. -end-
+
+.. -project-information-
+
+Getting Help
+============
+
+Please use the ``python-attrs`` tag on `StackOverflow <https://stackoverflow.com/questions/tagged/python-attrs>`_ to get help.
+
+Answering questions of your fellow developers is also great way to help the project!
+
+
+Project Information
+===================
+
+``attrs`` is released under the `MIT <https://choosealicense.com/licenses/mit/>`_ license,
+its documentation lives at `Read the Docs <http://www.attrs.org/>`_,
+the code on `GitHub <https://github.com/python-attrs/attrs>`_,
+and the latest release on `PyPI <https://pypi.org/project/attrs/>`_.
+It’s rigorously tested on Python 2.7, 3.4+, and PyPy.
+
+If you'd like to contribute you're most welcome and we've written `a little guide <http://www.attrs.org/en/latest/contributing.html>`_ to get you started!
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/log/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/.gitignore
index e69de29bb2d..e69de29bb2d 100644
--- a/tests/wpt/web-platform-tests/tools/py/testing/log/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/.gitignore
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/261.change.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/261.change.rst
new file mode 100644
index 00000000000..6867f66dd23
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/261.change.rst
@@ -0,0 +1,4 @@
+Generated ``__hash__`` methods now hash the class type along with the attribute values.
+Until now the hashes of two classes with the same values were identical which was a bug.
+
+The generated method is also *much* faster now.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/284.change.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/284.change.rst
new file mode 100644
index 00000000000..266599daabe
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/284.change.rst
@@ -0,0 +1,2 @@
+``ctypes`` is optional now however if it's missing, a bare ``super()`` will not work in slots classes.
+This should only happen in special environments like Google App Engine.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/285.change.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/285.change.rst
new file mode 100644
index 00000000000..c3fbb797d4f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/285.change.rst
@@ -0,0 +1,2 @@
+The attribute redefinition feature introduced in 17.3.0 now takes into account if an attribute is redefined via multiple inheritance.
+In that case, the definition that is closer to the base of the class hierarchy wins.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/286.change.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/286.change.rst
new file mode 100644
index 00000000000..266599daabe
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/286.change.rst
@@ -0,0 +1,2 @@
+``ctypes`` is optional now however if it's missing, a bare ``super()`` will not work in slots classes.
+This should only happen in special environments like Google App Engine.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/287.change.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/287.change.rst
new file mode 100644
index 00000000000..c3fbb797d4f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/287.change.rst
@@ -0,0 +1,2 @@
+The attribute redefinition feature introduced in 17.3.0 now takes into account if an attribute is redefined via multiple inheritance.
+In that case, the definition that is closer to the base of the class hierarchy wins.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/291.change.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/291.change.rst
new file mode 100644
index 00000000000..0d5438f1dbe
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/291.change.rst
@@ -0,0 +1 @@
+Subclasses of ``auto_attribs=True`` can be empty now.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/292.change.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/292.change.rst
new file mode 100644
index 00000000000..0d5438f1dbe
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/292.change.rst
@@ -0,0 +1 @@
+Subclasses of ``auto_attribs=True`` can be empty now.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/295.change.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/295.change.rst
new file mode 100644
index 00000000000..6867f66dd23
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/295.change.rst
@@ -0,0 +1,4 @@
+Generated ``__hash__`` methods now hash the class type along with the attribute values.
+Until now the hashes of two classes with the same values were identical which was a bug.
+
+The generated method is also *much* faster now.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/296.change.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/296.change.rst
new file mode 100644
index 00000000000..6867f66dd23
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/changelog.d/296.change.rst
@@ -0,0 +1,4 @@
+Generated ``__hash__`` methods now hash the class type along with the attribute values.
+Until now the hashes of two classes with the same values were identical which was a bug.
+
+The generated method is also *much* faster now.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/conftest.py
new file mode 100644
index 00000000000..ed4d6525b45
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/conftest.py
@@ -0,0 +1,28 @@
+from __future__ import absolute_import, division, print_function
+
+import sys
+
+import pytest
+
+
+@pytest.fixture(scope="session")
+def C():
+ """
+ Return a simple but fully featured attrs class with an x and a y attribute.
+ """
+ import attr
+
+ @attr.s
+ class C(object):
+ x = attr.ib()
+ y = attr.ib()
+
+ return C
+
+
+collect_ignore = []
+if sys.version_info[:2] < (3, 6):
+ collect_ignore.extend([
+ "tests/test_annotations.py",
+ "tests/test_init_subclass.py",
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/dev-requirements.txt b/tests/wpt/web-platform-tests/tools/third_party/attrs/dev-requirements.txt
new file mode 100644
index 00000000000..bbd6b9201e0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/dev-requirements.txt
@@ -0,0 +1,6 @@
+coverage
+hypothesis
+pympler
+pytest
+six
+zope.interface
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs-requirements.txt b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs-requirements.txt
new file mode 100644
index 00000000000..c473e1e4324
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs-requirements.txt
@@ -0,0 +1,3 @@
+-e .
+sphinx
+zope.interface
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/Makefile b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/Makefile
new file mode 100644
index 00000000000..3143891daf8
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/Makefile
@@ -0,0 +1,177 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/attrs.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/attrs.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/attrs"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/attrs"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/_static/attrs_logo.png b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/_static/attrs_logo.png
new file mode 100644
index 00000000000..11b6e6fe3f9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/_static/attrs_logo.png
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/_static/attrs_logo.svg b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/_static/attrs_logo.svg
new file mode 100644
index 00000000000..1bb3e4b7273
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/_static/attrs_logo.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 142 118" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><rect id="ArtBoard1" x="0" y="0" width="141.578" height="117.638" style="fill:none;"/><path d="M32.55,82.516c-0.191,4.027 -0.832,8.503 -1.793,13.238c-1.023,4.793 -2.301,9.269 -3.836,13.234c-3.324,1.215 -7.035,1.922 -10.808,1.922c-3.77,0 -7.161,-0.707 -9.973,-1.922c0,0 -0.387,-3.133 0.637,-7.863c1.023,-4.797 2.687,-7.93 2.687,-7.93c3.324,-1.215 7.098,-1.918 10.871,-1.918c1.856,0 3.645,0.192 5.309,0.512c0.445,-2.688 0.832,-5.309 0.832,-5.309c0,0 -2.496,-0.703 -4.988,-0.703c-4.864,0 -9.465,1.086 -11.45,1.664c0.579,-1.726 1.09,-3.328 1.727,-4.925c3.328,-1.153 7.035,-1.856 10.809,-1.856c3.773,0 7.16,0.703 9.976,1.856Zm-15.348,23.277c2.75,0 5.309,-0.703 5.309,-0.703c0,0 1.34,-4.668 2.109,-7.996c-1.152,-0.317 -3.132,-0.704 -5.371,-0.704c-2.496,0 -5.437,0.704 -5.437,0.704c0,0 -0.703,1.664 -1.215,4.031c-0.512,2.301 -0.445,3.965 -0.445,3.965c0,0 2.554,0.703 5.05,0.703Zm32.676,0c2.813,0 5.629,-0.512 7.867,-1.024c-0.769,1.981 -1.664,3.774 -2.621,5.5c-2.047,0.383 -4.16,0.641 -6.332,0.641c-3.773,0 -7.097,-0.707 -9.914,-1.922c0.129,-3.965 0.77,-8.441 1.793,-13.234l1.918,-9.082l-6.461,0c0.707,-1.789 1.473,-3.453 2.496,-5.051l5.051,0l1.344,-6.332c1.918,-0.703 3.965,-1.215 6.074,-1.535l-1.66,7.867l11.125,0c-0.703,1.789 -1.535,3.516 -2.492,5.051l-9.723,0l-1.918,9.082c-0.703,3.262 -1.469,9.336 -1.469,9.336c0,0 2.493,0.703 4.922,0.703Zm27.496,0c2.817,0 5.629,-0.512 7.867,-1.024c-0.765,1.981 -1.664,3.774 -2.621,5.5c-2.046,0.383 -4.156,0.641 -6.332,0.641c-3.773,0 -7.097,-0.707 -9.91,-1.922c0.125,-3.965 0.766,-8.441 1.789,-13.234l1.918,-9.082l-6.457,0c0.703,-1.789 1.469,-3.453 2.492,-5.051l5.055,0l1.34,-6.332c1.918,-0.703 3.965,-1.215 6.074,-1.535l-1.66,7.867l11.125,0c-0.703,1.789 -1.535,3.516 -2.492,5.051l-9.719,0l-1.922,9.082c-0.703,3.262 -1.469,9.336 -1.469,9.336c0,0 2.493,0.703 4.922,0.703Zm28.137,-25.133c1.984,0 3.84,0.191 5.629,0.578c-0.703,1.727 -1.469,3.324 -2.367,4.86c-1.406,-0.192 -2.813,-0.321 -4.348,-0.321c-2.492,0 -5.242,0.703 -5.242,0.703c0,0 -1.856,6.075 -2.496,9.274l-3.067,14.515l-5.82,0l3.07,-14.515c0.957,-4.735 2.301,-9.211 3.836,-13.238c3.325,-1.153 7.035,-1.856 10.805,-1.856Zm28.715,13.621c0,0 0.445,2.621 -0.574,7.356c-1.024,4.796 -2.559,7.351 -2.559,7.351c-3.328,1.215 -7.035,1.922 -10.809,1.922c-3.773,0 -7.16,-0.707 -9.976,-1.922c0,0 -0.383,-1.726 0.129,-4.988c1.406,0.387 6.457,1.793 10.933,1.793c2.497,0 5.375,-0.703 5.375,-0.703c0,0 0.704,-1.153 1.149,-3.453c0.512,-2.305 0.32,-3.454 0.32,-3.454c0,0 -2.558,-0.703 -5.051,-0.703c-3.902,0 -7.226,-0.64 -10.039,-1.855c0,0 -0.386,-2.879 0.383,-6.524c0.766,-3.707 2.43,-6.585 2.43,-6.585c3.324,-1.153 7.035,-1.856 10.808,-1.856c3.77,0 7.161,0.703 9.973,1.856c0,0.128 0.387,2.046 -0.062,5.179c-1.536,-0.449 -7.165,-1.918 -11,-1.918c-2.493,0 -5.372,0.703 -5.372,0.703c0,0 -0.64,1.024 -0.957,2.621c-0.32,1.598 -0.195,2.622 -0.195,2.622c0,0 2.496,0.64 5.117,0.703c3.774,0 7.164,0.64 9.977,1.855Z" style="fill:#222;fill-rule:nonzero;"/><path d="M79.12,44.539c0,-0.914 -0.066,-1.805 -0.195,-2.68l2.832,-1.687l-0.058,-0.266c-0.434,-2.004 -1.145,-3.902 -2.086,-5.66l-0.137,-0.254l-3.156,0.723c-0.766,-1.235 -1.676,-2.375 -2.703,-3.395l1.285,-3.012l-0.2,-0.175c-1.5,-1.293 -3.168,-2.391 -4.976,-3.246l-0.254,-0.118l-2.137,2.442c-1.386,-0.551 -2.855,-0.934 -4.386,-1.137l-0.727,-3.176l-0.277,-0.015c-0.477,-0.035 -0.965,-0.059 -1.457,-0.059c-1.52,0 -3,0.16 -4.43,0.457l-0.277,0.051l-0.293,3.25c-1.465,0.41 -2.864,0.996 -4.164,1.73l-2.438,-2.132l-0.238,0.156c-1.672,1.09 -3.188,2.402 -4.496,3.894l-0.18,0.207l1.684,2.821c-0.86,1.125 -1.602,2.363 -2.188,3.675l-3.219,-0.289l-0.093,0.266c-0.707,1.863 -1.157,3.852 -1.313,5.918l-0.023,0.266l3.035,1.296c-0.004,0.047 -0.004,0.098 -0.004,0.149c0,1.289 0.137,2.555 0.387,3.766l-2.782,1.656l0.071,0.273c0.55,2.028 1.379,3.926 2.453,5.672l0.14,0.227l3.223,-0.731c0.703,0.973 1.496,1.883 2.371,2.703l-1.277,2.993l0.211,0.179c1.609,1.328 3.41,2.434 5.359,3.258l0.242,0.105l2.176,-2.48c1.059,0.367 2.176,0.641 3.313,0.809l0.726,3.187l0.281,0.02c0.563,0.047 1.149,0.078 1.743,0.078c1.554,0 3.07,-0.168 4.535,-0.481l0.273,-0.058l0.293,-3.254c1.113,-0.317 2.18,-0.738 3.203,-1.242l2.477,2.164l0.234,-0.137c1.813,-1.07 3.449,-2.391 4.875,-3.918l0.196,-0.203l-1.665,-2.789c0.774,-0.938 1.45,-1.965 2.028,-3.047l3.293,0.297l0.105,-0.25c0.821,-1.84 1.391,-3.836 1.672,-5.922l0.035,-0.285l-2.976,-1.27c0.031,-0.429 0.054,-0.871 0.054,-1.32Zm-12.921,0c0,3.156 -2.555,5.711 -5.711,5.715c-3.161,-0.004 -5.711,-2.559 -5.715,-5.715c0.004,-3.152 2.554,-5.703 5.715,-5.715c3.156,0.012 5.711,2.563 5.711,5.715Z" style="fill:#222;fill-rule:nonzero;"/><path d="M103.519,15.5l-0.067,-0.242l-2.253,-0.012c-0.317,-0.934 -0.747,-1.832 -1.258,-2.664l1.351,-1.844l-0.136,-0.191c-0.809,-1.121 -1.778,-2.133 -2.868,-3.004l-0.195,-0.156l-1.84,1.316c-0.832,-0.578 -1.75,-1.059 -2.722,-1.437l0.011,-2.266l-0.23,-0.074c-0.328,-0.098 -0.649,-0.192 -0.992,-0.274c-1.028,-0.242 -2.063,-0.367 -3.086,-0.402l-0.25,-0.004l-0.715,2.16c-1.028,0.047 -2.047,0.215 -3.024,0.496l-1.308,-1.84l-0.238,0.09c-1.317,0.469 -2.559,1.121 -3.692,1.934l-0.195,0.14l0.695,2.18c-0.742,0.617 -1.422,1.313 -2.016,2.09l-2.14,-0.711l-0.137,0.211c-0.773,1.156 -1.402,2.434 -1.844,3.816l-0.066,0.223l1.852,1.363c-0.012,0.02 -0.016,0.043 -0.02,0.059c-0.207,0.867 -0.312,1.726 -0.34,2.578l-2.156,0.684l0.012,0.246c0.054,1.468 0.308,2.902 0.761,4.261l0.075,0.227l2.308,0.012c0.313,0.75 0.692,1.465 1.137,2.148l-1.348,1.828l0.149,0.196c0.886,1.16 1.929,2.207 3.129,3.074l0.191,0.14l1.867,-1.335c0.641,0.402 1.328,0.75 2.047,1.039l-0.016,2.281l0.231,0.074c0.387,0.121 0.777,0.234 1.18,0.332c1.062,0.246 2.121,0.379 3.168,0.402l0.242,0.004l0.718,-2.164c0.782,-0.035 1.547,-0.144 2.297,-0.316l1.336,1.863l0.231,-0.074c1.398,-0.43 2.73,-1.074 3.945,-1.887l0.203,-0.137l-0.679,-2.16c0.648,-0.504 1.257,-1.066 1.804,-1.687l2.184,0.726l0.144,-0.191c0.86,-1.117 1.567,-2.387 2.09,-3.762l0.09,-0.238l-1.816,-1.336c0.086,-0.277 0.16,-0.559 0.23,-0.84c0.141,-0.613 0.238,-1.219 0.293,-1.82l2.191,-0.695l0,-0.239c0,-0.086 0.008,-0.172 0.008,-0.261c-0.004,-1.34 -0.183,-2.661 -0.523,-3.93Zm-10.766,3.941c0,0.301 -0.031,0.602 -0.101,0.903c-0.43,1.8 -2.032,3.015 -3.805,3.015c-0.301,0 -0.598,-0.035 -0.898,-0.109c-1.805,-0.414 -3.012,-2.024 -3.012,-3.797c0,-0.297 0.031,-0.594 0.097,-0.898c0.422,-1.801 2.032,-3.016 3.809,-3.016c0.289,0 0.594,0.031 0.898,0.105c1.793,0.418 3.012,2.032 3.012,3.797Z" style="fill:#222;fill-rule:nonzero;"/></svg> \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/api.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/api.rst
new file mode 100644
index 00000000000..e2acb7400f4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/api.rst
@@ -0,0 +1,428 @@
+.. _api:
+
+API Reference
+=============
+
+.. currentmodule:: attr
+
+``attrs`` works by decorating a class using :func:`attr.s` and then optionally defining attributes on the class using :func:`attr.ib`.
+
+.. note::
+
+ When this documentation speaks about "``attrs`` attributes" it means those attributes that are defined using :func:`attr.ib` in the class body.
+
+What follows is the API explanation, if you'd like a more hands-on introduction, have a look at :doc:`examples`.
+
+
+
+Core
+----
+
+.. autofunction:: attr.s(these=None, repr_ns=None, repr=True, cmp=True, hash=None, init=True, slots=False, frozen=False, str=False)
+
+ .. note::
+
+ ``attrs`` also comes with a serious business alias ``attr.attrs``.
+
+ For example:
+
+ .. doctest::
+
+ >>> import attr
+ >>> @attr.s
+ ... class C(object):
+ ... _private = attr.ib()
+ >>> C(private=42)
+ C(_private=42)
+ >>> class D(object):
+ ... def __init__(self, x):
+ ... self.x = x
+ >>> D(1)
+ <D object at ...>
+ >>> D = attr.s(these={"x": attr.ib()}, init=False)(D)
+ >>> D(1)
+ D(x=1)
+
+
+.. autofunction:: attr.ib
+
+ .. note::
+
+ ``attrs`` also comes with a serious business alias ``attr.attrib``.
+
+ The object returned by :func:`attr.ib` also allows for setting the default and the validator using decorators:
+
+ .. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+ ... @x.validator
+ ... def name_can_be_anything(self, attribute, value):
+ ... if value < 0:
+ ... raise ValueError("x must be positive")
+ ... @y.default
+ ... def name_does_not_matter(self):
+ ... return self.x + 1
+ >>> C(1)
+ C(x=1, y=2)
+ >>> C(-1)
+ Traceback (most recent call last):
+ ...
+ ValueError: x must be positive
+
+.. autoclass:: attr.Attribute
+
+ Instances of this class are frequently used for introspection purposes like:
+
+ - :func:`fields` returns a tuple of them.
+ - Validators get them passed as the first argument.
+
+ .. warning::
+
+ You should never instantiate this class yourself!
+
+ .. doctest::
+
+ >>> import attr
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib()
+ >>> attr.fields(C).x
+ Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None)
+
+
+.. autofunction:: attr.make_class
+
+ This is handy if you want to programmatically create classes.
+
+ For example:
+
+ .. doctest::
+
+ >>> C1 = attr.make_class("C1", ["x", "y"])
+ >>> C1(1, 2)
+ C1(x=1, y=2)
+ >>> C2 = attr.make_class("C2", {"x": attr.ib(default=42),
+ ... "y": attr.ib(default=attr.Factory(list))})
+ >>> C2()
+ C2(x=42, y=[])
+
+
+.. autoclass:: attr.Factory
+
+ For example:
+
+ .. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(default=attr.Factory(list))
+ ... y = attr.ib(default=attr.Factory(
+ ... lambda self: set(self.x),
+ ... takes_self=True)
+ ... )
+ >>> C()
+ C(x=[], y=set())
+ >>> C([1, 2, 3])
+ C(x=[1, 2, 3], y={1, 2, 3})
+
+
+.. autoexception:: attr.exceptions.FrozenInstanceError
+.. autoexception:: attr.exceptions.AttrsAttributeNotFoundError
+.. autoexception:: attr.exceptions.NotAnAttrsClassError
+.. autoexception:: attr.exceptions.DefaultAlreadySetError
+.. autoexception:: attr.exceptions.UnannotatedAttributeError
+
+ For example::
+
+ @attr.s(auto_attribs=True)
+ class C:
+ x: int
+ y = attr.ib()
+
+
+Influencing Initialization
+++++++++++++++++++++++++++
+
+Generally speaking, it's best to keep logic out of your ``__init__``.
+The moment you need a finer control over how your class is instantiated, it's usually best to use a classmethod factory or to apply the `builder pattern <https://en.wikipedia.org/wiki/Builder_pattern>`_.
+
+However, sometimes you need to do that one quick thing after your class is initialized.
+And for that ``attrs`` offers the ``__attrs_post_init__`` hook that is automatically detected and run after ``attrs`` is done initializing your instance:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib()
+ ... y = attr.ib(init=False)
+ ... def __attrs_post_init__(self):
+ ... self.y = self.x + 1
+ >>> C(1)
+ C(x=1, y=2)
+
+Please note that you can't directly set attributes on frozen classes:
+
+.. doctest::
+
+ >>> @attr.s(frozen=True)
+ ... class FrozenBroken(object):
+ ... x = attr.ib()
+ ... y = attr.ib(init=False)
+ ... def __attrs_post_init__(self):
+ ... self.y = self.x + 1
+ >>> FrozenBroken(1)
+ Traceback (most recent call last):
+ ...
+ attr.exceptions.FrozenInstanceError: can't set attribute
+
+If you need to set attributes on a frozen class, you'll have to resort to the :ref:`same trick <how-frozen>` as ``attrs`` and use :meth:`object.__setattr__`:
+
+.. doctest::
+
+ >>> @attr.s(frozen=True)
+ ... class Frozen(object):
+ ... x = attr.ib()
+ ... y = attr.ib(init=False)
+ ... def __attrs_post_init__(self):
+ ... object.__setattr__(self, "y", self.x + 1)
+ >>> Frozen(1)
+ Frozen(x=1, y=2)
+
+
+.. _helpers:
+
+Helpers
+-------
+
+``attrs`` comes with a bunch of helper methods that make working with it easier:
+
+.. autofunction:: attr.fields
+
+ For example:
+
+ .. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+ >>> attr.fields(C)
+ (Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None), Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None))
+ >>> attr.fields(C)[1]
+ Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None)
+ >>> attr.fields(C).y is attr.fields(C)[1]
+ True
+
+
+.. autofunction:: attr.has
+
+ For example:
+
+ .. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... pass
+ >>> attr.has(C)
+ True
+ >>> attr.has(object)
+ False
+
+
+.. autofunction:: attr.asdict
+
+ For example:
+
+ .. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+ >>> attr.asdict(C(1, C(2, 3)))
+ {'x': 1, 'y': {'x': 2, 'y': 3}}
+
+
+.. autofunction:: attr.astuple
+
+ For example:
+
+ .. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+ >>> attr.astuple(C(1,2))
+ (1, 2)
+
+``attrs`` includes some handy helpers for filtering:
+
+.. autofunction:: attr.filters.include
+
+.. autofunction:: attr.filters.exclude
+
+See :ref:`asdict` for examples.
+
+.. autofunction:: attr.evolve
+
+ For example:
+
+ .. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+ >>> i1 = C(1, 2)
+ >>> i1
+ C(x=1, y=2)
+ >>> i2 = attr.evolve(i1, y=3)
+ >>> i2
+ C(x=1, y=3)
+ >>> i1 == i2
+ False
+
+ ``evolve`` creates a new instance using ``__init__``.
+ This fact has several implications:
+
+ * private attributes should be specified without the leading underscore, just like in ``__init__``.
+ * attributes with ``init=False`` can't be set with ``evolve``.
+ * the usual ``__init__`` validators will validate the new values.
+
+.. autofunction:: validate
+
+ For example:
+
+ .. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(validator=attr.validators.instance_of(int))
+ >>> i = C(1)
+ >>> i.x = "1"
+ >>> attr.validate(i)
+ Traceback (most recent call last):
+ ...
+ TypeError: ("'x' must be <type 'int'> (got '1' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, repr=True, cmp=True, hash=None, init=True, type=None), <type 'int'>, '1')
+
+
+Validators can be globally disabled if you want to run them only in development and tests but not in production because you fear their performance impact:
+
+.. autofunction:: set_run_validators
+
+.. autofunction:: get_run_validators
+
+
+.. _api_validators:
+
+Validators
+----------
+
+``attrs`` comes with some common validators in the ``attrs.validators`` module:
+
+
+.. autofunction:: attr.validators.instance_of
+
+
+ For example:
+
+ .. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(validator=attr.validators.instance_of(int))
+ >>> C(42)
+ C(x=42)
+ >>> C("42")
+ Traceback (most recent call last):
+ ...
+ TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, type=None), <type 'int'>, '42')
+ >>> C(None)
+ Traceback (most recent call last):
+ ...
+ TypeError: ("'x' must be <type 'int'> (got None that is a <type 'NoneType'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, repr=True, cmp=True, hash=None, init=True, type=None), <type 'int'>, None)
+
+.. autofunction:: attr.validators.in_
+
+ For example:
+
+ .. doctest::
+
+ >>> import enum
+ >>> class State(enum.Enum):
+ ... ON = "on"
+ ... OFF = "off"
+ >>> @attr.s
+ ... class C(object):
+ ... state = attr.ib(validator=attr.validators.in_(State))
+ ... val = attr.ib(validator=attr.validators.in_([1, 2, 3]))
+ >>> C(State.ON, 1)
+ C(state=<State.ON: 'on'>, val=1)
+ >>> C("on", 1)
+ Traceback (most recent call last):
+ ...
+ ValueError: 'state' must be in <enum 'State'> (got 'on')
+ >>> C(State.ON, 4)
+ Traceback (most recent call last):
+ ...
+ ValueError: 'val' must be in [1, 2, 3] (got 4)
+
+.. autofunction:: attr.validators.provides
+
+.. autofunction:: attr.validators.and_
+
+ For convenience, it's also possible to pass a list to :func:`attr.ib`'s validator argument.
+
+ Thus the following two statements are equivalent::
+
+ x = attr.ib(validator=attr.validators.and_(v1, v2, v3))
+ x = attr.ib(validator=[v1, v2, v3])
+
+.. autofunction:: attr.validators.optional
+
+ For example:
+
+ .. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(int)))
+ >>> C(42)
+ C(x=42)
+ >>> C("42")
+ Traceback (most recent call last):
+ ...
+ TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, type=None), <type 'int'>, '42')
+ >>> C(None)
+ C(x=None)
+
+
+Converters
+----------
+
+.. autofunction:: attr.converters.optional
+
+ For example:
+
+ .. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(convert=attr.converters.optional(int))
+ >>> C(None)
+ C(x=None)
+ >>> C(42)
+ C(x=42)
+
+
+Deprecated APIs
+---------------
+
+The serious business aliases used to be called ``attr.attributes`` and ``attr.attr``.
+There are no plans to remove them but they shouldn't be used in new code.
+
+.. autofunction:: assoc
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/backward-compatibility.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/backward-compatibility.rst
new file mode 100644
index 00000000000..52559f8d450
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/backward-compatibility.rst
@@ -0,0 +1,19 @@
+Backward Compatibility
+======================
+
+.. currentmodule:: attr
+
+``attrs`` has a very strong backward compatibility policy that is inspired by the policy of the `Twisted framework <https://twistedmatrix.com/trac/wiki/CompatibilityPolicy>`_.
+
+Put simply, you shouldn't ever be afraid to upgrade ``attrs`` if you're only using its public APIs.
+If there will ever be a need to break compatibility, it will be announced in the :doc:`changelog` and raise a ``DeprecationWarning`` for a year (if possible) before it's finally really broken.
+
+
+.. _exemption:
+
+.. warning::
+
+ The structure of the :class:`attr.Attribute` class is exempt from this rule.
+ It *will* change in the future, but since it should be considered read-only, that shouldn't matter.
+
+ However if you intend to build extensions on top of ``attrs`` you have to anticipate that.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/changelog.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/changelog.rst
new file mode 100644
index 00000000000..565b0521d0c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/changelog.rst
@@ -0,0 +1 @@
+.. include:: ../CHANGELOG.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/conf.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/conf.py
new file mode 100644
index 00000000000..1cdb07c1f79
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/conf.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+
+import codecs
+import os
+import re
+
+
+def read(*parts):
+ """
+ Build an absolute path from *parts* and and return the contents of the
+ resulting file. Assume UTF-8 encoding.
+ """
+ here = os.path.abspath(os.path.dirname(__file__))
+ with codecs.open(os.path.join(here, *parts), "rb", "utf-8") as f:
+ return f.read()
+
+
+def find_version(*file_paths):
+ """
+ Build a path from *file_paths* and search for a ``__version__``
+ string inside.
+ """
+ version_file = read(*file_paths)
+ version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
+ version_file, re.M)
+ if version_match:
+ return version_match.group(1)
+ raise RuntimeError("Unable to find version string.")
+
+
+# -- General configuration ------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.intersphinx',
+ 'sphinx.ext.todo',
+]
+
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'attrs'
+copyright = u'2015, Hynek Schlawack'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+release = find_version("../src/attr/__init__.py")
+version = release.rsplit(u".", 1)[0]
+# The full version, including alpha/beta/rc tags.
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+add_function_parentheses = True
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+
+html_theme = "alabaster"
+html_theme_options = {
+ "font_family": '"Avenir Next", Calibri, "PT Sans", sans-serif',
+ "head_font_family": '"Avenir Next", Calibri, "PT Sans", sans-serif',
+ "font_size": "18px",
+ "page_width": "980px",
+}
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+html_logo = "_static/attrs_logo.svg"
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+# html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If false, no module index is generated.
+html_domain_indices = True
+
+# If false, no index is generated.
+html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+html_show_sourcelink = False
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+# html_use_opensearch = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'attrsdoc'
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'attrs', u'attrs Documentation',
+ [u'Hynek Schlawack'], 1)
+]
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'attrs', u'attrs Documentation',
+ u'Hynek Schlawack', 'attrs', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+intersphinx_mapping = {
+ "https://docs.python.org/3": None,
+}
+
+# Allow non-local URIs so we can have images in CHANGELOG etc.
+suppress_warnings = ['image.nonlocal_uri']
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/contributing.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/contributing.rst
new file mode 100644
index 00000000000..1d519c38151
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/contributing.rst
@@ -0,0 +1,5 @@
+.. _contributing:
+
+.. include:: ../CONTRIBUTING.rst
+
+.. include:: ../CODE_OF_CONDUCT.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/docutils.conf b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/docutils.conf
new file mode 100644
index 00000000000..db8ca82c747
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/docutils.conf
@@ -0,0 +1,3 @@
+[parsers]
+[restructuredtext parser]
+smart_quotes=yes
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/examples.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/examples.rst
new file mode 100644
index 00000000000..4432e8fcb53
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/examples.rst
@@ -0,0 +1,705 @@
+.. _examples:
+
+``attrs`` by Example
+====================
+
+
+Basics
+------
+
+The simplest possible usage is:
+
+.. doctest::
+
+ >>> import attr
+ >>> @attr.s
+ ... class Empty(object):
+ ... pass
+ >>> Empty()
+ Empty()
+ >>> Empty() == Empty()
+ True
+ >>> Empty() is Empty()
+ False
+
+So in other words: ``attrs`` is useful even without actual attributes!
+
+But you'll usually want some data on your classes, so let's add some:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class Coordinates(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+
+By default, all features are added, so you immediately have a fully functional data class with a nice ``repr`` string and comparison methods.
+
+.. doctest::
+
+ >>> c1 = Coordinates(1, 2)
+ >>> c1
+ Coordinates(x=1, y=2)
+ >>> c2 = Coordinates(x=2, y=1)
+ >>> c2
+ Coordinates(x=2, y=1)
+ >>> c1 == c2
+ False
+
+As shown, the generated ``__init__`` method allows for both positional and keyword arguments.
+
+If playful naming turns you off, ``attrs`` comes with serious business aliases:
+
+.. doctest::
+
+ >>> from attr import attrs, attrib
+ >>> @attrs
+ ... class SeriousCoordinates(object):
+ ... x = attrib()
+ ... y = attrib()
+ >>> SeriousCoordinates(1, 2)
+ SeriousCoordinates(x=1, y=2)
+ >>> attr.fields(Coordinates) == attr.fields(SeriousCoordinates)
+ True
+
+For private attributes, ``attrs`` will strip the leading underscores for keyword arguments:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... _x = attr.ib()
+ >>> C(x=1)
+ C(_x=1)
+
+If you want to initialize your private attributes yourself, you can do that too:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... _x = attr.ib(init=False, default=42)
+ >>> C()
+ C(_x=42)
+ >>> C(23)
+ Traceback (most recent call last):
+ ...
+ TypeError: __init__() takes exactly 1 argument (2 given)
+
+An additional way of defining attributes is supported too.
+This is useful in times when you want to enhance classes that are not yours (nice ``__repr__`` for Django models anyone?):
+
+.. doctest::
+
+ >>> class SomethingFromSomeoneElse(object):
+ ... def __init__(self, x):
+ ... self.x = x
+ >>> SomethingFromSomeoneElse = attr.s(
+ ... these={
+ ... "x": attr.ib()
+ ... }, init=False)(SomethingFromSomeoneElse)
+ >>> SomethingFromSomeoneElse(1)
+ SomethingFromSomeoneElse(x=1)
+
+
+`Subclassing is bad for you <https://www.youtube.com/watch?v=3MNVP9-hglc>`_, but ``attrs`` will still do what you'd hope for:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class A(object):
+ ... a = attr.ib()
+ ... def get_a(self):
+ ... return self.a
+ >>> @attr.s
+ ... class B(object):
+ ... b = attr.ib()
+ >>> @attr.s
+ ... class C(B, A):
+ ... c = attr.ib()
+ >>> i = C(1, 2, 3)
+ >>> i
+ C(a=1, b=2, c=3)
+ >>> i == C(1, 2, 3)
+ True
+ >>> i.get_a()
+ 1
+
+The order of the attributes is defined by the `MRO <https://www.python.org/download/releases/2.3/mro/>`_.
+
+In Python 3, classes defined within other classes are `detected <https://www.python.org/dev/peps/pep-3155/>`_ and reflected in the ``__repr__``.
+In Python 2 though, it's impossible.
+Therefore ``@attr.s`` comes with the ``repr_ns`` option to set it manually:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... @attr.s(repr_ns="C")
+ ... class D(object):
+ ... pass
+ >>> C.D()
+ C.D()
+
+``repr_ns`` works on both Python 2 and 3.
+On Python 3 it overrides the implicit detection.
+
+
+.. _asdict:
+
+Converting to Collections Types
+-------------------------------
+
+When you have a class with data, it often is very convenient to transform that class into a :class:`dict` (for example if you want to serialize it to JSON):
+
+.. doctest::
+
+ >>> attr.asdict(Coordinates(x=1, y=2))
+ {'x': 1, 'y': 2}
+
+Some fields cannot or should not be transformed.
+For that, :func:`attr.asdict` offers a callback that decides whether an attribute should be included:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class UserList(object):
+ ... users = attr.ib()
+ >>> @attr.s
+ ... class User(object):
+ ... email = attr.ib()
+ ... password = attr.ib()
+ >>> attr.asdict(UserList([User("jane@doe.invalid", "s33kred"),
+ ... User("joe@doe.invalid", "p4ssw0rd")]),
+ ... filter=lambda attr, value: attr.name != "password")
+ {'users': [{'email': 'jane@doe.invalid'}, {'email': 'joe@doe.invalid'}]}
+
+For the common case where you want to :func:`include <attr.filters.include>` or :func:`exclude <attr.filters.exclude>` certain types or attributes, ``attrs`` ships with a few helpers:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class User(object):
+ ... login = attr.ib()
+ ... password = attr.ib()
+ ... id = attr.ib()
+ >>> attr.asdict(
+ ... User("jane", "s33kred", 42),
+ ... filter=attr.filters.exclude(attr.fields(User).password, int))
+ {'login': 'jane'}
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+ ... z = attr.ib()
+ >>> attr.asdict(C("foo", "2", 3),
+ ... filter=attr.filters.include(int, attr.fields(C).x))
+ {'x': 'foo', 'z': 3}
+
+Other times, all you want is a tuple and ``attrs`` won't let you down:
+
+.. doctest::
+
+ >>> import sqlite3
+ >>> import attr
+ >>> @attr.s
+ ... class Foo:
+ ... a = attr.ib()
+ ... b = attr.ib()
+ >>> foo = Foo(2, 3)
+ >>> with sqlite3.connect(":memory:") as conn:
+ ... c = conn.cursor()
+ ... c.execute("CREATE TABLE foo (x INTEGER PRIMARY KEY ASC, y)") #doctest: +ELLIPSIS
+ ... c.execute("INSERT INTO foo VALUES (?, ?)", attr.astuple(foo)) #doctest: +ELLIPSIS
+ ... foo2 = Foo(*c.execute("SELECT x, y FROM foo").fetchone())
+ <sqlite3.Cursor object at ...>
+ <sqlite3.Cursor object at ...>
+ >>> foo == foo2
+ True
+
+
+
+
+Defaults
+--------
+
+Sometimes you want to have default values for your initializer.
+And sometimes you even want mutable objects as default values (ever used accidentally ``def f(arg=[])``?).
+``attrs`` has you covered in both cases:
+
+.. doctest::
+
+ >>> import collections
+ >>> @attr.s
+ ... class Connection(object):
+ ... socket = attr.ib()
+ ... @classmethod
+ ... def connect(cls, db_string):
+ ... # ... connect somehow to db_string ...
+ ... return cls(socket=42)
+ >>> @attr.s
+ ... class ConnectionPool(object):
+ ... db_string = attr.ib()
+ ... pool = attr.ib(default=attr.Factory(collections.deque))
+ ... debug = attr.ib(default=False)
+ ... def get_connection(self):
+ ... try:
+ ... return self.pool.pop()
+ ... except IndexError:
+ ... if self.debug:
+ ... print("New connection!")
+ ... return Connection.connect(self.db_string)
+ ... def free_connection(self, conn):
+ ... if self.debug:
+ ... print("Connection returned!")
+ ... self.pool.appendleft(conn)
+ ...
+ >>> cp = ConnectionPool("postgres://localhost")
+ >>> cp
+ ConnectionPool(db_string='postgres://localhost', pool=deque([]), debug=False)
+ >>> conn = cp.get_connection()
+ >>> conn
+ Connection(socket=42)
+ >>> cp.free_connection(conn)
+ >>> cp
+ ConnectionPool(db_string='postgres://localhost', pool=deque([Connection(socket=42)]), debug=False)
+
+More information on why class methods for constructing objects are awesome can be found in this insightful `blog post <http://as.ynchrono.us/2014/12/asynchronous-object-initialization.html>`_.
+
+Default factories can also be set using a decorator.
+The method receives the partially initialized instance which enables you to base a default value on other attributes:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(default=1)
+ ... y = attr.ib()
+ ... @y.default
+ ... def name_does_not_matter(self):
+ ... return self.x + 1
+ >>> C()
+ C(x=1, y=2)
+
+
+.. _examples_validators:
+
+Validators
+----------
+
+Although your initializers should do as little as possible (ideally: just initialize your instance according to the arguments!), it can come in handy to do some kind of validation on the arguments.
+
+``attrs`` offers two ways to define validators for each attribute and it's up to you to choose which one suites better your style and project.
+
+
+Decorator
+~~~~~~~~~
+
+The more straightforward way is by using the attribute's ``validator`` method as a decorator.
+The method has to accept three arguments:
+
+#. the *instance* that's being validated (aka ``self``),
+#. the *attribute* that it's validating, and finally
+#. the *value* that is passed for it.
+
+If the value does not pass the validator's standards, it just raises an appropriate exception.
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib()
+ ... @x.validator
+ ... def check(self, attribute, value):
+ ... if value > 42:
+ ... raise ValueError("x must be smaller or equal to 42")
+ >>> C(42)
+ C(x=42)
+ >>> C(43)
+ Traceback (most recent call last):
+ ...
+ ValueError: x must be smaller or equal to 42
+
+
+Callables
+~~~~~~~~~
+
+If you want to re-use your validators, you should have a look at the ``validator`` argument to :func:`attr.ib()`.
+
+It takes either a callable or a list of callables (usually functions) and treats them as validators that receive the same arguments as with the decorator approach.
+
+Since the validators runs *after* the instance is initialized, you can refer to other attributes while validating:
+
+.. doctest::
+
+ >>> def x_smaller_than_y(instance, attribute, value):
+ ... if value >= instance.y:
+ ... raise ValueError("'x' has to be smaller than 'y'!")
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(validator=[attr.validators.instance_of(int),
+ ... x_smaller_than_y])
+ ... y = attr.ib()
+ >>> C(x=3, y=4)
+ C(x=3, y=4)
+ >>> C(x=4, y=3)
+ Traceback (most recent call last):
+ ...
+ ValueError: 'x' has to be smaller than 'y'!
+
+This example also shows of some syntactic sugar for using the :func:`attr.validators.and_` validator: if you pass a list, all validators have to pass.
+
+``attrs`` won't intercept your changes to those attributes but you can always call :func:`attr.validate` on any instance to verify that it's still valid:
+
+.. doctest::
+
+ >>> i = C(4, 5)
+ >>> i.x = 5 # works, no magic here
+ >>> attr.validate(i)
+ Traceback (most recent call last):
+ ...
+ ValueError: 'x' has to be smaller than 'y'!
+
+``attrs`` ships with a bunch of validators, make sure to :ref:`check them out <api_validators>` before writing your own:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(validator=attr.validators.instance_of(int))
+ >>> C(42)
+ C(x=42)
+ >>> C("42")
+ Traceback (most recent call last):
+ ...
+ TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attribute(name='x', default=NOTHING, factory=NOTHING, validator=<instance_of validator for type <type 'int'>>, type=None), <type 'int'>, '42')
+
+Of course you can mix and match the two approaches at your convenience:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(validator=attr.validators.instance_of(int))
+ ... @x.validator
+ ... def fits_byte(self, attribute, value):
+ ... if not 0 < value < 256:
+ ... raise ValueError("value out of bounds")
+ >>> C(128)
+ C(x=128)
+ >>> C("128")
+ Traceback (most recent call last):
+ ...
+ TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=[<instance_of validator for type <class 'int'>>, <function fits_byte at 0x10fd7a0d0>], repr=True, cmp=True, hash=True, init=True, convert=None, metadata=mappingproxy({}), type=None), <class 'int'>, '128')
+ >>> C(256)
+ Traceback (most recent call last):
+ ...
+ ValueError: value out of bounds
+
+And finally you can disable validators globally:
+
+ >>> attr.set_run_validators(False)
+ >>> C("128")
+ C(x='128')
+ >>> attr.set_run_validators(True)
+ >>> C("128")
+ Traceback (most recent call last):
+ ...
+ TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=[<instance_of validator for type <class 'int'>>, <function fits_byte at 0x10fd7a0d0>], repr=True, cmp=True, hash=True, init=True, convert=None, metadata=mappingproxy({}), type=None), <class 'int'>, '128')
+
+
+Conversion
+----------
+
+Attributes can have a ``convert`` function specified, which will be called with the attribute's passed-in value to get a new value to use.
+This can be useful for doing type-conversions on values that you don't want to force your callers to do.
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(convert=int)
+ >>> o = C("1")
+ >>> o.x
+ 1
+
+Converters are run *before* validators, so you can use validators to check the final form of the value.
+
+.. doctest::
+
+ >>> def validate_x(instance, attribute, value):
+ ... if value < 0:
+ ... raise ValueError("x must be be at least 0.")
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(convert=int, validator=validate_x)
+ >>> o = C("0")
+ >>> o.x
+ 0
+ >>> C("-1")
+ Traceback (most recent call last):
+ ...
+ ValueError: x must be be at least 0.
+
+
+.. _metadata:
+
+Metadata
+--------
+
+All ``attrs`` attributes may include arbitrary metadata in the form of a read-only dictionary.
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib(metadata={'my_metadata': 1})
+ >>> attr.fields(C).x.metadata
+ mappingproxy({'my_metadata': 1})
+ >>> attr.fields(C).x.metadata['my_metadata']
+ 1
+
+Metadata is not used by ``attrs``, and is meant to enable rich functionality in third-party libraries.
+The metadata dictionary follows the normal dictionary rules: keys need to be hashable, and both keys and values are recommended to be immutable.
+
+If you're the author of a third-party library with ``attrs`` integration, please see :ref:`Extending Metadata <extending_metadata>`.
+
+
+Types
+-----
+
+``attrs`` also allows you to associate a type with an attribute using either the *type* argument to :func:`attr.ib` or -- as of Python 3.6 -- using `PEP 526 <https://www.python.org/dev/peps/pep-0526/>`_-annotations:
+
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C:
+ ... x = attr.ib(type=int)
+ ... y: int = attr.ib()
+ >>> attr.fields(C).x.type
+ <class 'int'>
+ >>> attr.fields(C).y.type
+ <class 'int'>
+
+If you don't mind annotating *all* attributes, you can even drop the :func:`attr.ib` and assign default values instead:
+
+.. doctest::
+
+ >>> import typing
+ >>> @attr.s(auto_attribs=True)
+ ... class AutoC:
+ ... cls_var: typing.ClassVar[int] = 5 # this one is ignored
+ ... l: typing.List[int] = attr.Factory(list)
+ ... x: int = 1
+ ... foo: str = attr.ib(
+ ... default="every attrib needs a type if auto_attribs=True"
+ ... )
+ ... bar: typing.Any = None
+ >>> attr.fields(AutoC).l.type
+ typing.List[int]
+ >>> attr.fields(AutoC).x.type
+ <class 'int'>
+ >>> attr.fields(AutoC).foo.type
+ <class 'str'>
+ >>> attr.fields(AutoC).bar.type
+ typing.Any
+ >>> AutoC()
+ AutoC(l=[], x=1, foo='every attrib needs a type if auto_attribs=True', bar=None)
+ >>> AutoC.cls_var
+ 5
+
+
+.. warning::
+
+ ``attrs`` itself doesn't have any features that work on top of type metadata *yet*.
+ However it's useful for writing your own validators or serialization frameworks.
+
+
+.. _slots:
+
+Slots
+-----
+
+By default, instances of classes have a dictionary for attribute storage.
+This wastes space for objects having very few data attributes.
+The space consumption can become significant when creating large numbers of instances.
+
+Normal Python classes can avoid using a separate dictionary for each instance of a class by `defining <https://docs.python.org/3/reference/datamodel.html#slots>`_ ``__slots__``.
+For ``attrs`` classes it's enough to set ``slots=True``:
+
+.. doctest::
+
+ >>> @attr.s(slots=True)
+ ... class Coordinates(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+
+
+.. note::
+
+ ``attrs`` slot classes can inherit from other classes just like non-slot classes, but some of the benefits of slot classes are lost if you do that.
+ If you must inherit from other classes, try to inherit only from other slot classes.
+
+Slot classes are a little different than ordinary, dictionary-backed classes:
+
+- Assigning to a non-existent attribute of an instance will result in an ``AttributeError`` being raised.
+ Depending on your needs, this might be a good thing since it will let you catch typos early.
+ This is not the case if your class inherits from any non-slot classes.
+
+ .. doctest::
+
+ >>> @attr.s(slots=True)
+ ... class Coordinates(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+ ...
+ >>> c = Coordinates(x=1, y=2)
+ >>> c.z = 3
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'Coordinates' object has no attribute 'z'
+
+- Since non-slot classes cannot be turned into slot classes after they have been created, ``attr.s(slots=True)`` will *replace* the class it is applied to with a copy.
+ In almost all cases this isn't a problem, but we mention it for the sake of completeness.
+
+ * One notable problem is that certain metaclass features like ``__init_subclass__`` do not work with slot classes.
+
+- Using :mod:`pickle` with slot classes requires pickle protocol 2 or greater.
+ Python 2 uses protocol 0 by default so the protocol needs to be specified.
+ Python 3 uses protocol 3 by default.
+ You can support protocol 0 and 1 by implementing :meth:`__getstate__ <object.__getstate__>` and :meth:`__setstate__ <object.__setstate__>` methods yourself.
+ Those methods are created for frozen slot classes because they won't pickle otherwise.
+ `Think twice <https://www.youtube.com/watch?v=7KnfGDajDQw>`_ before using :mod:`pickle` though.
+
+- As always with slot classes, you must specify a ``__weakref__`` slot if you wish for the class to be weak-referenceable.
+ Here's how it looks using ``attrs``:
+
+ .. doctest::
+
+ >>> import weakref
+ >>> @attr.s(slots=True)
+ ... class C(object):
+ ... __weakref__ = attr.ib(init=False, hash=False, repr=False, cmp=False)
+ ... x = attr.ib()
+ >>> c = C(1)
+ >>> weakref.ref(c)
+ <weakref at 0x...; to 'C' at 0x...>
+
+All in all, setting ``slots=True`` is usually a very good idea.
+
+
+Immutability
+------------
+
+Sometimes you have instances that shouldn't be changed after instantiation.
+Immutability is especially popular in functional programming and is generally a very good thing.
+If you'd like to enforce it, ``attrs`` will try to help:
+
+.. doctest::
+
+ >>> @attr.s(frozen=True)
+ ... class C(object):
+ ... x = attr.ib()
+ >>> i = C(1)
+ >>> i.x = 2
+ Traceback (most recent call last):
+ ...
+ attr.exceptions.FrozenInstanceError: can't set attribute
+ >>> i.x
+ 1
+
+Please note that true immutability is impossible in Python but it will :ref:`get <how-frozen>` you 99% there.
+By themselves, immutable classes are useful for long-lived objects that should never change; like configurations for example.
+
+In order to use them in regular program flow, you'll need a way to easily create new instances with changed attributes.
+In Clojure that function is called `assoc <https://clojuredocs.org/clojure.core/assoc>`_ and ``attrs`` shamelessly imitates it: :func:`attr.evolve`:
+
+.. doctest::
+
+ >>> @attr.s(frozen=True)
+ ... class C(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+ >>> i1 = C(1, 2)
+ >>> i1
+ C(x=1, y=2)
+ >>> i2 = attr.evolve(i1, y=3)
+ >>> i2
+ C(x=1, y=3)
+ >>> i1 == i2
+ False
+
+
+Other Goodies
+-------------
+
+Sometimes you may want to create a class programmatically.
+``attrs`` won't let you down and gives you :func:`attr.make_class` :
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C1(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+ >>> C2 = attr.make_class("C2", ["x", "y"])
+ >>> attr.fields(C1) == attr.fields(C2)
+ True
+
+You can still have power over the attributes if you pass a dictionary of name: ``attr.ib`` mappings and can pass arguments to ``@attr.s``:
+
+.. doctest::
+
+ >>> C = attr.make_class("C", {"x": attr.ib(default=42),
+ ... "y": attr.ib(default=attr.Factory(list))},
+ ... repr=False)
+ >>> i = C()
+ >>> i # no repr added!
+ <__main__.C object at ...>
+ >>> i.x
+ 42
+ >>> i.y
+ []
+
+If you need to dynamically make a class with :func:`attr.make_class` and it needs to be a subclass of something else than ``object``, use the ``bases`` argument:
+
+.. doctest::
+
+ >>> class D(object):
+ ... def __eq__(self, other):
+ ... return True # arbitrary example
+ >>> C = attr.make_class("C", {}, bases=(D,), cmp=False)
+ >>> isinstance(C(), D)
+ True
+
+Sometimes, you want to have your class's ``__init__`` method do more than just
+the initialization, validation, etc. that gets done for you automatically when
+using ``@attr.s``.
+To do this, just define a ``__attrs_post_init__`` method in your class.
+It will get called at the end of the generated ``__init__`` method.
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib()
+ ... y = attr.ib()
+ ... z = attr.ib(init=False)
+ ...
+ ... def __attrs_post_init__(self):
+ ... self.z = self.x + self.y
+ >>> obj = C(x=1, y=2)
+ >>> obj
+ C(x=1, y=2, z=3)
+
+Finally, you can exclude single attributes from certain methods:
+
+.. doctest::
+
+ >>> @attr.s
+ ... class C(object):
+ ... user = attr.ib()
+ ... password = attr.ib(repr=False)
+ >>> C("me", "s3kr3t")
+ C(user='me')
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/extending.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/extending.rst
new file mode 100644
index 00000000000..d460ee9b8c0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/extending.rst
@@ -0,0 +1,112 @@
+.. _extending:
+
+Extending
+=========
+
+Each ``attrs``-decorated class has a ``__attrs_attrs__`` class attribute.
+It is a tuple of :class:`attr.Attribute` carrying meta-data about each attribute.
+
+So it is fairly simple to build your own decorators on top of ``attrs``:
+
+.. doctest::
+
+ >>> import attr
+ >>> def print_attrs(cls):
+ ... print(cls.__attrs_attrs__)
+ >>> @print_attrs
+ ... @attr.s
+ ... class C(object):
+ ... a = attr.ib()
+ (Attribute(name='a', default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata=mappingproxy({}), type=None),)
+
+
+.. warning::
+
+ The :func:`attr.s` decorator **must** be applied first because it puts ``__attrs_attrs__`` in place!
+ That means that is has to come *after* your decorator because::
+
+ @a
+ @b
+ def f():
+ pass
+
+ is just `syntactic sugar <https://en.wikipedia.org/wiki/Syntactic_sugar>`_ for::
+
+ def original_f():
+ pass
+
+ f = a(b(original_f))
+
+
+Wrapping the Decorator
+----------------------
+
+A more elegant way can be to wrap ``attrs`` altogether and build a class `DSL <https://en.wikipedia.org/wiki/Domain-specific_language>`_ on top of it.
+
+An example for that is the package `environ_config <https://github.com/hynek/environ_config>`_ that uses ``attrs`` under the hood to define environment-based configurations declaratively without exposing ``attrs`` APIs at all.
+
+
+Types
+-----
+
+``attrs`` offers two ways of attaching type information to attributes:
+
+- `PEP 526 <https://www.python.org/dev/peps/pep-0526/>`_ annotations on Python 3.6 and later,
+- and the *type* argument to :func:`attr.ib`.
+
+This information is available to you:
+
+.. doctest::
+
+ >>> import attr
+ >>> @attr.s
+ ... class C(object):
+ ... x: int = attr.ib()
+ ... y = attr.ib(type=str)
+ >>> attr.fields(C).x.type
+ <class 'int'>
+ >>> attr.fields(C).y.type
+ <class 'str'>
+
+Currently, ``attrs`` doesn't do anything with this information but it's very useful if you'd like to write your own validators or serializers!
+
+
+.. _extending_metadata:
+
+Metadata
+--------
+
+If you're the author of a third-party library with ``attrs`` integration, you may want to take advantage of attribute metadata.
+
+Here are some tips for effective use of metadata:
+
+- Try making your metadata keys and values immutable.
+ This keeps the entire ``Attribute`` instances immutable too.
+
+- To avoid metadata key collisions, consider exposing your metadata keys from your modules.::
+
+ from mylib import MY_METADATA_KEY
+
+ @attr.s
+ class C(object):
+ x = attr.ib(metadata={MY_METADATA_KEY: 1})
+
+ Metadata should be composable, so consider supporting this approach even if you decide implementing your metadata in one of the following ways.
+
+- Expose ``attr.ib`` wrappers for your specific metadata.
+ This is a more graceful approach if your users don't require metadata from other libraries.
+
+ .. doctest::
+
+ >>> MY_TYPE_METADATA = '__my_type_metadata'
+ >>>
+ >>> def typed(cls, default=attr.NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True, convert=None, metadata={}):
+ ... metadata = dict() if not metadata else metadata
+ ... metadata[MY_TYPE_METADATA] = cls
+ ... return attr.ib(default, validator, repr, cmp, hash, init, convert, metadata)
+ >>>
+ >>> @attr.s
+ ... class C(object):
+ ... x = typed(int, default=1, init=False)
+ >>> attr.fields(C).x.metadata[MY_TYPE_METADATA]
+ <class 'int'>
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/how-does-it-work.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/how-does-it-work.rst
new file mode 100644
index 00000000000..c20becd1f78
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/how-does-it-work.rst
@@ -0,0 +1,79 @@
+.. _how:
+
+How Does It Work?
+=================
+
+
+Boilerplate
+-----------
+
+``attrs`` certainly isn't the first library that aims to simplify class definition in Python.
+But its **declarative** approach combined with **no runtime overhead** lets it stand out.
+
+Once you apply the ``@attr.s`` decorator to a class, ``attrs`` searches the class object for instances of ``attr.ib``\ s.
+Internally they're a representation of the data passed into ``attr.ib`` along with a counter to preserve the order of the attributes.
+
+In order to ensure that sub-classing works as you'd expect it to work, ``attrs`` also walks the class hierarchy and collects the attributes of all super-classes.
+Please note that ``attrs`` does *not* call ``super()`` *ever*.
+It will write dunder methods to work on *all* of those attributes which also has performance benefits due to fewer function calls.
+
+Once ``attrs`` knows what attributes it has to work on, it writes the requested dunder methods and -- depending on whether you wish to have ``__slots__`` -- creates a new class for you (``slots=True``) or attaches them to the original class (``slots=False``).
+While creating new classes is more elegant, we've run into several edge cases surrounding metaclasses that make it impossible to go this route unconditionally.
+
+To be very clear: if you define a class with a single attribute without a default value, the generated ``__init__`` will look *exactly* how you'd expect:
+
+.. doctest::
+
+ >>> import attr, inspect
+ >>> @attr.s
+ ... class C(object):
+ ... x = attr.ib()
+ >>> print(inspect.getsource(C.__init__))
+ def __init__(self, x):
+ self.x = x
+ <BLANKLINE>
+
+No magic, no meta programming, no expensive introspection at runtime.
+
+****
+
+Everything until this point happens exactly *once* when the class is defined.
+As soon as a class is done, it's done.
+And it's just a regular Python class like any other, except for a single ``__attrs_attrs__`` attribute that can be used for introspection or for writing your own tools and decorators on top of ``attrs`` (like :func:`attr.asdict`).
+
+And once you start instantiating your classes, ``attrs`` is out of your way completely.
+
+This **static** approach was very much a design goal of ``attrs`` and what I strongly believe makes it distinct.
+
+
+.. _how-frozen:
+
+Immutability
+------------
+
+In order to give you immutability, ``attrs`` will attach a ``__setattr__`` method to your class that raises a :exc:`attr.exceptions.FrozenInstanceError` whenever anyone tries to set an attribute.
+
+To circumvent that ourselves in ``__init__``, ``attrs`` uses (an aggressively cached) :meth:`object.__setattr__` to set your attributes.
+This is (still) slower than a plain assignment:
+
+.. code-block:: none
+
+ $ pyperf timeit --rigorous \
+ -s "import attr; C = attr.make_class('C', ['x', 'y', 'z'], slots=True)" \
+ "C(1, 2, 3)"
+ ........................................
+ Median +- std dev: 378 ns +- 12 ns
+
+ $ pyperf timeit --rigorous \
+ -s "import attr; C = attr.make_class('C', ['x', 'y', 'z'], slots=True, frozen=True)" \
+ "C(1, 2, 3)"
+ ........................................
+ Median +- std dev: 676 ns +- 16 ns
+
+So on a standard notebook the difference is about 300 nanoseconds (1 second is 1,000,000,000 nanoseconds).
+It's certainly something you'll feel in a hot loop but shouldn't matter in normal code.
+Pick what's more important to you.
+
+****
+
+Once constructed, frozen instances don't differ in any way from regular ones except that you cannot change its attributes.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/index.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/index.rst
new file mode 100644
index 00000000000..bb24cd773fb
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/index.rst
@@ -0,0 +1,80 @@
+======================================
+``attrs``: Classes Without Boilerplate
+======================================
+
+Release v\ |release| (:doc:`What's new? <changelog>`).
+
+.. include:: ../README.rst
+ :start-after: teaser-begin
+ :end-before: -spiel-end-
+
+
+Getting Started
+===============
+
+``attrs`` is a Python-only package `hosted on PyPI <https://pypi.org/project/attrs/>`_.
+The recommended installation method is `pip <https://pip.pypa.io/en/stable/>`_-installing into a `virtualenv <https://hynek.me/articles/virtualenv-lives/>`_:
+
+.. code-block:: console
+
+ $ pip install attrs
+
+The next three steps should bring you up and running in no time:
+
+- :doc:`overview` will show you a simple example of ``attrs`` in action and introduce you to its philosophy.
+ Afterwards, you can start writing your own classes, understand what drives ``attrs``'s design, and know what ``@attr.s`` and ``attr.ib()`` stand for.
+- :doc:`examples` will give you a comprehensive tour of ``attrs``'s features.
+ After reading, you will know about our advanced features and how to use them.
+- Finally :doc:`why` gives you a rundown of potential alternatives and why we think ``attrs`` is superior.
+ Yes, we've heard about ``namedtuple``\ s!
+
+
+If you need any help while getting started, feel free to use the ``python-attrs`` tag on `StackOverflow <https://stackoverflow.com/questions/tagged/python-attrs>`_ and someone will surely help you out!
+
+
+Day-to-Day Usage
+================
+
+- Once you're comfortable with the concepts, our :doc:`api` contains all information you need to use ``attrs`` to its fullest.
+- ``attrs`` is built for extension from the ground up.
+ :doc:`extending` will show you the affordances it offers and how to make it a building block of your own projects.
+
+
+.. include:: ../README.rst
+ :start-after: -testimonials-
+ :end-before: -end-
+
+.. include:: ../README.rst
+ :start-after: -project-information-
+
+.. toctree::
+ :maxdepth: 1
+
+ license
+ backward-compatibility
+ contributing
+ changelog
+
+
+----
+
+
+Full Table of Contents
+======================
+
+.. toctree::
+ :maxdepth: 2
+
+ overview
+ why
+ examples
+ api
+ extending
+ how-does-it-work
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`search`
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/license.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/license.rst
new file mode 100644
index 00000000000..cef5f393993
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/license.rst
@@ -0,0 +1,8 @@
+===================
+License and Credits
+===================
+
+``attrs`` is licensed under the `MIT <https://choosealicense.com/licenses/mit/>`_ license.
+The full license text can be also found in the `source code repository <https://github.com/python-attrs/attrs/blob/master/LICENSE>`_.
+
+.. include:: ../AUTHORS.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/overview.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/overview.rst
new file mode 100644
index 00000000000..47b022ff32e
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/overview.rst
@@ -0,0 +1,87 @@
+========
+Overview
+========
+
+In order to fulfill its ambitious goal of bringing back the joy to writing classes, it gives you a class decorator and a way to declaratively define the attributes on that class:
+
+.. include:: ../README.rst
+ :start-after: -code-begin-
+ :end-before: -testimonials-
+
+
+.. _philosophy:
+
+Philosophy
+==========
+
+**It's about regular classes.**
+ ``attrs`` is for creating well-behaved classes with a type, attributes, methods, and everything that comes with a class.
+ It can be used for data-only containers like ``namedtuple``\ s or ``types.SimpleNamespace`` but they're just a sub-genre of what ``attrs`` is good for.
+
+**The class belongs to the users.**
+ You define a class and ``attrs`` adds static methods to that class based on the attributes you declare.
+ The end.
+ It doesn't add metaclasses.
+ It doesn't add classes you've never heard of to your inheritance tree.
+ An ``attrs`` class in runtime is indistiguishable from a regular class: because it *is* a regular class with a few boilerplate-y methods attached.
+
+**Be light on API impact.**
+ As convenient as it seems at first, ``attrs`` will *not* tack on any methods to your classes save the dunder ones.
+ Hence all the useful :ref:`tools <helpers>` that come with ``attrs`` live in functions that operate on top of instances.
+ Since they take an ``attrs`` instance as their first argument, you can attach them to your classes with one line of code.
+
+**Performance matters.**
+ ``attrs`` runtime impact is very close to zero because all the work is done when the class is defined.
+ Once you're instantiating it, ``attrs`` is out of the picture completely.
+
+**No surprises.**
+ ``attrs`` creates classes that arguably work the way a Python beginner would reasonably expect them to work.
+ It doesn't try to guess what you mean because explicit is better than implicit.
+ It doesn't try to be clever because software shouldn't be clever.
+
+Check out :doc:`how-does-it-work` if you'd like to know how it achieves all of the above.
+
+
+What ``attrs`` Is Not
+=====================
+
+``attrs`` does *not* invent some kind of magic system that pulls classes out of its hat using meta classes, runtime introspection, and shaky interdependencies.
+
+All ``attrs`` does is:
+
+1. take your declaration,
+2. write dunder methods based on that information,
+3. and attach them to your class.
+
+It does *nothing* dynamic at runtime, hence zero runtime overhead.
+It's still *your* class.
+Do with it as you please.
+
+
+On the ``attr.s`` and ``attr.ib`` Names
+=======================================
+
+The ``attr.s`` decorator and the ``attr.ib`` function aren't any obscure abbreviations.
+They are a *concise* and highly *readable* way to write ``attrs`` and ``attrib`` with an *explicit namespace*.
+
+At first, some people have a negative gut reaction to that; resembling the reactions to Python's significant whitespace.
+And as with that, once one gets used to it, the readability and explicitness of that API prevails and delights.
+
+For those who can't swallow that API at all, ``attrs`` comes with serious business aliases: ``attr.attrs`` and ``attr.attrib``.
+
+Therefore, the following class definition is identical to the previous one:
+
+.. doctest::
+
+ >>> from attr import attrs, attrib, Factory
+ >>> @attrs
+ ... class SomeClass(object):
+ ... a_number = attrib(default=42)
+ ... list_of_numbers = attrib(default=Factory(list))
+ ...
+ ... def hard_math(self, another_number):
+ ... return self.a_number + sum(self.list_of_numbers) * another_number
+ >>> SomeClass(1, [1, 2, 3])
+ SomeClass(a_number=1, list_of_numbers=[1, 2, 3])
+
+Use whichever variant fits your taste better.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/why.rst b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/why.rst
new file mode 100644
index 00000000000..9c64cb93c57
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/docs/why.rst
@@ -0,0 +1,251 @@
+.. _why:
+
+Why not…
+========
+
+
+If you'd like third party's account why ``attrs`` is great, have a look at Glyph's `The One Python Library Everyone Needs <https://glyph.twistedmatrix.com/2016/08/attrs.html>`_!
+
+
+…tuples?
+--------
+
+
+Readability
+^^^^^^^^^^^
+
+What makes more sense while debugging::
+
+ Point(x=1, y=2)
+
+or::
+
+ (1, 2)
+
+?
+
+Let's add even more ambiguity::
+
+ Customer(id=42, reseller=23, first_name="Jane", last_name="John")
+
+or::
+
+ (42, 23, "Jane", "John")
+
+?
+
+Why would you want to write ``customer[2]`` instead of ``customer.first_name``?
+
+Don't get me started when you add nesting.
+If you've never run into mysterious tuples you had no idea what the hell they meant while debugging, you're much smarter than yours truly.
+
+Using proper classes with names and types makes program code much more readable and comprehensible_.
+Especially when trying to grok a new piece of software or returning to old code after several months.
+
+.. _comprehensible: https://arxiv.org/pdf/1304.5257.pdf
+
+
+Extendability
+^^^^^^^^^^^^^
+
+Imagine you have a function that takes or returns a tuple.
+Especially if you use tuple unpacking (eg. ``x, y = get_point()``), adding additional data means that you have to change the invocation of that function *everywhere*.
+
+Adding an attribute to a class concerns only those who actually care about that attribute.
+
+
+…namedtuples?
+-------------
+
+:func:`collections.namedtuple`\ s are tuples with names, not classes. [#history]_
+Since writing classes is tiresome in Python, every now and then someone discovers all the typing they could save and gets really excited.
+However that convenience comes at a price.
+
+The most obvious difference between ``namedtuple``\ s and ``attrs``-based classes is that the latter are type-sensitive:
+
+.. doctest::
+
+ >>> import attr
+ >>> C1 = attr.make_class("C1", ["a"])
+ >>> C2 = attr.make_class("C2", ["a"])
+ >>> i1 = C1(1)
+ >>> i2 = C2(1)
+ >>> i1.a == i2.a
+ True
+ >>> i1 == i2
+ False
+
+…while a ``namedtuple`` is *intentionally* `behaving like a tuple`_ which means the type of a tuple is *ignored*:
+
+.. doctest::
+
+ >>> from collections import namedtuple
+ >>> NT1 = namedtuple("NT1", "a")
+ >>> NT2 = namedtuple("NT2", "b")
+ >>> t1 = NT1(1)
+ >>> t2 = NT2(1)
+ >>> t1 == t2 == (1,)
+ True
+
+Other often surprising behaviors include:
+
+- Since they are a subclass of tuples, ``namedtuple``\ s have a length and are both iterable and indexable.
+ That's not what you'd expect from a class and is likely to shadow subtle typo bugs.
+- Iterability also implies that it's easy to accidentally unpack a ``namedtuple`` which leads to hard-to-find bugs. [#iter]_
+- ``namedtuple``\ s have their methods *on your instances* whether you like it or not. [#pollution]_
+- ``namedtuple``\ s are *always* immutable.
+ Not only does that mean that you can't decide for yourself whether your instances should be immutable or not, it also means that if you want to influence your class' initialization (validation? default values?), you have to implement :meth:`__new__() <object.__new__>` which is a particularly hacky and error-prone requirement for a very common problem. [#immutable]_
+- To attach methods to a ``namedtuple`` you have to subclass it.
+ And if you follow the standard library documentation's recommendation of::
+
+ class Point(namedtuple('Point', ['x', 'y'])):
+ # ...
+
+ you end up with a class that has *two* ``Point``\ s in its :attr:`__mro__ <class.__mro__>`: ``[<class 'point.Point'>, <class 'point.Point'>, <type 'tuple'>, <type 'object'>]``.
+
+ That's not only confusing, it also has very practical consequences:
+ for example if you create documentation that includes class hierarchies like `Sphinx's autodoc <http://www.sphinx-doc.org/en/stable/ext/autodoc.html>`_ with ``show-inheritance``.
+ Again: common problem, hacky solution with confusing fallout.
+
+All these things make ``namedtuple``\ s a particularly poor choice for public APIs because all your objects are irrevocably tainted.
+With ``attrs`` your users won't notice a difference because it creates regular, well-behaved classes.
+
+.. admonition:: Summary
+
+ If you want a *tuple with names*, by all means: go for a ``namedtuple``. [#perf]_
+ But if you want a class with methods, you're doing yourself a disservice by relying on a pile of hacks that requires you to employ even more hacks as your requirements expand.
+
+ Other than that, ``attrs`` also adds nifty features like validators, converters, and (mutable!) default values.
+
+
+.. rubric:: Footnotes
+
+.. [#history] The word is that ``namedtuple``\ s were added to the Python standard library as a way to make tuples in return values more readable.
+ And indeed that is something you see throughout the standard library.
+
+ Looking at what the makers of ``namedtuple``\ s use it for themselves is a good guideline for deciding on your own use cases.
+.. [#pollution] ``attrs`` only adds a single attribute: ``__attrs_attrs__`` for introspection.
+ All helpers are functions in the ``attr`` package.
+ Since they take the instance as first argument, you can easily attach them to your classes under a name of your own choice.
+.. [#iter] :func:`attr.astuple` can be used to get that behavior in ``attrs`` on *explicit demand*.
+.. [#immutable] ``attrs`` offers *optional* immutability through the ``frozen`` keyword.
+.. [#perf] Although ``attrs`` would serve you just as well!
+ Since both employ the same method of writing and compiling Python code for you, the performance penalty is negligible at worst and in some cases ``attrs`` is even faster if you use ``slots=True`` (which is generally a good idea anyway).
+
+.. _behaving like a tuple: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
+
+
+…dicts?
+-------
+
+Dictionaries are not for fixed fields.
+
+If you have a dict, it maps something to something else.
+You should be able to add and remove values.
+
+
+
+``attrs`` lets you be specific about those expectations; a dictionary does not.
+It gives you a named entity (the class) in your code, which lets you explain in other places whether you take a parameter of that class or return a value of that class.
+
+In other words: if your dict has a fixed and known set of keys, it is an object, not a hash.
+So if you never iterate over the keys of a dict, you should use a proper class.
+
+
+…hand-written classes?
+----------------------
+
+While we're fans of all things artisanal, writing the same nine methods all over again doesn't qualify for me.
+I usually manage to get some typos inside and there's simply more code that can break and thus has to be tested.
+
+To bring it into perspective, the equivalent of
+
+.. doctest::
+
+ >>> @attr.s
+ ... class SmartClass(object):
+ ... a = attr.ib()
+ ... b = attr.ib()
+ >>> SmartClass(1, 2)
+ SmartClass(a=1, b=2)
+
+is
+
+.. doctest::
+
+ >>> class ArtisanalClass(object):
+ ... def __init__(self, a, b):
+ ... self.a = a
+ ... self.b = b
+ ...
+ ... def __repr__(self):
+ ... return "ArtisanalClass(a={}, b={})".format(self.a, self.b)
+ ...
+ ... def __eq__(self, other):
+ ... if other.__class__ is self.__class__:
+ ... return (self.a, self.b) == (other.a, other.b)
+ ... else:
+ ... return NotImplemented
+ ...
+ ... def __ne__(self, other):
+ ... result = self.__eq__(other)
+ ... if result is NotImplemented:
+ ... return NotImplemented
+ ... else:
+ ... return not result
+ ...
+ ... def __lt__(self, other):
+ ... if other.__class__ is self.__class__:
+ ... return (self.a, self.b) < (other.a, other.b)
+ ... else:
+ ... return NotImplemented
+ ...
+ ... def __le__(self, other):
+ ... if other.__class__ is self.__class__:
+ ... return (self.a, self.b) <= (other.a, other.b)
+ ... else:
+ ... return NotImplemented
+ ...
+ ... def __gt__(self, other):
+ ... if other.__class__ is self.__class__:
+ ... return (self.a, self.b) > (other.a, other.b)
+ ... else:
+ ... return NotImplemented
+ ...
+ ... def __ge__(self, other):
+ ... if other.__class__ is self.__class__:
+ ... return (self.a, self.b) >= (other.a, other.b)
+ ... else:
+ ... return NotImplemented
+ ...
+ ... def __hash__(self):
+ ... return hash((self.a, self.b))
+ >>> ArtisanalClass(a=1, b=2)
+ ArtisanalClass(a=1, b=2)
+
+which is quite a mouthful and it doesn't even use any of ``attrs``'s more advanced features like validators or defaults values.
+Also: no tests whatsoever.
+And who will guarantee you, that you don't accidentally flip the ``<`` in your tenth implementation of ``__gt__``?
+
+It also should be noted that ``attrs`` is not an all-or-nothing solution.
+You can freely choose which features you want and disable those that you want more control over:
+
+.. doctest::
+
+ >>> @attr.s(repr=False)
+ ... class SmartClass(object):
+ ... a = attr.ib()
+ ... b = attr.ib()
+ ...
+ ... def __repr__(self):
+ ... return "<SmartClass(a=%d)>" % (self.a,)
+ >>> SmartClass(1, 2)
+ <SmartClass(a=1)>
+
+.. admonition:: Summary
+
+ If you don't care and like typing, we're not gonna stop you.
+
+ However it takes a lot of bias and determined rationalization to claim that ``attrs`` raises the mental burden on a project given how difficult it is to find the important bits in a hand-written class and how annoying it is to ensure you've copy-pasted your code correctly over all your classes.
+
+ In any case, if you ever get sick of the repetitiveness and drowning important code in a sea of boilerplate, ``attrs`` will be waiting for you.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/pyproject.toml b/tests/wpt/web-platform-tests/tools/third_party/attrs/pyproject.toml
new file mode 100644
index 00000000000..0f68a7cb0fc
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/pyproject.toml
@@ -0,0 +1,26 @@
+[tool.towncrier]
+ package = "attr"
+ package_dir = "src"
+ filename = "CHANGELOG.rst"
+ issue_format = "`#{issue} <https://github.com/python-attrs/attrs/issues/{issue}>`_"
+ directory = "changelog.d"
+ title_format = "{version} ({project_date})"
+ underlines = ["-", "^"]
+
+ [[tool.towncrier.section]]
+ path = ""
+
+ [[tool.towncrier.type]]
+ directory = "breaking"
+ name = "Backward-incompatible Changes"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "deprecation"
+ name = "Deprecations"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "change"
+ name = "Changes"
+ showcontent = true
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/setup.cfg b/tests/wpt/web-platform-tests/tools/third_party/attrs/setup.cfg
new file mode 100644
index 00000000000..8ddbbabc3cd
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/setup.cfg
@@ -0,0 +1,26 @@
+[bdist_wheel]
+universal = 1
+
+
+[metadata]
+# ensure LICENSE is included in wheel metadata
+license_file = LICENSE
+
+
+[tool:pytest]
+minversion = 3.0
+strict = true
+addopts = -ra
+testpaths = tests
+filterwarnings =
+ once::Warning
+
+
+[isort]
+atomic=true
+lines_after_imports=2
+lines_between_types=1
+multi_line_output=5
+not_skip=__init__.py
+
+known_first_party=attr
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/setup.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/setup.py
new file mode 100644
index 00000000000..232d3f5eaaa
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/setup.py
@@ -0,0 +1,95 @@
+import codecs
+import os
+import re
+
+from setuptools import find_packages, setup
+
+
+###############################################################################
+
+NAME = "attrs"
+PACKAGES = find_packages(where="src")
+META_PATH = os.path.join("src", "attr", "__init__.py")
+KEYWORDS = ["class", "attribute", "boilerplate"]
+CLASSIFIERS = [
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "Natural Language :: English",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.4",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Programming Language :: Python :: Implementation :: PyPy",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+]
+INSTALL_REQUIRES = []
+
+###############################################################################
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+
+
+def read(*parts):
+ """
+ Build an absolute path from *parts* and and return the contents of the
+ resulting file. Assume UTF-8 encoding.
+ """
+ with codecs.open(os.path.join(HERE, *parts), "rb", "utf-8") as f:
+ return f.read()
+
+
+META_FILE = read(META_PATH)
+
+
+def find_meta(meta):
+ """
+ Extract __*meta*__ from META_FILE.
+ """
+ meta_match = re.search(
+ r"^__{meta}__ = ['\"]([^'\"]*)['\"]".format(meta=meta),
+ META_FILE, re.M
+ )
+ if meta_match:
+ return meta_match.group(1)
+ raise RuntimeError("Unable to find __{meta}__ string.".format(meta=meta))
+
+
+VERSION = find_meta("version")
+URI = find_meta("uri")
+LONG = (
+ read("README.rst") + "\n\n" +
+ "Release Information\n" +
+ "===================\n\n" +
+ re.search("(\d+.\d.\d \(.*?\)\n.*?)\n\n\n----\n\n\n",
+ read("CHANGELOG.rst"), re.S).group(1) +
+ "\n\n`Full changelog " +
+ "<{uri}en/stable/changelog.html>`_.\n\n".format(uri=URI) +
+ read("AUTHORS.rst")
+)
+
+
+if __name__ == "__main__":
+ setup(
+ name=NAME,
+ description=find_meta("description"),
+ license=find_meta("license"),
+ url=URI,
+ version=VERSION,
+ author=find_meta("author"),
+ author_email=find_meta("email"),
+ maintainer=find_meta("author"),
+ maintainer_email=find_meta("email"),
+ keywords=KEYWORDS,
+ long_description=LONG,
+ packages=PACKAGES,
+ package_dir={"": "src"},
+ zip_safe=False,
+ classifiers=CLASSIFIERS,
+ install_requires=INSTALL_REQUIRES,
+ )
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/__init__.py
new file mode 100644
index 00000000000..929b1721ff1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/__init__.py
@@ -0,0 +1,55 @@
+from __future__ import absolute_import, division, print_function
+
+from functools import partial
+
+from . import converters, exceptions, filters, validators
+from ._config import get_run_validators, set_run_validators
+from ._funcs import asdict, assoc, astuple, evolve, has
+from ._make import (
+ NOTHING, Attribute, Factory, attrib, attrs, fields, make_class, validate
+)
+
+
+__version__ = "17.4.0.dev0"
+
+__title__ = "attrs"
+__description__ = "Classes Without Boilerplate"
+__uri__ = "http://www.attrs.org/"
+__doc__ = __description__ + " <" + __uri__ + ">"
+
+__author__ = "Hynek Schlawack"
+__email__ = "hs@ox.cx"
+
+__license__ = "MIT"
+__copyright__ = "Copyright (c) 2015 Hynek Schlawack"
+
+
+s = attributes = attrs
+ib = attr = attrib
+dataclass = partial(attrs, auto_attribs=True) # happy Easter ;)
+
+__all__ = [
+ "Attribute",
+ "Factory",
+ "NOTHING",
+ "asdict",
+ "assoc",
+ "astuple",
+ "attr",
+ "attrib",
+ "attributes",
+ "attrs",
+ "converters",
+ "evolve",
+ "exceptions",
+ "fields",
+ "filters",
+ "get_run_validators",
+ "has",
+ "ib",
+ "make_class",
+ "s",
+ "set_run_validators",
+ "validate",
+ "validators",
+]
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_compat.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_compat.py
new file mode 100644
index 00000000000..8a49341b25f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_compat.py
@@ -0,0 +1,139 @@
+from __future__ import absolute_import, division, print_function
+
+import platform
+import sys
+import types
+import warnings
+
+
+PY2 = sys.version_info[0] == 2
+PYPY = platform.python_implementation() == "PyPy"
+
+
+if PY2:
+ from UserDict import IterableUserDict
+
+ # We 'bundle' isclass instead of using inspect as importing inspect is
+ # fairly expensive (order of 10-15 ms for a modern machine in 2016)
+ def isclass(klass):
+ return isinstance(klass, (type, types.ClassType))
+
+ # TYPE is used in exceptions, repr(int) is different on Python 2 and 3.
+ TYPE = "type"
+
+ def iteritems(d):
+ return d.iteritems()
+
+ # Python 2 is bereft of a read-only dict proxy, so we make one!
+ class ReadOnlyDict(IterableUserDict):
+ """
+ Best-effort read-only dict wrapper.
+ """
+
+ def __setitem__(self, key, val):
+ # We gently pretend we're a Python 3 mappingproxy.
+ raise TypeError("'mappingproxy' object does not support item "
+ "assignment")
+
+ def update(self, _):
+ # We gently pretend we're a Python 3 mappingproxy.
+ raise AttributeError("'mappingproxy' object has no attribute "
+ "'update'")
+
+ def __delitem__(self, _):
+ # We gently pretend we're a Python 3 mappingproxy.
+ raise TypeError("'mappingproxy' object does not support item "
+ "deletion")
+
+ def clear(self):
+ # We gently pretend we're a Python 3 mappingproxy.
+ raise AttributeError("'mappingproxy' object has no attribute "
+ "'clear'")
+
+ def pop(self, key, default=None):
+ # We gently pretend we're a Python 3 mappingproxy.
+ raise AttributeError("'mappingproxy' object has no attribute "
+ "'pop'")
+
+ def popitem(self):
+ # We gently pretend we're a Python 3 mappingproxy.
+ raise AttributeError("'mappingproxy' object has no attribute "
+ "'popitem'")
+
+ def setdefault(self, key, default=None):
+ # We gently pretend we're a Python 3 mappingproxy.
+ raise AttributeError("'mappingproxy' object has no attribute "
+ "'setdefault'")
+
+ def __repr__(self):
+ # Override to be identical to the Python 3 version.
+ return "mappingproxy(" + repr(self.data) + ")"
+
+ def metadata_proxy(d):
+ res = ReadOnlyDict()
+ res.data.update(d) # We blocked update, so we have to do it like this.
+ return res
+
+else:
+ def isclass(klass):
+ return isinstance(klass, type)
+
+ TYPE = "class"
+
+ def iteritems(d):
+ return d.items()
+
+ def metadata_proxy(d):
+ return types.MappingProxyType(dict(d))
+
+
+def import_ctypes(): # pragma: nocover
+ """
+ Moved into a function for testability.
+ """
+ try:
+ import ctypes
+ return ctypes
+ except ImportError:
+ return None
+
+
+if not PY2:
+ def just_warn(*args, **kw):
+ """
+ We only warn on Python 3 because we are not aware of any concrete
+ consequences of not setting the cell on Python 2.
+ """
+ warnings.warn(
+ "Missing ctypes. Some features like bare super() or accessing "
+ "__class__ will not work with slots classes.",
+ RuntimeWarning,
+ stacklevel=2,
+ )
+else:
+ def just_warn(*args, **kw): # pragma: nocover
+ """
+ We only warn on Python 3 because we are not aware of any concrete
+ consequences of not setting the cell on Python 2.
+ """
+
+
+def make_set_closure_cell():
+ """
+ Moved into a function for testability.
+ """
+ if PYPY: # pragma: no cover
+ def set_closure_cell(cell, value):
+ cell.__setstate__((value,))
+ else:
+ ctypes = import_ctypes()
+ if ctypes is not None:
+ set_closure_cell = ctypes.pythonapi.PyCell_Set
+ set_closure_cell.argtypes = (ctypes.py_object, ctypes.py_object)
+ set_closure_cell.restype = ctypes.c_int
+ else:
+ set_closure_cell = just_warn
+ return set_closure_cell
+
+
+set_closure_cell = make_set_closure_cell()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_config.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_config.py
new file mode 100644
index 00000000000..8ec920962d1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_config.py
@@ -0,0 +1,23 @@
+from __future__ import absolute_import, division, print_function
+
+
+__all__ = ["set_run_validators", "get_run_validators"]
+
+_run_validators = True
+
+
+def set_run_validators(run):
+ """
+ Set whether or not validators are run. By default, they are run.
+ """
+ if not isinstance(run, bool):
+ raise TypeError("'run' must be bool.")
+ global _run_validators
+ _run_validators = run
+
+
+def get_run_validators():
+ """
+ Return whether or not validators are run.
+ """
+ return _run_validators
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_funcs.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_funcs.py
new file mode 100644
index 00000000000..798043af3f8
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_funcs.py
@@ -0,0 +1,212 @@
+from __future__ import absolute_import, division, print_function
+
+import copy
+
+from ._compat import iteritems
+from ._make import NOTHING, _obj_setattr, fields
+from .exceptions import AttrsAttributeNotFoundError
+
+
+def asdict(inst, recurse=True, filter=None, dict_factory=dict,
+ retain_collection_types=False):
+ """
+ Return the ``attrs`` attribute values of *inst* as a dict.
+
+ Optionally recurse into other ``attrs``-decorated classes.
+
+ :param inst: Instance of an ``attrs``-decorated class.
+ :param bool recurse: Recurse into classes that are also
+ ``attrs``-decorated.
+ :param callable filter: A callable whose return code determines whether an
+ attribute or element is included (``True``) or dropped (``False``). Is
+ called with the :class:`attr.Attribute` as the first argument and the
+ value as the second argument.
+ :param callable dict_factory: A callable to produce dictionaries from. For
+ example, to produce ordered dictionaries instead of normal Python
+ dictionaries, pass in ``collections.OrderedDict``.
+ :param bool retain_collection_types: Do not convert to ``list`` when
+ encountering an attribute whose type is ``tuple`` or ``set``. Only
+ meaningful if ``recurse`` is ``True``.
+
+ :rtype: return type of *dict_factory*
+
+ :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ class.
+
+ .. versionadded:: 16.0.0 *dict_factory*
+ .. versionadded:: 16.1.0 *retain_collection_types*
+ """
+ attrs = fields(inst.__class__)
+ rv = dict_factory()
+ for a in attrs:
+ v = getattr(inst, a.name)
+ if filter is not None and not filter(a, v):
+ continue
+ if recurse is True:
+ if has(v.__class__):
+ rv[a.name] = asdict(v, recurse=True, filter=filter,
+ dict_factory=dict_factory)
+ elif isinstance(v, (tuple, list, set)):
+ cf = v.__class__ if retain_collection_types is True else list
+ rv[a.name] = cf([
+ asdict(i, recurse=True, filter=filter,
+ dict_factory=dict_factory)
+ if has(i.__class__) else i
+ for i in v
+ ])
+ elif isinstance(v, dict):
+ df = dict_factory
+ rv[a.name] = df((
+ asdict(kk, dict_factory=df) if has(kk.__class__) else kk,
+ asdict(vv, dict_factory=df) if has(vv.__class__) else vv)
+ for kk, vv in iteritems(v))
+ else:
+ rv[a.name] = v
+ else:
+ rv[a.name] = v
+ return rv
+
+
+def astuple(inst, recurse=True, filter=None, tuple_factory=tuple,
+ retain_collection_types=False):
+ """
+ Return the ``attrs`` attribute values of *inst* as a tuple.
+
+ Optionally recurse into other ``attrs``-decorated classes.
+
+ :param inst: Instance of an ``attrs``-decorated class.
+ :param bool recurse: Recurse into classes that are also
+ ``attrs``-decorated.
+ :param callable filter: A callable whose return code determines whether an
+ attribute or element is included (``True``) or dropped (``False``). Is
+ called with the :class:`attr.Attribute` as the first argument and the
+ value as the second argument.
+ :param callable tuple_factory: A callable to produce tuples from. For
+ example, to produce lists instead of tuples.
+ :param bool retain_collection_types: Do not convert to ``list``
+ or ``dict`` when encountering an attribute which type is
+ ``tuple``, ``dict`` or ``set``. Only meaningful if ``recurse`` is
+ ``True``.
+
+ :rtype: return type of *tuple_factory*
+
+ :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ class.
+
+ .. versionadded:: 16.2.0
+ """
+ attrs = fields(inst.__class__)
+ rv = []
+ retain = retain_collection_types # Very long. :/
+ for a in attrs:
+ v = getattr(inst, a.name)
+ if filter is not None and not filter(a, v):
+ continue
+ if recurse is True:
+ if has(v.__class__):
+ rv.append(astuple(v, recurse=True, filter=filter,
+ tuple_factory=tuple_factory,
+ retain_collection_types=retain))
+ elif isinstance(v, (tuple, list, set)):
+ cf = v.__class__ if retain is True else list
+ rv.append(cf([
+ astuple(j, recurse=True, filter=filter,
+ tuple_factory=tuple_factory,
+ retain_collection_types=retain)
+ if has(j.__class__) else j
+ for j in v
+ ]))
+ elif isinstance(v, dict):
+ df = v.__class__ if retain is True else dict
+ rv.append(df(
+ (
+ astuple(
+ kk,
+ tuple_factory=tuple_factory,
+ retain_collection_types=retain
+ ) if has(kk.__class__) else kk,
+ astuple(
+ vv,
+ tuple_factory=tuple_factory,
+ retain_collection_types=retain
+ ) if has(vv.__class__) else vv
+ )
+ for kk, vv in iteritems(v)))
+ else:
+ rv.append(v)
+ else:
+ rv.append(v)
+ return rv if tuple_factory is list else tuple_factory(rv)
+
+
+def has(cls):
+ """
+ Check whether *cls* is a class with ``attrs`` attributes.
+
+ :param type cls: Class to introspect.
+ :raise TypeError: If *cls* is not a class.
+
+ :rtype: :class:`bool`
+ """
+ return getattr(cls, "__attrs_attrs__", None) is not None
+
+
+def assoc(inst, **changes):
+ """
+ Copy *inst* and apply *changes*.
+
+ :param inst: Instance of a class with ``attrs`` attributes.
+ :param changes: Keyword changes in the new copy.
+
+ :return: A copy of inst with *changes* incorporated.
+
+ :raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't
+ be found on *cls*.
+ :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ class.
+
+ .. deprecated:: 17.1.0
+ Use :func:`evolve` instead.
+ """
+ import warnings
+ warnings.warn("assoc is deprecated and will be removed after 2018/01.",
+ DeprecationWarning, stacklevel=2)
+ new = copy.copy(inst)
+ attrs = fields(inst.__class__)
+ for k, v in iteritems(changes):
+ a = getattr(attrs, k, NOTHING)
+ if a is NOTHING:
+ raise AttrsAttributeNotFoundError(
+ "{k} is not an attrs attribute on {cl}."
+ .format(k=k, cl=new.__class__)
+ )
+ _obj_setattr(new, k, v)
+ return new
+
+
+def evolve(inst, **changes):
+ """
+ Create a new instance, based on *inst* with *changes* applied.
+
+ :param inst: Instance of a class with ``attrs`` attributes.
+ :param changes: Keyword changes in the new copy.
+
+ :return: A copy of inst with *changes* incorporated.
+
+ :raise TypeError: If *attr_name* couldn't be found in the class
+ ``__init__``.
+ :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ class.
+
+ .. versionadded:: 17.1.0
+ """
+ cls = inst.__class__
+ attrs = fields(cls)
+ for a in attrs:
+ if not a.init:
+ continue
+ attr_name = a.name # To deal with private attributes.
+ init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
+ if init_name not in changes:
+ changes[init_name] = getattr(inst, attr_name)
+ return cls(**changes)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_make.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_make.py
new file mode 100644
index 00000000000..31c5f94ce38
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/_make.py
@@ -0,0 +1,1395 @@
+from __future__ import absolute_import, division, print_function
+
+import hashlib
+import linecache
+import sys
+
+from operator import itemgetter
+
+from . import _config
+from ._compat import PY2, isclass, iteritems, metadata_proxy, set_closure_cell
+from .exceptions import (
+ DefaultAlreadySetError, FrozenInstanceError, NotAnAttrsClassError,
+ UnannotatedAttributeError
+)
+
+
+# This is used at least twice, so cache it here.
+_obj_setattr = object.__setattr__
+_init_convert_pat = "__attr_convert_{}"
+_init_factory_pat = "__attr_factory_{}"
+_tuple_property_pat = " {attr_name} = property(itemgetter({index}))"
+_empty_metadata_singleton = metadata_proxy({})
+
+
+class _Nothing(object):
+ """
+ Sentinel class to indicate the lack of a value when ``None`` is ambiguous.
+
+ All instances of `_Nothing` are equal.
+ """
+ def __copy__(self):
+ return self
+
+ def __deepcopy__(self, _):
+ return self
+
+ def __eq__(self, other):
+ return other.__class__ == _Nothing
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __repr__(self):
+ return "NOTHING"
+
+ def __hash__(self):
+ return 0xdeadbeef
+
+
+NOTHING = _Nothing()
+"""
+Sentinel to indicate the lack of a value when ``None`` is ambiguous.
+"""
+
+
+def attrib(default=NOTHING, validator=None,
+ repr=True, cmp=True, hash=None, init=True,
+ convert=None, metadata={}, type=None):
+ """
+ Create a new attribute on a class.
+
+ .. warning::
+
+ Does *not* do anything unless the class is also decorated with
+ :func:`attr.s`!
+
+ :param default: A value that is used if an ``attrs``-generated ``__init__``
+ is used and no value is passed while instantiating or the attribute is
+ excluded using ``init=False``.
+
+ If the value is an instance of :class:`Factory`, its callable will be
+ used to construct a new value (useful for mutable data types like lists
+ or dicts).
+
+ If a default is not set (or set manually to ``attr.NOTHING``), a value
+ *must* be supplied when instantiating; otherwise a :exc:`TypeError`
+ will be raised.
+
+ The default can also be set using decorator notation as shown below.
+
+ :type default: Any value.
+
+ :param validator: :func:`callable` that is called by ``attrs``-generated
+ ``__init__`` methods after the instance has been initialized. They
+ receive the initialized instance, the :class:`Attribute`, and the
+ passed value.
+
+ The return value is *not* inspected so the validator has to throw an
+ exception itself.
+
+ If a ``list`` is passed, its items are treated as validators and must
+ all pass.
+
+ Validators can be globally disabled and re-enabled using
+ :func:`get_run_validators`.
+
+ The validator can also be set using decorator notation as shown below.
+
+ :type validator: ``callable`` or a ``list`` of ``callable``\ s.
+
+ :param bool repr: Include this attribute in the generated ``__repr__``
+ method.
+ :param bool cmp: Include this attribute in the generated comparison methods
+ (``__eq__`` et al).
+ :param hash: Include this attribute in the generated ``__hash__``
+ method. If ``None`` (default), mirror *cmp*'s value. This is the
+ correct behavior according the Python spec. Setting this value to
+ anything else than ``None`` is *discouraged*.
+ :type hash: ``bool`` or ``None``
+ :param bool init: Include this attribute in the generated ``__init__``
+ method. It is possible to set this to ``False`` and set a default
+ value. In that case this attributed is unconditionally initialized
+ with the specified default value or factory.
+ :param callable convert: :func:`callable` that is called by
+ ``attrs``-generated ``__init__`` methods to convert attribute's value
+ to the desired format. It is given the passed-in value, and the
+ returned value will be used as the new value of the attribute. The
+ value is converted before being passed to the validator, if any.
+ :param metadata: An arbitrary mapping, to be used by third-party
+ components. See :ref:`extending_metadata`.
+ :param type: The type of the attribute. In Python 3.6 or greater, the
+ preferred method to specify the type is using a variable annotation
+ (see `PEP 526 <https://www.python.org/dev/peps/pep-0526/>`_).
+ This argument is provided for backward compatibility.
+ Regardless of the approach used, the type will be stored on
+ ``Attribute.type``.
+
+ .. versionchanged:: 17.1.0 *validator* can be a ``list`` now.
+ .. versionchanged:: 17.1.0
+ *hash* is ``None`` and therefore mirrors *cmp* by default.
+ .. versionadded:: 17.3.0 *type*
+ """
+ if hash is not None and hash is not True and hash is not False:
+ raise TypeError(
+ "Invalid value for hash. Must be True, False, or None."
+ )
+ return _CountingAttr(
+ default=default,
+ validator=validator,
+ repr=repr,
+ cmp=cmp,
+ hash=hash,
+ init=init,
+ convert=convert,
+ metadata=metadata,
+ type=type,
+ )
+
+
+def _make_attr_tuple_class(cls_name, attr_names):
+ """
+ Create a tuple subclass to hold `Attribute`s for an `attrs` class.
+
+ The subclass is a bare tuple with properties for names.
+
+ class MyClassAttributes(tuple):
+ __slots__ = ()
+ x = property(itemgetter(0))
+ """
+ attr_class_name = "{}Attributes".format(cls_name)
+ attr_class_template = [
+ "class {}(tuple):".format(attr_class_name),
+ " __slots__ = ()",
+ ]
+ if attr_names:
+ for i, attr_name in enumerate(attr_names):
+ attr_class_template.append(_tuple_property_pat.format(
+ index=i,
+ attr_name=attr_name,
+ ))
+ else:
+ attr_class_template.append(" pass")
+ globs = {"itemgetter": itemgetter}
+ eval(compile("\n".join(attr_class_template), "", "exec"), globs)
+ return globs[attr_class_name]
+
+
+# Tuple class for extracted attributes from a class definition.
+# `super_attrs` is a subset of `attrs`.
+_Attributes = _make_attr_tuple_class("_Attributes", [
+ "attrs", # all attributes to build dunder methods for
+ "super_attrs", # attributes that have been inherited from super classes
+])
+
+
+def _is_class_var(annot):
+ """
+ Check whether *annot* is a typing.ClassVar.
+
+ The implementation is gross but importing `typing` is slow and there are
+ discussions to remove it from the stdlib alltogether.
+ """
+ return str(annot).startswith("typing.ClassVar")
+
+
+def _get_annotations(cls):
+ """
+ Get annotations for *cls*.
+ """
+ anns = getattr(cls, "__annotations__", None)
+ if anns is None:
+ return {}
+
+ # Verify that the annotations aren't merely inherited.
+ for super_cls in cls.__mro__[1:]:
+ if anns is getattr(super_cls, "__annotations__", None):
+ return {}
+
+ return anns
+
+
+def _transform_attrs(cls, these, auto_attribs):
+ """
+ Transform all `_CountingAttr`s on a class into `Attribute`s.
+
+ If *these* is passed, use that and don't look for them on the class.
+
+ Return an `_Attributes`.
+ """
+ cd = cls.__dict__
+ anns = _get_annotations(cls)
+
+ if these is not None:
+ ca_list = sorted((
+ (name, ca)
+ for name, ca
+ in iteritems(these)
+ ), key=lambda e: e[1].counter)
+ elif auto_attribs is True:
+ ca_names = {
+ name
+ for name, attr
+ in cd.items()
+ if isinstance(attr, _CountingAttr)
+ }
+ ca_list = []
+ annot_names = set()
+ for attr_name, type in anns.items():
+ if _is_class_var(type):
+ continue
+ annot_names.add(attr_name)
+ a = cd.get(attr_name, NOTHING)
+ if not isinstance(a, _CountingAttr):
+ if a is NOTHING:
+ a = attrib()
+ else:
+ a = attrib(default=a)
+ ca_list.append((attr_name, a))
+
+ unannotated = ca_names - annot_names
+ if len(unannotated) > 0:
+ raise UnannotatedAttributeError(
+ "The following `attr.ib`s lack a type annotation: " +
+ ", ".join(sorted(
+ unannotated,
+ key=lambda n: cd.get(n).counter
+ )) + "."
+ )
+ else:
+ ca_list = sorted((
+ (name, attr)
+ for name, attr
+ in cd.items()
+ if isinstance(attr, _CountingAttr)
+ ), key=lambda e: e[1].counter)
+
+ non_super_attrs = [
+ Attribute.from_counting_attr(
+ name=attr_name,
+ ca=ca,
+ type=anns.get(attr_name),
+ )
+ for attr_name, ca
+ in ca_list
+ ]
+
+ # Walk *down* the MRO for attributes. While doing so, we collect the names
+ # of attributes we've seen in `take_attr_names` and ignore their
+ # redefinitions deeper in the hierarchy.
+ super_attrs = []
+ taken_attr_names = {a.name: a for a in non_super_attrs}
+ for super_cls in cls.__mro__[1:-1]:
+ sub_attrs = getattr(super_cls, "__attrs_attrs__", None)
+ if sub_attrs is not None:
+ # We iterate over sub_attrs backwards so we can reverse the whole
+ # list in the end and get all attributes in the order they have
+ # been defined.
+ for a in reversed(sub_attrs):
+ prev_a = taken_attr_names.get(a.name)
+ if prev_a is None:
+ super_attrs.append(a)
+ taken_attr_names[a.name] = a
+ elif prev_a == a:
+ # This happens thru multiple inheritance. We don't want
+ # to favor attributes that are further down in the tree
+ # so we move them to the back.
+ super_attrs.remove(a)
+ super_attrs.append(a)
+
+ # Now reverse the list, such that the attributes are sorted by *descending*
+ # age. IOW: the oldest attribute definition is at the head of the list.
+ super_attrs.reverse()
+
+ attr_names = [a.name for a in super_attrs + non_super_attrs]
+
+ AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names)
+
+ attrs = AttrsClass(
+ super_attrs + [
+ Attribute.from_counting_attr(
+ name=attr_name,
+ ca=ca,
+ type=anns.get(attr_name)
+ )
+ for attr_name, ca
+ in ca_list
+ ]
+ )
+
+ had_default = False
+ for a in attrs:
+ if had_default is True and a.default is NOTHING and a.init is True:
+ raise ValueError(
+ "No mandatory attributes allowed after an attribute with a "
+ "default value or factory. Attribute in question: {a!r}"
+ .format(a=a)
+ )
+ elif had_default is False and \
+ a.default is not NOTHING and \
+ a.init is not False:
+ had_default = True
+
+ return _Attributes((attrs, super_attrs))
+
+
+def _frozen_setattrs(self, name, value):
+ """
+ Attached to frozen classes as __setattr__.
+ """
+ raise FrozenInstanceError()
+
+
+def _frozen_delattrs(self, name):
+ """
+ Attached to frozen classes as __delattr__.
+ """
+ raise FrozenInstanceError()
+
+
+class _ClassBuilder(object):
+ """
+ Iteratively build *one* class.
+ """
+ __slots__ = (
+ "_cls", "_cls_dict", "_attrs", "_super_names", "_attr_names", "_slots",
+ "_frozen", "_has_post_init",
+ )
+
+ def __init__(self, cls, these, slots, frozen, auto_attribs):
+ attrs, super_attrs = _transform_attrs(cls, these, auto_attribs)
+
+ self._cls = cls
+ self._cls_dict = dict(cls.__dict__) if slots else {}
+ self._attrs = attrs
+ self._super_names = set(a.name for a in super_attrs)
+ self._attr_names = tuple(a.name for a in attrs)
+ self._slots = slots
+ self._frozen = frozen or _has_frozen_superclass(cls)
+ self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False))
+
+ self._cls_dict["__attrs_attrs__"] = self._attrs
+
+ if frozen:
+ self._cls_dict["__setattr__"] = _frozen_setattrs
+ self._cls_dict["__delattr__"] = _frozen_delattrs
+
+ def __repr__(self):
+ return "<_ClassBuilder(cls={cls})>".format(cls=self._cls.__name__)
+
+ def build_class(self):
+ """
+ Finalize class based on the accumulated configuration.
+
+ Builder cannot be used anymore after calling this method.
+ """
+ if self._slots is True:
+ return self._create_slots_class()
+ else:
+ return self._patch_original_class()
+
+ def _patch_original_class(self):
+ """
+ Apply accumulated methods and return the class.
+ """
+ cls = self._cls
+ super_names = self._super_names
+
+ # Clean class of attribute definitions (`attr.ib()`s).
+ for name in self._attr_names:
+ if name not in super_names and \
+ getattr(cls, name, None) is not None:
+ delattr(cls, name)
+
+ # Attach our dunder methods.
+ for name, value in self._cls_dict.items():
+ setattr(cls, name, value)
+
+ return cls
+
+ def _create_slots_class(self):
+ """
+ Build and return a new class with a `__slots__` attribute.
+ """
+ super_names = self._super_names
+ cd = {
+ k: v
+ for k, v in iteritems(self._cls_dict)
+ if k not in tuple(self._attr_names) + ("__dict__",)
+ }
+
+ # We only add the names of attributes that aren't inherited.
+ # Settings __slots__ to inherited attributes wastes memory.
+ cd["__slots__"] = tuple(
+ name
+ for name in self._attr_names
+ if name not in super_names
+ )
+
+ qualname = getattr(self._cls, "__qualname__", None)
+ if qualname is not None:
+ cd["__qualname__"] = qualname
+
+ attr_names = tuple(self._attr_names)
+
+ def slots_getstate(self):
+ """
+ Automatically created by attrs.
+ """
+ return tuple(getattr(self, name) for name in attr_names)
+
+ def slots_setstate(self, state):
+ """
+ Automatically created by attrs.
+ """
+ __bound_setattr = _obj_setattr.__get__(self, Attribute)
+ for name, value in zip(attr_names, state):
+ __bound_setattr(name, value)
+
+ # slots and frozen require __getstate__/__setstate__ to work
+ cd["__getstate__"] = slots_getstate
+ cd["__setstate__"] = slots_setstate
+
+ # Create new class based on old class and our methods.
+ cls = type(self._cls)(
+ self._cls.__name__,
+ self._cls.__bases__,
+ cd,
+ )
+
+ # The following is a fix for
+ # https://github.com/python-attrs/attrs/issues/102. On Python 3,
+ # if a method mentions `__class__` or uses the no-arg super(), the
+ # compiler will bake a reference to the class in the method itself
+ # as `method.__closure__`. Since we replace the class with a
+ # clone, we rewrite these references so it keeps working.
+ for item in cls.__dict__.values():
+ if isinstance(item, (classmethod, staticmethod)):
+ # Class- and staticmethods hide their functions inside.
+ # These might need to be rewritten as well.
+ closure_cells = getattr(item.__func__, "__closure__", None)
+ else:
+ closure_cells = getattr(item, "__closure__", None)
+
+ if not closure_cells: # Catch None or the empty list.
+ continue
+ for cell in closure_cells:
+ if cell.cell_contents is self._cls:
+ set_closure_cell(cell, cls)
+
+ return cls
+
+ def add_repr(self, ns):
+ self._cls_dict["__repr__"] = _make_repr(self._attrs, ns=ns)
+ return self
+
+ def add_str(self):
+ repr_ = self._cls_dict.get("__repr__")
+ if repr_ is None:
+ raise ValueError(
+ "__str__ can only be generated if a __repr__ exists."
+ )
+
+ self._cls_dict["__str__"] = repr_
+ return self
+
+ def make_unhashable(self):
+ self._cls_dict["__hash__"] = None
+ return self
+
+ def add_hash(self):
+ self._cls_dict["__hash__"] = _make_hash(self._attrs)
+ return self
+
+ def add_init(self):
+ self._cls_dict["__init__"] = _make_init(
+ self._attrs,
+ self._has_post_init,
+ self._frozen,
+ )
+ return self
+
+ def add_cmp(self):
+ cd = self._cls_dict
+
+ cd["__eq__"], cd["__ne__"], cd["__lt__"], cd["__le__"], cd["__gt__"], \
+ cd["__ge__"] = _make_cmp(self._attrs)
+
+ return self
+
+
+def attrs(maybe_cls=None, these=None, repr_ns=None,
+ repr=True, cmp=True, hash=None, init=True,
+ slots=False, frozen=False, str=False, auto_attribs=False):
+ r"""
+ A class decorator that adds `dunder
+ <https://wiki.python.org/moin/DunderAlias>`_\ -methods according to the
+ specified attributes using :func:`attr.ib` or the *these* argument.
+
+ :param these: A dictionary of name to :func:`attr.ib` mappings. This is
+ useful to avoid the definition of your attributes within the class body
+ because you can't (e.g. if you want to add ``__repr__`` methods to
+ Django models) or don't want to.
+
+ If *these* is not ``None``, ``attrs`` will *not* search the class body
+ for attributes.
+
+ :type these: :class:`dict` of :class:`str` to :func:`attr.ib`
+
+ :param str repr_ns: When using nested classes, there's no way in Python 2
+ to automatically detect that. Therefore it's possible to set the
+ namespace explicitly for a more meaningful ``repr`` output.
+ :param bool repr: Create a ``__repr__`` method with a human readable
+ representation of ``attrs`` attributes..
+ :param bool str: Create a ``__str__`` method that is identical to
+ ``__repr__``. This is usually not necessary except for
+ :class:`Exception`\ s.
+ :param bool cmp: Create ``__eq__``, ``__ne__``, ``__lt__``, ``__le__``,
+ ``__gt__``, and ``__ge__`` methods that compare the class as if it were
+ a tuple of its ``attrs`` attributes. But the attributes are *only*
+ compared, if the type of both classes is *identical*!
+ :param hash: If ``None`` (default), the ``__hash__`` method is generated
+ according how *cmp* and *frozen* are set.
+
+ 1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you.
+ 2. If *cmp* is True and *frozen* is False, ``__hash__`` will be set to
+ None, marking it unhashable (which it is).
+ 3. If *cmp* is False, ``__hash__`` will be left untouched meaning the
+ ``__hash__`` method of the superclass will be used (if superclass is
+ ``object``, this means it will fall back to id-based hashing.).
+
+ Although not recommended, you can decide for yourself and force
+ ``attrs`` to create one (e.g. if the class is immutable even though you
+ didn't freeze it programmatically) by passing ``True`` or not. Both of
+ these cases are rather special and should be used carefully.
+
+ See the `Python documentation \
+ <https://docs.python.org/3/reference/datamodel.html#object.__hash__>`_
+ and the `GitHub issue that led to the default behavior \
+ <https://github.com/python-attrs/attrs/issues/136>`_ for more details.
+ :type hash: ``bool`` or ``None``
+ :param bool init: Create a ``__init__`` method that initializes the
+ ``attrs`` attributes. Leading underscores are stripped for the
+ argument name. If a ``__attrs_post_init__`` method exists on the
+ class, it will be called after the class is fully initialized.
+ :param bool slots: Create a slots_-style class that's more
+ memory-efficient. See :ref:`slots` for further ramifications.
+ :param bool frozen: Make instances immutable after initialization. If
+ someone attempts to modify a frozen instance,
+ :exc:`attr.exceptions.FrozenInstanceError` is raised.
+
+ Please note:
+
+ 1. This is achieved by installing a custom ``__setattr__`` method
+ on your class so you can't implement an own one.
+
+ 2. True immutability is impossible in Python.
+
+ 3. This *does* have a minor a runtime performance :ref:`impact
+ <how-frozen>` when initializing new instances. In other words:
+ ``__init__`` is slightly slower with ``frozen=True``.
+
+ 4. If a class is frozen, you cannot modify ``self`` in
+ ``__attrs_post_init__`` or a self-written ``__init__``. You can
+ circumvent that limitation by using
+ ``object.__setattr__(self, "attribute_name", value)``.
+
+ .. _slots: https://docs.python.org/3/reference/datamodel.html#slots
+ :param bool auto_attribs: If True, collect `PEP 526`_-annotated attributes
+ (Python 3.6 and later only) from the class body.
+
+ In this case, you **must** annotate every field. If ``attrs``
+ encounters a field that is set to an :func:`attr.ib` but lacks a type
+ annotation, an :exc:`attr.exceptions.UnannotatedAttributeError` is
+ raised. Use ``field_name: typing.Any = attr.ib(...)`` if you don't
+ want to set a type.
+
+ If you assign a value to those attributes (e.g. ``x: int = 42``), that
+ value becomes the default value like if it were passed using
+ ``attr.ib(default=42)``. Passing an instance of :class:`Factory` also
+ works as expected.
+
+ Attributes annotated as :data:`typing.ClassVar` are **ignored**.
+
+ .. _`PEP 526`: https://www.python.org/dev/peps/pep-0526/
+
+ .. versionadded:: 16.0.0 *slots*
+ .. versionadded:: 16.1.0 *frozen*
+ .. versionadded:: 16.3.0 *str*, and support for ``__attrs_post_init__``.
+ .. versionchanged::
+ 17.1.0 *hash* supports ``None`` as value which is also the default
+ now.
+ .. versionadded:: 17.3.0 *auto_attribs*
+ """
+ def wrap(cls):
+ if getattr(cls, "__class__", None) is None:
+ raise TypeError("attrs only works with new-style classes.")
+
+ builder = _ClassBuilder(cls, these, slots, frozen, auto_attribs)
+
+ if repr is True:
+ builder.add_repr(repr_ns)
+ if str is True:
+ builder.add_str()
+ if cmp is True:
+ builder.add_cmp()
+
+ if hash is not True and hash is not False and hash is not None:
+ # Can't use `hash in` because 1 == True for example.
+ raise TypeError(
+ "Invalid value for hash. Must be True, False, or None."
+ )
+ elif hash is False or (hash is None and cmp is False):
+ pass
+ elif hash is True or (hash is None and cmp is True and frozen is True):
+ builder.add_hash()
+ else:
+ builder.make_unhashable()
+
+ if init is True:
+ builder.add_init()
+
+ return builder.build_class()
+
+ # maybe_cls's type depends on the usage of the decorator. It's a class
+ # if it's used as `@attrs` but ``None`` if used as `@attrs()`.
+ if maybe_cls is None:
+ return wrap
+ else:
+ return wrap(maybe_cls)
+
+
+_attrs = attrs
+"""
+Internal alias so we can use it in functions that take an argument called
+*attrs*.
+"""
+
+
+if PY2:
+ def _has_frozen_superclass(cls):
+ """
+ Check whether *cls* has a frozen ancestor by looking at its
+ __setattr__.
+ """
+ return (
+ getattr(
+ cls.__setattr__, "__module__", None
+ ) == _frozen_setattrs.__module__ and
+ cls.__setattr__.__name__ == _frozen_setattrs.__name__
+ )
+else:
+ def _has_frozen_superclass(cls):
+ """
+ Check whether *cls* has a frozen ancestor by looking at its
+ __setattr__.
+ """
+ return cls.__setattr__ == _frozen_setattrs
+
+
+def _attrs_to_tuple(obj, attrs):
+ """
+ Create a tuple of all values of *obj*'s *attrs*.
+ """
+ return tuple(getattr(obj, a.name) for a in attrs)
+
+
+def _make_hash(attrs):
+ attrs = tuple(
+ a
+ for a in attrs
+ if a.hash is True or (a.hash is None and a.cmp is True)
+ )
+
+ # We cache the generated init methods for the same kinds of attributes.
+ sha1 = hashlib.sha1()
+ sha1.update(repr(attrs).encode("utf-8"))
+ unique_filename = "<attrs generated hash %s>" % (sha1.hexdigest(),)
+ type_hash = hash(unique_filename)
+ lines = [
+ "def __hash__(self):",
+ " return hash((",
+ " %d," % (type_hash,),
+ ]
+ for a in attrs:
+ lines.append(" self.%s," % (a.name))
+
+ lines.append(" ))")
+
+ script = "\n".join(lines)
+ globs = {}
+ locs = {}
+ bytecode = compile(script, unique_filename, "exec")
+ eval(bytecode, globs, locs)
+
+ # In order of debuggers like PDB being able to step through the code,
+ # we add a fake linecache entry.
+ linecache.cache[unique_filename] = (
+ len(script),
+ None,
+ script.splitlines(True),
+ unique_filename,
+ )
+
+ return locs["__hash__"]
+
+
+def _add_hash(cls, attrs):
+ """
+ Add a hash method to *cls*.
+ """
+ cls.__hash__ = _make_hash(attrs)
+ return cls
+
+
+def _make_cmp(attrs):
+ attrs = [a for a in attrs if a.cmp]
+
+ def attrs_to_tuple(obj):
+ """
+ Save us some typing.
+ """
+ return _attrs_to_tuple(obj, attrs)
+
+ def eq(self, other):
+ """
+ Automatically created by attrs.
+ """
+ if other.__class__ is self.__class__:
+ return attrs_to_tuple(self) == attrs_to_tuple(other)
+ else:
+ return NotImplemented
+
+ def ne(self, other):
+ """
+ Automatically created by attrs.
+ """
+ result = eq(self, other)
+ if result is NotImplemented:
+ return NotImplemented
+ else:
+ return not result
+
+ def lt(self, other):
+ """
+ Automatically created by attrs.
+ """
+ if isinstance(other, self.__class__):
+ return attrs_to_tuple(self) < attrs_to_tuple(other)
+ else:
+ return NotImplemented
+
+ def le(self, other):
+ """
+ Automatically created by attrs.
+ """
+ if isinstance(other, self.__class__):
+ return attrs_to_tuple(self) <= attrs_to_tuple(other)
+ else:
+ return NotImplemented
+
+ def gt(self, other):
+ """
+ Automatically created by attrs.
+ """
+ if isinstance(other, self.__class__):
+ return attrs_to_tuple(self) > attrs_to_tuple(other)
+ else:
+ return NotImplemented
+
+ def ge(self, other):
+ """
+ Automatically created by attrs.
+ """
+ if isinstance(other, self.__class__):
+ return attrs_to_tuple(self) >= attrs_to_tuple(other)
+ else:
+ return NotImplemented
+
+ return eq, ne, lt, le, gt, ge
+
+
+def _add_cmp(cls, attrs=None):
+ """
+ Add comparison methods to *cls*.
+ """
+ if attrs is None:
+ attrs = cls.__attrs_attrs__
+
+ cls.__eq__, cls.__ne__, cls.__lt__, cls.__le__, cls.__gt__, cls.__ge__ = \
+ _make_cmp(attrs)
+
+ return cls
+
+
+def _make_repr(attrs, ns):
+ """
+ Make a repr method for *attr_names* adding *ns* to the full name.
+ """
+ attr_names = tuple(
+ a.name
+ for a in attrs
+ if a.repr
+ )
+
+ def repr_(self):
+ """
+ Automatically created by attrs.
+ """
+ real_cls = self.__class__
+ if ns is None:
+ qualname = getattr(real_cls, "__qualname__", None)
+ if qualname is not None:
+ class_name = qualname.rsplit(">.", 1)[-1]
+ else:
+ class_name = real_cls.__name__
+ else:
+ class_name = ns + "." + real_cls.__name__
+
+ return "{0}({1})".format(
+ class_name,
+ ", ".join(
+ name + "=" + repr(getattr(self, name))
+ for name in attr_names
+ )
+ )
+ return repr_
+
+
+def _add_repr(cls, ns=None, attrs=None):
+ """
+ Add a repr method to *cls*.
+ """
+ if attrs is None:
+ attrs = cls.__attrs_attrs__
+
+ repr_ = _make_repr(attrs, ns)
+ cls.__repr__ = repr_
+ return cls
+
+
+def _make_init(attrs, post_init, frozen):
+ attrs = [
+ a
+ for a in attrs
+ if a.init or a.default is not NOTHING
+ ]
+
+ # We cache the generated init methods for the same kinds of attributes.
+ sha1 = hashlib.sha1()
+ sha1.update(repr(attrs).encode("utf-8"))
+ unique_filename = "<attrs generated init {0}>".format(
+ sha1.hexdigest()
+ )
+
+ script, globs = _attrs_to_init_script(
+ attrs,
+ frozen,
+ post_init,
+ )
+ locs = {}
+ bytecode = compile(script, unique_filename, "exec")
+ attr_dict = dict((a.name, a) for a in attrs)
+ globs.update({
+ "NOTHING": NOTHING,
+ "attr_dict": attr_dict,
+ })
+ if frozen is True:
+ # Save the lookup overhead in __init__ if we need to circumvent
+ # immutability.
+ globs["_cached_setattr"] = _obj_setattr
+ eval(bytecode, globs, locs)
+
+ # In order of debuggers like PDB being able to step through the code,
+ # we add a fake linecache entry.
+ linecache.cache[unique_filename] = (
+ len(script),
+ None,
+ script.splitlines(True),
+ unique_filename,
+ )
+
+ return locs["__init__"]
+
+
+def _add_init(cls, frozen):
+ """
+ Add a __init__ method to *cls*. If *frozen* is True, make it immutable.
+ """
+ cls.__init__ = _make_init(
+ cls.__attrs_attrs__,
+ getattr(cls, "__attrs_post_init__", False),
+ frozen,
+ )
+ return cls
+
+
+def fields(cls):
+ """
+ Returns the tuple of ``attrs`` attributes for a class.
+
+ The tuple also allows accessing the fields by their names (see below for
+ examples).
+
+ :param type cls: Class to introspect.
+
+ :raise TypeError: If *cls* is not a class.
+ :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ class.
+
+ :rtype: tuple (with name accessors) of :class:`attr.Attribute`
+
+ .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields
+ by name.
+ """
+ if not isclass(cls):
+ raise TypeError("Passed object must be a class.")
+ attrs = getattr(cls, "__attrs_attrs__", None)
+ if attrs is None:
+ raise NotAnAttrsClassError(
+ "{cls!r} is not an attrs-decorated class.".format(cls=cls)
+ )
+ return attrs
+
+
+def validate(inst):
+ """
+ Validate all attributes on *inst* that have a validator.
+
+ Leaves all exceptions through.
+
+ :param inst: Instance of a class with ``attrs`` attributes.
+ """
+ if _config._run_validators is False:
+ return
+
+ for a in fields(inst.__class__):
+ v = a.validator
+ if v is not None:
+ v(inst, a, getattr(inst, a.name))
+
+
+def _attrs_to_init_script(attrs, frozen, post_init):
+ """
+ Return a script of an initializer for *attrs* and a dict of globals.
+
+ The globals are expected by the generated script.
+
+ If *frozen* is True, we cannot set the attributes directly so we use
+ a cached ``object.__setattr__``.
+ """
+ lines = []
+ if frozen is True:
+ lines.append(
+ # Circumvent the __setattr__ descriptor to save one lookup per
+ # assignment.
+ "_setattr = _cached_setattr.__get__(self, self.__class__)"
+ )
+
+ def fmt_setter(attr_name, value_var):
+ return "_setattr('%(attr_name)s', %(value_var)s)" % {
+ "attr_name": attr_name,
+ "value_var": value_var,
+ }
+
+ def fmt_setter_with_converter(attr_name, value_var):
+ conv_name = _init_convert_pat.format(attr_name)
+ return "_setattr('%(attr_name)s', %(conv)s(%(value_var)s))" % {
+ "attr_name": attr_name,
+ "value_var": value_var,
+ "conv": conv_name,
+ }
+ else:
+ def fmt_setter(attr_name, value):
+ return "self.%(attr_name)s = %(value)s" % {
+ "attr_name": attr_name,
+ "value": value,
+ }
+
+ def fmt_setter_with_converter(attr_name, value_var):
+ conv_name = _init_convert_pat.format(attr_name)
+ return "self.%(attr_name)s = %(conv)s(%(value_var)s)" % {
+ "attr_name": attr_name,
+ "value_var": value_var,
+ "conv": conv_name,
+ }
+
+ args = []
+ attrs_to_validate = []
+
+ # This is a dictionary of names to validator and converter callables.
+ # Injecting this into __init__ globals lets us avoid lookups.
+ names_for_globals = {}
+
+ for a in attrs:
+ if a.validator:
+ attrs_to_validate.append(a)
+ attr_name = a.name
+ arg_name = a.name.lstrip("_")
+ has_factory = isinstance(a.default, Factory)
+ if has_factory and a.default.takes_self:
+ maybe_self = "self"
+ else:
+ maybe_self = ""
+ if a.init is False:
+ if has_factory:
+ init_factory_name = _init_factory_pat.format(a.name)
+ if a.convert is not None:
+ lines.append(fmt_setter_with_converter(
+ attr_name,
+ init_factory_name + "({0})".format(maybe_self)))
+ conv_name = _init_convert_pat.format(a.name)
+ names_for_globals[conv_name] = a.convert
+ else:
+ lines.append(fmt_setter(
+ attr_name,
+ init_factory_name + "({0})".format(maybe_self)
+ ))
+ names_for_globals[init_factory_name] = a.default.factory
+ else:
+ if a.convert is not None:
+ lines.append(fmt_setter_with_converter(
+ attr_name,
+ "attr_dict['{attr_name}'].default"
+ .format(attr_name=attr_name)
+ ))
+ conv_name = _init_convert_pat.format(a.name)
+ names_for_globals[conv_name] = a.convert
+ else:
+ lines.append(fmt_setter(
+ attr_name,
+ "attr_dict['{attr_name}'].default"
+ .format(attr_name=attr_name)
+ ))
+ elif a.default is not NOTHING and not has_factory:
+ args.append(
+ "{arg_name}=attr_dict['{attr_name}'].default".format(
+ arg_name=arg_name,
+ attr_name=attr_name,
+ )
+ )
+ if a.convert is not None:
+ lines.append(fmt_setter_with_converter(attr_name, arg_name))
+ names_for_globals[_init_convert_pat.format(a.name)] = a.convert
+ else:
+ lines.append(fmt_setter(attr_name, arg_name))
+ elif has_factory:
+ args.append("{arg_name}=NOTHING".format(arg_name=arg_name))
+ lines.append("if {arg_name} is not NOTHING:"
+ .format(arg_name=arg_name))
+ init_factory_name = _init_factory_pat.format(a.name)
+ if a.convert is not None:
+ lines.append(" " + fmt_setter_with_converter(attr_name,
+ arg_name))
+ lines.append("else:")
+ lines.append(" " + fmt_setter_with_converter(
+ attr_name,
+ init_factory_name + "({0})".format(maybe_self)
+ ))
+ names_for_globals[_init_convert_pat.format(a.name)] = a.convert
+ else:
+ lines.append(" " + fmt_setter(attr_name, arg_name))
+ lines.append("else:")
+ lines.append(" " + fmt_setter(
+ attr_name,
+ init_factory_name + "({0})".format(maybe_self)
+ ))
+ names_for_globals[init_factory_name] = a.default.factory
+ else:
+ args.append(arg_name)
+ if a.convert is not None:
+ lines.append(fmt_setter_with_converter(attr_name, arg_name))
+ names_for_globals[_init_convert_pat.format(a.name)] = a.convert
+ else:
+ lines.append(fmt_setter(attr_name, arg_name))
+
+ if attrs_to_validate: # we can skip this if there are no validators.
+ names_for_globals["_config"] = _config
+ lines.append("if _config._run_validators is True:")
+ for a in attrs_to_validate:
+ val_name = "__attr_validator_{}".format(a.name)
+ attr_name = "__attr_{}".format(a.name)
+ lines.append(" {}(self, {}, self.{})".format(
+ val_name, attr_name, a.name))
+ names_for_globals[val_name] = a.validator
+ names_for_globals[attr_name] = a
+ if post_init:
+ lines.append("self.__attrs_post_init__()")
+
+ return """\
+def __init__(self, {args}):
+ {lines}
+""".format(
+ args=", ".join(args),
+ lines="\n ".join(lines) if lines else "pass",
+ ), names_for_globals
+
+
+class Attribute(object):
+ """
+ *Read-only* representation of an attribute.
+
+ :attribute name: The name of the attribute.
+
+ Plus *all* arguments of :func:`attr.ib`.
+ """
+ __slots__ = (
+ "name", "default", "validator", "repr", "cmp", "hash", "init",
+ "convert", "metadata", "type"
+ )
+
+ def __init__(self, name, default, validator, repr, cmp, hash, init,
+ convert=None, metadata=None, type=None):
+ # Cache this descriptor here to speed things up later.
+ bound_setattr = _obj_setattr.__get__(self, Attribute)
+
+ bound_setattr("name", name)
+ bound_setattr("default", default)
+ bound_setattr("validator", validator)
+ bound_setattr("repr", repr)
+ bound_setattr("cmp", cmp)
+ bound_setattr("hash", hash)
+ bound_setattr("init", init)
+ bound_setattr("convert", convert)
+ bound_setattr("metadata", (metadata_proxy(metadata) if metadata
+ else _empty_metadata_singleton))
+ bound_setattr("type", type)
+
+ def __setattr__(self, name, value):
+ raise FrozenInstanceError()
+
+ @classmethod
+ def from_counting_attr(cls, name, ca, type=None):
+ # type holds the annotated value. deal with conflicts:
+ if type is None:
+ type = ca.type
+ elif ca.type is not None:
+ raise ValueError(
+ "Type annotation and type argument cannot both be present"
+ )
+ inst_dict = {
+ k: getattr(ca, k)
+ for k
+ in Attribute.__slots__
+ if k not in (
+ "name", "validator", "default", "type"
+ ) # exclude methods
+ }
+ return cls(name=name, validator=ca._validator, default=ca._default,
+ type=type, **inst_dict)
+
+ # Don't use _add_pickle since fields(Attribute) doesn't work
+ def __getstate__(self):
+ """
+ Play nice with pickle.
+ """
+ return tuple(getattr(self, name) if name != "metadata"
+ else dict(self.metadata)
+ for name in self.__slots__)
+
+ def __setstate__(self, state):
+ """
+ Play nice with pickle.
+ """
+ bound_setattr = _obj_setattr.__get__(self, Attribute)
+ for name, value in zip(self.__slots__, state):
+ if name != "metadata":
+ bound_setattr(name, value)
+ else:
+ bound_setattr(name, metadata_proxy(value) if value else
+ _empty_metadata_singleton)
+
+
+_a = [Attribute(name=name, default=NOTHING, validator=None,
+ repr=True, cmp=True, hash=(name != "metadata"), init=True)
+ for name in Attribute.__slots__]
+
+Attribute = _add_hash(
+ _add_cmp(_add_repr(Attribute, attrs=_a), attrs=_a),
+ attrs=[a for a in _a if a.hash]
+)
+
+
+class _CountingAttr(object):
+ """
+ Intermediate representation of attributes that uses a counter to preserve
+ the order in which the attributes have been defined.
+
+ *Internal* data structure of the attrs library. Running into is most
+ likely the result of a bug like a forgotten `@attr.s` decorator.
+ """
+ __slots__ = ("counter", "_default", "repr", "cmp", "hash", "init",
+ "metadata", "_validator", "convert", "type")
+ __attrs_attrs__ = tuple(
+ Attribute(name=name, default=NOTHING, validator=None,
+ repr=True, cmp=True, hash=True, init=True)
+ for name
+ in ("counter", "_default", "repr", "cmp", "hash", "init",)
+ ) + (
+ Attribute(name="metadata", default=None, validator=None,
+ repr=True, cmp=True, hash=False, init=True),
+ )
+ cls_counter = 0
+
+ def __init__(self, default, validator, repr, cmp, hash, init, convert,
+ metadata, type):
+ _CountingAttr.cls_counter += 1
+ self.counter = _CountingAttr.cls_counter
+ self._default = default
+ # If validator is a list/tuple, wrap it using helper validator.
+ if validator and isinstance(validator, (list, tuple)):
+ self._validator = and_(*validator)
+ else:
+ self._validator = validator
+ self.repr = repr
+ self.cmp = cmp
+ self.hash = hash
+ self.init = init
+ self.convert = convert
+ self.metadata = metadata
+ self.type = type
+
+ def validator(self, meth):
+ """
+ Decorator that adds *meth* to the list of validators.
+
+ Returns *meth* unchanged.
+
+ .. versionadded:: 17.1.0
+ """
+ if self._validator is None:
+ self._validator = meth
+ else:
+ self._validator = and_(self._validator, meth)
+ return meth
+
+ def default(self, meth):
+ """
+ Decorator that allows to set the default for an attribute.
+
+ Returns *meth* unchanged.
+
+ :raises DefaultAlreadySetError: If default has been set before.
+
+ .. versionadded:: 17.1.0
+ """
+ if self._default is not NOTHING:
+ raise DefaultAlreadySetError()
+
+ self._default = Factory(meth, takes_self=True)
+
+ return meth
+
+
+_CountingAttr = _add_cmp(_add_repr(_CountingAttr))
+
+
+@attrs(slots=True, init=False, hash=True)
+class Factory(object):
+ """
+ Stores a factory callable.
+
+ If passed as the default value to :func:`attr.ib`, the factory is used to
+ generate a new value.
+
+ :param callable factory: A callable that takes either none or exactly one
+ mandatory positional argument depending on *takes_self*.
+ :param bool takes_self: Pass the partially initialized instance that is
+ being initialized as a positional argument.
+
+ .. versionadded:: 17.1.0 *takes_self*
+ """
+ factory = attrib()
+ takes_self = attrib()
+
+ def __init__(self, factory, takes_self=False):
+ """
+ `Factory` is part of the default machinery so if we want a default
+ value here, we have to implement it ourselves.
+ """
+ self.factory = factory
+ self.takes_self = takes_self
+
+
+def make_class(name, attrs, bases=(object,), **attributes_arguments):
+ """
+ A quick way to create a new class called *name* with *attrs*.
+
+ :param name: The name for the new class.
+ :type name: str
+
+ :param attrs: A list of names or a dictionary of mappings of names to
+ attributes.
+ :type attrs: :class:`list` or :class:`dict`
+
+ :param tuple bases: Classes that the new class will subclass.
+
+ :param attributes_arguments: Passed unmodified to :func:`attr.s`.
+
+ :return: A new class with *attrs*.
+ :rtype: type
+
+ .. versionadded:: 17.1.0 *bases*
+ """
+ if isinstance(attrs, dict):
+ cls_dict = attrs
+ elif isinstance(attrs, (list, tuple)):
+ cls_dict = dict((a, attrib()) for a in attrs)
+ else:
+ raise TypeError("attrs argument must be a dict or a list.")
+
+ post_init = cls_dict.pop("__attrs_post_init__", None)
+ type_ = type(
+ name,
+ bases,
+ {} if post_init is None else {"__attrs_post_init__": post_init}
+ )
+ # For pickling to work, the __module__ variable needs to be set to the
+ # frame where the class is created. Bypass this step in environments where
+ # sys._getframe is not defined (Jython for example) or sys._getframe is not
+ # defined for arguments greater than 0 (IronPython).
+ try:
+ type_.__module__ = sys._getframe(1).f_globals.get(
+ "__name__", "__main__",
+ )
+ except (AttributeError, ValueError):
+ pass
+
+ return _attrs(these=cls_dict, **attributes_arguments)(type_)
+
+
+# These are required by within this module so we define them here and merely
+# import into .validators.
+
+
+@attrs(slots=True, hash=True)
+class _AndValidator(object):
+ """
+ Compose many validators to a single one.
+ """
+ _validators = attrib()
+
+ def __call__(self, inst, attr, value):
+ for v in self._validators:
+ v(inst, attr, value)
+
+
+def and_(*validators):
+ """
+ A validator that composes multiple validators into one.
+
+ When called on a value, it runs all wrapped validators.
+
+ :param validators: Arbitrary number of validators.
+ :type validators: callables
+
+ .. versionadded:: 17.1.0
+ """
+ vals = []
+ for validator in validators:
+ vals.extend(
+ validator._validators if isinstance(validator, _AndValidator)
+ else [validator]
+ )
+
+ return _AndValidator(tuple(vals))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/converters.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/converters.py
new file mode 100644
index 00000000000..3b3bac92be5
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/converters.py
@@ -0,0 +1,24 @@
+"""
+Commonly useful converters.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+
+def optional(converter):
+ """
+ A converter that allows an attribute to be optional. An optional attribute
+ is one which can be set to ``None``.
+
+ :param callable converter: the converter that is used for non-``None``
+ values.
+
+ .. versionadded:: 17.1.0
+ """
+
+ def optional_converter(val):
+ if val is None:
+ return None
+ return converter(val)
+
+ return optional_converter
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/exceptions.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/exceptions.py
new file mode 100644
index 00000000000..f949f3c9c01
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/exceptions.py
@@ -0,0 +1,48 @@
+from __future__ import absolute_import, division, print_function
+
+
+class FrozenInstanceError(AttributeError):
+ """
+ A frozen/immutable instance has been attempted to be modified.
+
+ It mirrors the behavior of ``namedtuples`` by using the same error message
+ and subclassing :exc:`AttributeError`.
+
+ .. versionadded:: 16.1.0
+ """
+ msg = "can't set attribute"
+ args = [msg]
+
+
+class AttrsAttributeNotFoundError(ValueError):
+ """
+ An ``attrs`` function couldn't find an attribute that the user asked for.
+
+ .. versionadded:: 16.2.0
+ """
+
+
+class NotAnAttrsClassError(ValueError):
+ """
+ A non-``attrs`` class has been passed into an ``attrs`` function.
+
+ .. versionadded:: 16.2.0
+ """
+
+
+class DefaultAlreadySetError(RuntimeError):
+ """
+ A default has been set using ``attr.ib()`` and is attempted to be reset
+ using the decorator.
+
+ .. versionadded:: 17.1.0
+ """
+
+
+class UnannotatedAttributeError(RuntimeError):
+ """
+ A class with ``auto_attribs=True`` has an ``attr.ib()`` without a type
+ annotation.
+
+ .. versionadded:: 17.3.0
+ """
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/filters.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/filters.py
new file mode 100644
index 00000000000..d1bad35e363
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/filters.py
@@ -0,0 +1,52 @@
+"""
+Commonly useful filters for :func:`attr.asdict`.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+from ._compat import isclass
+from ._make import Attribute
+
+
+def _split_what(what):
+ """
+ Returns a tuple of `frozenset`s of classes and attributes.
+ """
+ return (
+ frozenset(cls for cls in what if isclass(cls)),
+ frozenset(cls for cls in what if isinstance(cls, Attribute)),
+ )
+
+
+def include(*what):
+ """
+ Whitelist *what*.
+
+ :param what: What to whitelist.
+ :type what: :class:`list` of :class:`type` or :class:`attr.Attribute`\ s
+
+ :rtype: :class:`callable`
+ """
+ cls, attrs = _split_what(what)
+
+ def include_(attribute, value):
+ return value.__class__ in cls or attribute in attrs
+
+ return include_
+
+
+def exclude(*what):
+ """
+ Blacklist *what*.
+
+ :param what: What to blacklist.
+ :type what: :class:`list` of classes or :class:`attr.Attribute`\ s.
+
+ :rtype: :class:`callable`
+ """
+ cls, attrs = _split_what(what)
+
+ def exclude_(attribute, value):
+ return value.__class__ not in cls and attribute not in attrs
+
+ return exclude_
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/validators.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/validators.py
new file mode 100644
index 00000000000..f8892fcdfe4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/src/attr/validators.py
@@ -0,0 +1,166 @@
+"""
+Commonly useful validators.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+from ._make import _AndValidator, and_, attrib, attrs
+
+
+__all__ = [
+ "and_",
+ "in_",
+ "instance_of",
+ "optional",
+ "provides",
+]
+
+
+@attrs(repr=False, slots=True, hash=True)
+class _InstanceOfValidator(object):
+ type = attrib()
+
+ def __call__(self, inst, attr, value):
+ """
+ We use a callable class to be able to change the ``__repr__``.
+ """
+ if not isinstance(value, self.type):
+ raise TypeError(
+ "'{name}' must be {type!r} (got {value!r} that is a "
+ "{actual!r})."
+ .format(name=attr.name, type=self.type,
+ actual=value.__class__, value=value),
+ attr, self.type, value,
+ )
+
+ def __repr__(self):
+ return (
+ "<instance_of validator for type {type!r}>"
+ .format(type=self.type)
+ )
+
+
+def instance_of(type):
+ """
+ A validator that raises a :exc:`TypeError` if the initializer is called
+ with a wrong type for this particular attribute (checks are performed using
+ :func:`isinstance` therefore it's also valid to pass a tuple of types).
+
+ :param type: The type to check for.
+ :type type: type or tuple of types
+
+ :raises TypeError: With a human readable error message, the attribute
+ (of type :class:`attr.Attribute`), the expected type, and the value it
+ got.
+ """
+ return _InstanceOfValidator(type)
+
+
+@attrs(repr=False, slots=True, hash=True)
+class _ProvidesValidator(object):
+ interface = attrib()
+
+ def __call__(self, inst, attr, value):
+ """
+ We use a callable class to be able to change the ``__repr__``.
+ """
+ if not self.interface.providedBy(value):
+ raise TypeError(
+ "'{name}' must provide {interface!r} which {value!r} "
+ "doesn't."
+ .format(name=attr.name, interface=self.interface, value=value),
+ attr, self.interface, value,
+ )
+
+ def __repr__(self):
+ return (
+ "<provides validator for interface {interface!r}>"
+ .format(interface=self.interface)
+ )
+
+
+def provides(interface):
+ """
+ A validator that raises a :exc:`TypeError` if the initializer is called
+ with an object that does not provide the requested *interface* (checks are
+ performed using ``interface.providedBy(value)`` (see `zope.interface
+ <https://zopeinterface.readthedocs.io/en/latest/>`_).
+
+ :param zope.interface.Interface interface: The interface to check for.
+
+ :raises TypeError: With a human readable error message, the attribute
+ (of type :class:`attr.Attribute`), the expected interface, and the
+ value it got.
+ """
+ return _ProvidesValidator(interface)
+
+
+@attrs(repr=False, slots=True, hash=True)
+class _OptionalValidator(object):
+ validator = attrib()
+
+ def __call__(self, inst, attr, value):
+ if value is None:
+ return
+
+ self.validator(inst, attr, value)
+
+ def __repr__(self):
+ return (
+ "<optional validator for {what} or None>"
+ .format(what=repr(self.validator))
+ )
+
+
+def optional(validator):
+ """
+ A validator that makes an attribute optional. An optional attribute is one
+ which can be set to ``None`` in addition to satisfying the requirements of
+ the sub-validator.
+
+ :param validator: A validator (or a list of validators) that is used for
+ non-``None`` values.
+ :type validator: callable or :class:`list` of callables.
+
+ .. versionadded:: 15.1.0
+ .. versionchanged:: 17.1.0 *validator* can be a list of validators.
+ """
+ if isinstance(validator, list):
+ return _OptionalValidator(_AndValidator(validator))
+ return _OptionalValidator(validator)
+
+
+@attrs(repr=False, slots=True, hash=True)
+class _InValidator(object):
+ options = attrib()
+
+ def __call__(self, inst, attr, value):
+ if value not in self.options:
+ raise ValueError(
+ "'{name}' must be in {options!r} (got {value!r})"
+ .format(name=attr.name, options=self.options, value=value)
+ )
+
+ def __repr__(self):
+ return (
+ "<in_ validator with options {options!r}>"
+ .format(options=self.options)
+ )
+
+
+def in_(options):
+ """
+ A validator that raises a :exc:`ValueError` if the initializer is called
+ with a value that does not belong in the options provided. The check is
+ performed using ``value in options``.
+
+ :param options: Allowed options.
+ :type options: list, tuple, :class:`enum.Enum`, ...
+
+ :raises ValueError: With a human readable error message, the attribute (of
+ type :class:`attr.Attribute`), the expected options, and the value it
+ got.
+
+ .. versionadded:: 17.1.0
+ """
+ return _InValidator(options)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/__init__.py
index e69de29bb2d..e69de29bb2d 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_annotations.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_annotations.py
new file mode 100644
index 00000000000..602f21bd5e6
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_annotations.py
@@ -0,0 +1,156 @@
+"""
+Tests for PEP-526 type annotations.
+
+Python 3.6+ only.
+"""
+
+import types
+import typing
+
+import pytest
+
+import attr
+
+from attr.exceptions import UnannotatedAttributeError
+
+
+class TestAnnotations:
+ """
+ Tests for types derived from variable annotations (PEP-526).
+ """
+
+ def test_basic_annotations(self):
+ """
+ Sets the `Attribute.type` attr from basic type annotations.
+ """
+ @attr.s
+ class C:
+ x: int = attr.ib()
+ y = attr.ib(type=str)
+ z = attr.ib()
+
+ assert int is attr.fields(C).x.type
+ assert str is attr.fields(C).y.type
+ assert None is attr.fields(C).z.type
+
+ def test_catches_basic_type_conflict(self):
+ """
+ Raises ValueError if type is specified both ways.
+ """
+ with pytest.raises(ValueError) as e:
+ @attr.s
+ class C:
+ x: int = attr.ib(type=int)
+
+ assert (
+ "Type annotation and type argument cannot both be present",
+ ) == e.value.args
+
+ def test_typing_annotations(self):
+ """
+ Sets the `Attribute.type` attr from typing annotations.
+ """
+ @attr.s
+ class C:
+ x: typing.List[int] = attr.ib()
+ y = attr.ib(type=typing.Optional[str])
+
+ assert typing.List[int] is attr.fields(C).x.type
+ assert typing.Optional[str] is attr.fields(C).y.type
+
+ def test_only_attrs_annotations_collected(self):
+ """
+ Annotations that aren't set to an attr.ib are ignored.
+ """
+ @attr.s
+ class C:
+ x: typing.List[int] = attr.ib()
+ y: int
+
+ assert 1 == len(attr.fields(C))
+
+ @pytest.mark.parametrize("slots", [True, False])
+ def test_auto_attribs(self, slots):
+ """
+ If *auto_attribs* is True, bare annotations are collected too.
+ Defaults work and class variables are ignored.
+ """
+ @attr.s(auto_attribs=True, slots=slots)
+ class C:
+ cls_var: typing.ClassVar[int] = 23
+ a: int
+ x: typing.List[int] = attr.Factory(list)
+ y: int = 2
+ z: int = attr.ib(default=3)
+ foo: typing.Any = None
+
+ i = C(42)
+ assert "C(a=42, x=[], y=2, z=3, foo=None)" == repr(i)
+
+ attr_names = set(a.name for a in C.__attrs_attrs__)
+ assert "a" in attr_names # just double check that the set works
+ assert "cls_var" not in attr_names
+
+ assert int == attr.fields(C).a.type
+
+ assert attr.Factory(list) == attr.fields(C).x.default
+ assert typing.List[int] == attr.fields(C).x.type
+
+ assert int == attr.fields(C).y.type
+ assert 2 == attr.fields(C).y.default
+
+ assert int == attr.fields(C).z.type
+
+ assert typing.Any == attr.fields(C).foo.type
+
+ # Class body is clean.
+ if slots is False:
+ with pytest.raises(AttributeError):
+ C.y
+
+ assert 2 == i.y
+ else:
+ assert isinstance(C.y, types.MemberDescriptorType)
+
+ i.y = 23
+ assert 23 == i.y
+
+ @pytest.mark.parametrize("slots", [True, False])
+ def test_auto_attribs_unannotated(self, slots):
+ """
+ Unannotated `attr.ib`s raise an error.
+ """
+ with pytest.raises(UnannotatedAttributeError) as e:
+ @attr.s(slots=slots, auto_attribs=True)
+ class C:
+ v = attr.ib()
+ x: int
+ y = attr.ib()
+ z: str
+
+ assert (
+ "The following `attr.ib`s lack a type annotation: v, y.",
+ ) == e.value.args
+
+ @pytest.mark.parametrize("slots", [True, False])
+ def test_auto_attribs_subclassing(self, slots):
+ """
+ Attributes from super classes are inherited, it doesn't matter if the
+ subclass has annotations or not.
+
+ Ref #291
+ """
+ @attr.s(slots=slots, auto_attribs=True)
+ class A:
+ a: int = 1
+
+ @attr.s(slots=slots, auto_attribs=True)
+ class B(A):
+ b: int = 2
+
+ @attr.s(slots=slots, auto_attribs=True)
+ class C(A):
+ pass
+
+ assert "B(a=1, b=2)" == repr(B())
+ assert "C(a=1)" == repr(C())
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_config.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_config.py
new file mode 100644
index 00000000000..287be03a597
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_config.py
@@ -0,0 +1,43 @@
+"""
+Tests for `attr._config`.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+import pytest
+
+from attr import _config
+
+
+class TestConfig(object):
+ def test_default(self):
+ """
+ Run validators by default.
+ """
+ assert True is _config._run_validators
+
+ def test_set_run_validators(self):
+ """
+ Sets `_run_validators`.
+ """
+ _config.set_run_validators(False)
+ assert False is _config._run_validators
+ _config.set_run_validators(True)
+ assert True is _config._run_validators
+
+ def test_get_run_validators(self):
+ """
+ Returns `_run_validators`.
+ """
+ _config._run_validators = False
+ assert _config._run_validators is _config.get_run_validators()
+ _config._run_validators = True
+ assert _config._run_validators is _config.get_run_validators()
+
+ def test_wrong_type(self):
+ """
+ Passing anything else than a boolean raises TypeError.
+ """
+ with pytest.raises(TypeError) as e:
+ _config.set_run_validators("False")
+ assert "'run' must be bool." == e.value.args[0]
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_converters.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_converters.py
new file mode 100644
index 00000000000..daf39d8ace0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_converters.py
@@ -0,0 +1,36 @@
+"""
+Tests for `attr.converters`.
+"""
+
+from __future__ import absolute_import
+
+import pytest
+
+from attr.converters import optional
+
+
+class TestOptional(object):
+ """
+ Tests for `optional`.
+ """
+ def test_success_with_type(self):
+ """
+ Wrapped converter is used as usual if value is not None.
+ """
+ c = optional(int)
+ assert c("42") == 42
+
+ def test_success_with_none(self):
+ """
+ Nothing happens if None.
+ """
+ c = optional(int)
+ assert c(None) is None
+
+ def test_fail(self):
+ """
+ Propagates the underlying conversion error when conversion fails.
+ """
+ c = optional(int)
+ with pytest.raises(ValueError):
+ c("not_an_int")
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_dark_magic.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_dark_magic.py
new file mode 100644
index 00000000000..bc6665cf5fe
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_dark_magic.py
@@ -0,0 +1,382 @@
+"""
+End-to-end tests.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+import pickle
+
+import pytest
+import six
+
+from hypothesis import given
+from hypothesis.strategies import booleans
+
+import attr
+
+from attr._compat import TYPE
+from attr._make import NOTHING, Attribute
+from attr.exceptions import FrozenInstanceError
+
+
+@attr.s
+class C1(object):
+ x = attr.ib(validator=attr.validators.instance_of(int))
+ y = attr.ib()
+
+
+@attr.s(slots=True)
+class C1Slots(object):
+ x = attr.ib(validator=attr.validators.instance_of(int))
+ y = attr.ib()
+
+
+foo = None
+
+
+@attr.s()
+class C2(object):
+ x = attr.ib(default=foo)
+ y = attr.ib(default=attr.Factory(list))
+
+
+@attr.s(slots=True)
+class C2Slots(object):
+ x = attr.ib(default=foo)
+ y = attr.ib(default=attr.Factory(list))
+
+
+@attr.s
+class Super(object):
+ x = attr.ib()
+
+ def meth(self):
+ return self.x
+
+
+@attr.s(slots=True)
+class SuperSlots(object):
+ x = attr.ib()
+
+ def meth(self):
+ return self.x
+
+
+@attr.s
+class Sub(Super):
+ y = attr.ib()
+
+
+@attr.s(slots=True)
+class SubSlots(SuperSlots):
+ y = attr.ib()
+
+
+@attr.s(frozen=True, slots=True)
+class Frozen(object):
+ x = attr.ib()
+
+
+@attr.s
+class SubFrozen(Frozen):
+ y = attr.ib()
+
+
+@attr.s(frozen=True, slots=False)
+class FrozenNoSlots(object):
+ x = attr.ib()
+
+
+class Meta(type):
+ pass
+
+
+@attr.s
+@six.add_metaclass(Meta)
+class WithMeta(object):
+ pass
+
+
+@attr.s(slots=True)
+@six.add_metaclass(Meta)
+class WithMetaSlots(object):
+ pass
+
+
+FromMakeClass = attr.make_class("FromMakeClass", ["x"])
+
+
+class TestDarkMagic(object):
+ """
+ Integration tests.
+ """
+ @pytest.mark.parametrize("cls", [C2, C2Slots])
+ def test_fields(self, cls):
+ """
+ `attr.fields` works.
+ """
+ assert (
+ Attribute(name="x", default=foo, validator=None,
+ repr=True, cmp=True, hash=None, init=True),
+ Attribute(name="y", default=attr.Factory(list), validator=None,
+ repr=True, cmp=True, hash=None, init=True),
+ ) == attr.fields(cls)
+
+ @pytest.mark.parametrize("cls", [C1, C1Slots])
+ def test_asdict(self, cls):
+ """
+ `attr.asdict` works.
+ """
+ assert {
+ "x": 1,
+ "y": 2,
+ } == attr.asdict(cls(x=1, y=2))
+
+ @pytest.mark.parametrize("cls", [C1, C1Slots])
+ def test_validator(self, cls):
+ """
+ `instance_of` raises `TypeError` on type mismatch.
+ """
+ with pytest.raises(TypeError) as e:
+ cls("1", 2)
+
+ # Using C1 explicitly, since slot classes don't support this.
+ assert (
+ "'x' must be <{type} 'int'> (got '1' that is a <{type} "
+ "'str'>).".format(type=TYPE),
+ attr.fields(C1).x, int, "1",
+ ) == e.value.args
+
+ @given(booleans())
+ def test_renaming(self, slots):
+ """
+ Private members are renamed but only in `__init__`.
+ """
+ @attr.s(slots=slots)
+ class C3(object):
+ _x = attr.ib()
+
+ assert "C3(_x=1)" == repr(C3(x=1))
+
+ @given(booleans(), booleans())
+ def test_programmatic(self, slots, frozen):
+ """
+ `attr.make_class` works.
+ """
+ PC = attr.make_class("PC", ["a", "b"], slots=slots, frozen=frozen)
+ assert (
+ Attribute(name="a", default=NOTHING, validator=None,
+ repr=True, cmp=True, hash=None, init=True),
+ Attribute(name="b", default=NOTHING, validator=None,
+ repr=True, cmp=True, hash=None, init=True),
+ ) == attr.fields(PC)
+
+ @pytest.mark.parametrize("cls", [Sub, SubSlots])
+ def test_subclassing_with_extra_attrs(self, cls):
+ """
+ Sub-classing (where the subclass has extra attrs) does what you'd hope
+ for.
+ """
+ obj = object()
+ i = cls(x=obj, y=2)
+ assert i.x is i.meth() is obj
+ assert i.y == 2
+ if cls is Sub:
+ assert "Sub(x={obj}, y=2)".format(obj=obj) == repr(i)
+ else:
+ assert "SubSlots(x={obj}, y=2)".format(obj=obj) == repr(i)
+
+ @pytest.mark.parametrize("base", [Super, SuperSlots])
+ def test_subclass_without_extra_attrs(self, base):
+ """
+ Sub-classing (where the subclass does not have extra attrs) still
+ behaves the same as a subclass with extra attrs.
+ """
+ class Sub2(base):
+ pass
+
+ obj = object()
+ i = Sub2(x=obj)
+ assert i.x is i.meth() is obj
+ assert "Sub2(x={obj})".format(obj=obj) == repr(i)
+
+ @pytest.mark.parametrize("frozen_class", [
+ Frozen, # has slots=True
+ attr.make_class("FrozenToo", ["x"], slots=False, frozen=True),
+ ])
+ def test_frozen_instance(self, frozen_class):
+ """
+ Frozen instances can't be modified (easily).
+ """
+ frozen = frozen_class(1)
+
+ with pytest.raises(FrozenInstanceError) as e:
+ frozen.x = 2
+
+ with pytest.raises(FrozenInstanceError) as e:
+ del frozen.x
+
+ assert e.value.args[0] == "can't set attribute"
+ assert 1 == frozen.x
+
+ @pytest.mark.parametrize("cls",
+ [C1, C1Slots, C2, C2Slots, Super, SuperSlots,
+ Sub, SubSlots, Frozen, FrozenNoSlots,
+ FromMakeClass])
+ @pytest.mark.parametrize("protocol",
+ range(2, pickle.HIGHEST_PROTOCOL + 1))
+ def test_pickle_attributes(self, cls, protocol):
+ """
+ Pickling/un-pickling of Attribute instances works.
+ """
+ for attribute in attr.fields(cls):
+ assert attribute == pickle.loads(pickle.dumps(attribute, protocol))
+
+ @pytest.mark.parametrize("cls",
+ [C1, C1Slots, C2, C2Slots, Super, SuperSlots,
+ Sub, SubSlots, Frozen, FrozenNoSlots,
+ FromMakeClass])
+ @pytest.mark.parametrize("protocol",
+ range(2, pickle.HIGHEST_PROTOCOL + 1))
+ def test_pickle_object(self, cls, protocol):
+ """
+ Pickle object serialization works on all kinds of attrs classes.
+ """
+ if len(attr.fields(cls)) == 2:
+ obj = cls(123, 456)
+ else:
+ obj = cls(123)
+ assert repr(obj) == repr(pickle.loads(pickle.dumps(obj, protocol)))
+
+ def test_subclassing_frozen_gives_frozen(self):
+ """
+ The frozen-ness of classes is inherited. Subclasses of frozen classes
+ are also frozen and can be instantiated.
+ """
+ i = SubFrozen("foo", "bar")
+
+ assert i.x == "foo"
+ assert i.y == "bar"
+
+ @pytest.mark.parametrize("cls", [WithMeta, WithMetaSlots])
+ def test_metaclass_preserved(self, cls):
+ """
+ Metaclass data is preserved.
+ """
+ assert Meta == type(cls)
+
+ def test_default_decorator(self):
+ """
+ Default decorator sets the default and the respective method gets
+ called.
+ """
+ @attr.s
+ class C(object):
+ x = attr.ib(default=1)
+ y = attr.ib()
+
+ @y.default
+ def compute(self):
+ return self.x + 1
+
+ assert C(1, 2) == C()
+
+ @pytest.mark.parametrize("slots", [True, False])
+ @pytest.mark.parametrize("frozen", [True, False])
+ def test_attrib_overwrite(self, slots, frozen):
+ """
+ Subclasses can overwrite attributes of their superclass.
+ """
+ @attr.s(slots=slots, frozen=frozen)
+ class SubOverwrite(Super):
+ x = attr.ib(default=attr.Factory(list))
+
+ assert SubOverwrite([]) == SubOverwrite()
+
+ def test_dict_patch_class(self):
+ """
+ dict-classes are never replaced.
+ """
+ class C(object):
+ x = attr.ib()
+
+ C_new = attr.s(C)
+
+ assert C_new is C
+
+ def test_hash_by_id(self):
+ """
+ With dict classes, hashing by ID is active for hash=False even on
+ Python 3. This is incorrect behavior but we have to retain it for
+ backward compatibility.
+ """
+ @attr.s(hash=False)
+ class HashByIDBackwardCompat(object):
+ x = attr.ib()
+
+ assert (
+ hash(HashByIDBackwardCompat(1)) != hash(HashByIDBackwardCompat(1))
+ )
+
+ @attr.s(hash=False, cmp=False)
+ class HashByID(object):
+ x = attr.ib()
+
+ assert hash(HashByID(1)) != hash(HashByID(1))
+
+ @attr.s(hash=True)
+ class HashByValues(object):
+ x = attr.ib()
+
+ assert hash(HashByValues(1)) == hash(HashByValues(1))
+
+ def test_handles_different_defaults(self):
+ """
+ Unhashable defaults + subclassing values work.
+ """
+ @attr.s
+ class Unhashable(object):
+ pass
+
+ @attr.s
+ class C(object):
+ x = attr.ib(default=Unhashable())
+
+ @attr.s
+ class D(C):
+ pass
+
+ @pytest.mark.parametrize("slots", [True, False])
+ def test_hash_false_cmp_false(self, slots):
+ """
+ hash=False and cmp=False make a class hashable by ID.
+ """
+ @attr.s(hash=False, cmp=False, slots=slots)
+ class C(object):
+ pass
+
+ assert hash(C()) != hash(C())
+
+ def test_overwrite_super(self):
+ """
+ Super classes can overwrite each other and the attributes are added
+ in the order they are defined.
+ """
+ @attr.s
+ class C(object):
+ c = attr.ib(default=100)
+ x = attr.ib(default=1)
+ b = attr.ib(default=23)
+
+ @attr.s
+ class D(C):
+ a = attr.ib(default=42)
+ x = attr.ib(default=2)
+ d = attr.ib(default=3.14)
+
+ @attr.s
+ class E(D):
+ y = attr.ib(default=3)
+ z = attr.ib(default=4)
+
+ assert "E(c=100, b=23, a=42, x=2, d=3.14, y=3, z=4)" == repr(E())
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_dunders.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_dunders.py
new file mode 100644
index 00000000000..951faba2ffc
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_dunders.py
@@ -0,0 +1,502 @@
+"""
+Tests for dunder methods from `attrib._make`.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+import copy
+
+import pytest
+
+from hypothesis import given
+from hypothesis.strategies import booleans
+
+import attr
+
+from attr._make import (
+ NOTHING, Factory, _add_init, _add_repr, _Nothing, fields, make_class
+)
+from attr.validators import instance_of
+
+from .utils import simple_attr, simple_class
+
+
+CmpC = simple_class(cmp=True)
+CmpCSlots = simple_class(cmp=True, slots=True)
+ReprC = simple_class(repr=True)
+ReprCSlots = simple_class(repr=True, slots=True)
+
+# HashC is hashable by explicit definition while HashCSlots is hashable
+# implicitly.
+HashC = simple_class(hash=True)
+HashCSlots = simple_class(hash=None, cmp=True, frozen=True, slots=True)
+
+
+class InitC(object):
+ __attrs_attrs__ = [simple_attr("a"), simple_attr("b")]
+
+
+InitC = _add_init(InitC, False)
+
+
+class TestAddCmp(object):
+ """
+ Tests for `_add_cmp`.
+ """
+ @given(booleans())
+ def test_cmp(self, slots):
+ """
+ If `cmp` is False, ignore that attribute.
+ """
+ C = make_class("C", {
+ "a": attr.ib(cmp=False),
+ "b": attr.ib()
+ }, slots=slots)
+
+ assert C(1, 2) == C(2, 2)
+
+ @pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
+ def test_equal(self, cls):
+ """
+ Equal objects are detected as equal.
+ """
+ assert cls(1, 2) == cls(1, 2)
+ assert not (cls(1, 2) != cls(1, 2))
+
+ @pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
+ def test_unequal_same_class(self, cls):
+ """
+ Unequal objects of correct type are detected as unequal.
+ """
+ assert cls(1, 2) != cls(2, 1)
+ assert not (cls(1, 2) == cls(2, 1))
+
+ @pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
+ def test_unequal_different_class(self, cls):
+ """
+ Unequal objects of different type are detected even if their attributes
+ match.
+ """
+ class NotCmpC(object):
+ a = 1
+ b = 2
+ assert cls(1, 2) != NotCmpC()
+ assert not (cls(1, 2) == NotCmpC())
+
+ @pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
+ def test_lt(self, cls):
+ """
+ __lt__ compares objects as tuples of attribute values.
+ """
+ for a, b in [
+ ((1, 2), (2, 1)),
+ ((1, 2), (1, 3)),
+ (("a", "b"), ("b", "a")),
+ ]:
+ assert cls(*a) < cls(*b)
+
+ @pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
+ def test_lt_unordable(self, cls):
+ """
+ __lt__ returns NotImplemented if classes differ.
+ """
+ assert NotImplemented == (cls(1, 2).__lt__(42))
+
+ @pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
+ def test_le(self, cls):
+ """
+ __le__ compares objects as tuples of attribute values.
+ """
+ for a, b in [
+ ((1, 2), (2, 1)),
+ ((1, 2), (1, 3)),
+ ((1, 1), (1, 1)),
+ (("a", "b"), ("b", "a")),
+ (("a", "b"), ("a", "b")),
+ ]:
+ assert cls(*a) <= cls(*b)
+
+ @pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
+ def test_le_unordable(self, cls):
+ """
+ __le__ returns NotImplemented if classes differ.
+ """
+ assert NotImplemented == (cls(1, 2).__le__(42))
+
+ @pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
+ def test_gt(self, cls):
+ """
+ __gt__ compares objects as tuples of attribute values.
+ """
+ for a, b in [
+ ((2, 1), (1, 2)),
+ ((1, 3), (1, 2)),
+ (("b", "a"), ("a", "b")),
+ ]:
+ assert cls(*a) > cls(*b)
+
+ @pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
+ def test_gt_unordable(self, cls):
+ """
+ __gt__ returns NotImplemented if classes differ.
+ """
+ assert NotImplemented == (cls(1, 2).__gt__(42))
+
+ @pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
+ def test_ge(self, cls):
+ """
+ __ge__ compares objects as tuples of attribute values.
+ """
+ for a, b in [
+ ((2, 1), (1, 2)),
+ ((1, 3), (1, 2)),
+ ((1, 1), (1, 1)),
+ (("b", "a"), ("a", "b")),
+ (("a", "b"), ("a", "b")),
+ ]:
+ assert cls(*a) >= cls(*b)
+
+ @pytest.mark.parametrize("cls", [CmpC, CmpCSlots])
+ def test_ge_unordable(self, cls):
+ """
+ __ge__ returns NotImplemented if classes differ.
+ """
+ assert NotImplemented == (cls(1, 2).__ge__(42))
+
+
+class TestAddRepr(object):
+ """
+ Tests for `_add_repr`.
+ """
+ @pytest.mark.parametrize("slots", [True, False])
+ def test_repr(self, slots):
+ """
+ If `repr` is False, ignore that attribute.
+ """
+ C = make_class("C", {
+ "a": attr.ib(repr=False),
+ "b": attr.ib()
+ }, slots=slots)
+
+ assert "C(b=2)" == repr(C(1, 2))
+
+ @pytest.mark.parametrize("cls", [ReprC, ReprCSlots])
+ def test_repr_works(self, cls):
+ """
+ repr returns a sensible value.
+ """
+ assert "C(a=1, b=2)" == repr(cls(1, 2))
+
+ def test_underscores(self):
+ """
+ repr does not strip underscores.
+ """
+ class C(object):
+ __attrs_attrs__ = [simple_attr("_x")]
+
+ C = _add_repr(C)
+ i = C()
+ i._x = 42
+
+ assert "C(_x=42)" == repr(i)
+
+ @given(add_str=booleans(), slots=booleans())
+ def test_str(self, add_str, slots):
+ """
+ If str is True, it returns the same as repr.
+
+ This only makes sense when subclassing a class with an poor __str__
+ (like Exceptions).
+ """
+ @attr.s(str=add_str, slots=slots)
+ class Error(Exception):
+ x = attr.ib()
+
+ e = Error(42)
+
+ assert (str(e) == repr(e)) is add_str
+
+ def test_str_no_repr(self):
+ """
+ Raises a ValueError if repr=False and str=True.
+ """
+ with pytest.raises(ValueError) as e:
+ simple_class(repr=False, str=True)
+
+ assert (
+ "__str__ can only be generated if a __repr__ exists."
+ ) == e.value.args[0]
+
+
+class TestAddHash(object):
+ """
+ Tests for `_add_hash`.
+ """
+ def test_enforces_type(self):
+ """
+ The `hash` argument to both attrs and attrib must be None, True, or
+ False.
+ """
+ exc_args = ("Invalid value for hash. Must be True, False, or None.",)
+
+ with pytest.raises(TypeError) as e:
+ make_class("C", {}, hash=1),
+
+ assert exc_args == e.value.args
+
+ with pytest.raises(TypeError) as e:
+ make_class("C", {"a": attr.ib(hash=1)}),
+
+ assert exc_args == e.value.args
+
+ @given(booleans())
+ def test_hash_attribute(self, slots):
+ """
+ If `hash` is False on an attribute, ignore that attribute.
+ """
+ C = make_class("C", {"a": attr.ib(hash=False), "b": attr.ib()},
+ slots=slots, hash=True)
+
+ assert hash(C(1, 2)) == hash(C(2, 2))
+
+ @given(booleans())
+ def test_hash_attribute_mirrors_cmp(self, cmp):
+ """
+ If `hash` is None, the hash generation mirrors `cmp`.
+ """
+ C = make_class("C", {"a": attr.ib(cmp=cmp)}, cmp=True, frozen=True)
+
+ if cmp:
+ assert C(1) != C(2)
+ assert hash(C(1)) != hash(C(2))
+ assert hash(C(1)) == hash(C(1))
+ else:
+ assert C(1) == C(2)
+ assert hash(C(1)) == hash(C(2))
+
+ @given(booleans())
+ def test_hash_mirrors_cmp(self, cmp):
+ """
+ If `hash` is None, the hash generation mirrors `cmp`.
+ """
+ C = make_class("C", {"a": attr.ib()}, cmp=cmp, frozen=True)
+
+ i = C(1)
+
+ assert i == i
+ assert hash(i) == hash(i)
+
+ if cmp:
+ assert C(1) == C(1)
+ assert hash(C(1)) == hash(C(1))
+ else:
+ assert C(1) != C(1)
+ assert hash(C(1)) != hash(C(1))
+
+ @pytest.mark.parametrize("cls", [HashC, HashCSlots])
+ def test_hash_works(self, cls):
+ """
+ __hash__ returns different hashes for different values.
+ """
+ assert hash(cls(1, 2)) != hash(cls(1, 1))
+
+ def test_hash_default(self):
+ """
+ Classes are not hashable by default.
+ """
+ C = make_class("C", {})
+
+ with pytest.raises(TypeError) as e:
+ hash(C())
+
+ assert e.value.args[0] in (
+ "'C' objects are unhashable", # PyPy
+ "unhashable type: 'C'", # CPython
+ )
+
+
+class TestAddInit(object):
+ """
+ Tests for `_add_init`.
+ """
+ @given(booleans(), booleans())
+ def test_init(self, slots, frozen):
+ """
+ If `init` is False, ignore that attribute.
+ """
+ C = make_class("C", {"a": attr.ib(init=False), "b": attr.ib()},
+ slots=slots, frozen=frozen)
+ with pytest.raises(TypeError) as e:
+ C(a=1, b=2)
+
+ assert (
+ "__init__() got an unexpected keyword argument 'a'" ==
+ e.value.args[0]
+ )
+
+ @given(booleans(), booleans())
+ def test_no_init_default(self, slots, frozen):
+ """
+ If `init` is False but a Factory is specified, don't allow passing that
+ argument but initialize it anyway.
+ """
+ C = make_class("C", {
+ "_a": attr.ib(init=False, default=42),
+ "_b": attr.ib(init=False, default=Factory(list)),
+ "c": attr.ib()
+ }, slots=slots, frozen=frozen)
+ with pytest.raises(TypeError):
+ C(a=1, c=2)
+ with pytest.raises(TypeError):
+ C(b=1, c=2)
+
+ i = C(23)
+ assert (42, [], 23) == (i._a, i._b, i.c)
+
+ @given(booleans(), booleans())
+ def test_no_init_order(self, slots, frozen):
+ """
+ If an attribute is `init=False`, it's legal to come after a mandatory
+ attribute.
+ """
+ make_class("C", {
+ "a": attr.ib(default=Factory(list)),
+ "b": attr.ib(init=False),
+ }, slots=slots, frozen=frozen)
+
+ def test_sets_attributes(self):
+ """
+ The attributes are initialized using the passed keywords.
+ """
+ obj = InitC(a=1, b=2)
+ assert 1 == obj.a
+ assert 2 == obj.b
+
+ def test_default(self):
+ """
+ If a default value is present, it's used as fallback.
+ """
+ class C(object):
+ __attrs_attrs__ = [
+ simple_attr(name="a", default=2),
+ simple_attr(name="b", default="hallo"),
+ simple_attr(name="c", default=None),
+ ]
+
+ C = _add_init(C, False)
+ i = C()
+ assert 2 == i.a
+ assert "hallo" == i.b
+ assert None is i.c
+
+ def test_factory(self):
+ """
+ If a default factory is present, it's used as fallback.
+ """
+ class D(object):
+ pass
+
+ class C(object):
+ __attrs_attrs__ = [
+ simple_attr(name="a", default=Factory(list)),
+ simple_attr(name="b", default=Factory(D)),
+ ]
+ C = _add_init(C, False)
+ i = C()
+
+ assert [] == i.a
+ assert isinstance(i.b, D)
+
+ def test_validator(self):
+ """
+ If a validator is passed, call it with the preliminary instance, the
+ Attribute, and the argument.
+ """
+ class VException(Exception):
+ pass
+
+ def raiser(*args):
+ raise VException(*args)
+
+ C = make_class("C", {"a": attr.ib("a", validator=raiser)})
+ with pytest.raises(VException) as e:
+ C(42)
+
+ assert (fields(C).a, 42,) == e.value.args[1:]
+ assert isinstance(e.value.args[0], C)
+
+ def test_validator_slots(self):
+ """
+ If a validator is passed, call it with the preliminary instance, the
+ Attribute, and the argument.
+ """
+ class VException(Exception):
+ pass
+
+ def raiser(*args):
+ raise VException(*args)
+
+ C = make_class("C", {"a": attr.ib("a", validator=raiser)}, slots=True)
+ with pytest.raises(VException) as e:
+ C(42)
+
+ assert (fields(C)[0], 42,) == e.value.args[1:]
+ assert isinstance(e.value.args[0], C)
+
+ @given(booleans())
+ def test_validator_others(self, slots):
+ """
+ Does not interfere when setting non-attrs attributes.
+ """
+ C = make_class("C", {
+ "a": attr.ib("a", validator=instance_of(int))
+ }, slots=slots)
+ i = C(1)
+
+ assert 1 == i.a
+
+ if not slots:
+ i.b = "foo"
+ assert "foo" == i.b
+ else:
+ with pytest.raises(AttributeError):
+ i.b = "foo"
+
+ def test_underscores(self):
+ """
+ The argument names in `__init__` are without leading and trailing
+ underscores.
+ """
+ class C(object):
+ __attrs_attrs__ = [simple_attr("_private")]
+
+ C = _add_init(C, False)
+ i = C(private=42)
+ assert 42 == i._private
+
+
+class TestNothing(object):
+ """
+ Tests for `_Nothing`.
+ """
+ def test_copy(self):
+ """
+ __copy__ returns the same object.
+ """
+ n = _Nothing()
+ assert n is copy.copy(n)
+
+ def test_deepcopy(self):
+ """
+ __deepcopy__ returns the same object.
+ """
+ n = _Nothing()
+ assert n is copy.deepcopy(n)
+
+ def test_eq(self):
+ """
+ All instances are equal.
+ """
+ assert _Nothing() == _Nothing() == NOTHING
+ assert not (_Nothing() != _Nothing())
+ assert 1 != _Nothing()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_filters.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_filters.py
new file mode 100644
index 00000000000..8f7dd694925
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_filters.py
@@ -0,0 +1,94 @@
+"""
+Tests for `attr.filters`.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+import pytest
+
+import attr
+
+from attr import fields
+from attr.filters import _split_what, exclude, include
+
+
+@attr.s
+class C(object):
+ a = attr.ib()
+ b = attr.ib()
+
+
+class TestSplitWhat(object):
+ """
+ Tests for `_split_what`.
+ """
+ def test_splits(self):
+ """
+ Splits correctly.
+ """
+ assert (
+ frozenset((int, str)),
+ frozenset((fields(C).a,)),
+ ) == _split_what((str, fields(C).a, int,))
+
+
+class TestInclude(object):
+ """
+ Tests for `include`.
+ """
+ @pytest.mark.parametrize("incl,value", [
+ ((int,), 42),
+ ((str,), "hello"),
+ ((str, fields(C).a), 42),
+ ((str, fields(C).b), "hello"),
+ ])
+ def test_allow(self, incl, value):
+ """
+ Return True if a class or attribute is whitelisted.
+ """
+ i = include(*incl)
+ assert i(fields(C).a, value) is True
+
+ @pytest.mark.parametrize("incl,value", [
+ ((str,), 42),
+ ((int,), "hello"),
+ ((str, fields(C).b), 42),
+ ((int, fields(C).b), "hello"),
+ ])
+ def test_drop_class(self, incl, value):
+ """
+ Return False on non-whitelisted classes and attributes.
+ """
+ i = include(*incl)
+ assert i(fields(C).a, value) is False
+
+
+class TestExclude(object):
+ """
+ Tests for `exclude`.
+ """
+ @pytest.mark.parametrize("excl,value", [
+ ((str,), 42),
+ ((int,), "hello"),
+ ((str, fields(C).b), 42),
+ ((int, fields(C).b), "hello"),
+ ])
+ def test_allow(self, excl, value):
+ """
+ Return True if class or attribute is not blacklisted.
+ """
+ e = exclude(*excl)
+ assert e(fields(C).a, value) is True
+
+ @pytest.mark.parametrize("excl,value", [
+ ((int,), 42),
+ ((str,), "hello"),
+ ((str, fields(C).a), 42),
+ ((str, fields(C).b), "hello"),
+ ])
+ def test_drop_class(self, excl, value):
+ """
+ Return True on non-blacklisted classes and attributes.
+ """
+ e = exclude(*excl)
+ assert e(fields(C).a, value) is False
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_funcs.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_funcs.py
new file mode 100644
index 00000000000..0167823818e
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_funcs.py
@@ -0,0 +1,527 @@
+"""
+Tests for `attr._funcs`.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+from collections import Mapping, OrderedDict, Sequence
+
+import pytest
+
+from hypothesis import strategies as st
+from hypothesis import HealthCheck, assume, given, settings
+
+import attr
+
+from attr import asdict, assoc, astuple, evolve, fields, has
+from attr._compat import TYPE
+from attr.exceptions import AttrsAttributeNotFoundError
+from attr.validators import instance_of
+
+from .utils import nested_classes, simple_classes
+
+
+MAPPING_TYPES = (dict, OrderedDict)
+SEQUENCE_TYPES = (list, tuple)
+
+
+class TestAsDict(object):
+ """
+ Tests for `asdict`.
+ """
+ @given(st.sampled_from(MAPPING_TYPES))
+ def test_shallow(self, C, dict_factory):
+ """
+ Shallow asdict returns correct dict.
+ """
+ assert {
+ "x": 1,
+ "y": 2,
+ } == asdict(C(x=1, y=2), False, dict_factory=dict_factory)
+
+ @given(st.sampled_from(MAPPING_TYPES))
+ def test_recurse(self, C, dict_class):
+ """
+ Deep asdict returns correct dict.
+ """
+ assert {
+ "x": {"x": 1, "y": 2},
+ "y": {"x": 3, "y": 4},
+ } == asdict(C(
+ C(1, 2),
+ C(3, 4),
+ ), dict_factory=dict_class)
+
+ @given(nested_classes, st.sampled_from(MAPPING_TYPES))
+ @settings(suppress_health_check=[HealthCheck.too_slow])
+ def test_recurse_property(self, cls, dict_class):
+ """
+ Property tests for recursive asdict.
+ """
+ obj = cls()
+ obj_dict = asdict(obj, dict_factory=dict_class)
+
+ def assert_proper_dict_class(obj, obj_dict):
+ assert isinstance(obj_dict, dict_class)
+
+ for field in fields(obj.__class__):
+ field_val = getattr(obj, field.name)
+ if has(field_val.__class__):
+ # This field holds a class, recurse the assertions.
+ assert_proper_dict_class(field_val, obj_dict[field.name])
+ elif isinstance(field_val, Sequence):
+ dict_val = obj_dict[field.name]
+ for item, item_dict in zip(field_val, dict_val):
+ if has(item.__class__):
+ assert_proper_dict_class(item, item_dict)
+ elif isinstance(field_val, Mapping):
+ # This field holds a dictionary.
+ assert isinstance(obj_dict[field.name], dict_class)
+
+ for key, val in field_val.items():
+ if has(val.__class__):
+ assert_proper_dict_class(val,
+ obj_dict[field.name][key])
+
+ assert_proper_dict_class(obj, obj_dict)
+
+ @given(st.sampled_from(MAPPING_TYPES))
+ def test_filter(self, C, dict_factory):
+ """
+ Attributes that are supposed to be skipped are skipped.
+ """
+ assert {
+ "x": {"x": 1},
+ } == asdict(C(
+ C(1, 2),
+ C(3, 4),
+ ), filter=lambda a, v: a.name != "y", dict_factory=dict_factory)
+
+ @given(container=st.sampled_from(SEQUENCE_TYPES))
+ def test_lists_tuples(self, container, C):
+ """
+ If recurse is True, also recurse into lists.
+ """
+ assert {
+ "x": 1,
+ "y": [{"x": 2, "y": 3}, {"x": 4, "y": 5}, "a"],
+ } == asdict(C(1, container([C(2, 3), C(4, 5), "a"])))
+
+ @given(container=st.sampled_from(SEQUENCE_TYPES))
+ def test_lists_tuples_retain_type(self, container, C):
+ """
+ If recurse and retain_collection_types are True, also recurse
+ into lists and do not convert them into list.
+ """
+ assert {
+ "x": 1,
+ "y": container([{"x": 2, "y": 3}, {"x": 4, "y": 5}, "a"]),
+ } == asdict(C(1, container([C(2, 3), C(4, 5), "a"])),
+ retain_collection_types=True)
+
+ @given(st.sampled_from(MAPPING_TYPES))
+ def test_dicts(self, C, dict_factory):
+ """
+ If recurse is True, also recurse into dicts.
+ """
+ res = asdict(C(1, {"a": C(4, 5)}), dict_factory=dict_factory)
+ assert {
+ "x": 1,
+ "y": {"a": {"x": 4, "y": 5}},
+ } == res
+ assert isinstance(res, dict_factory)
+
+ @given(simple_classes(private_attrs=False), st.sampled_from(MAPPING_TYPES))
+ def test_roundtrip(self, cls, dict_class):
+ """
+ Test dumping to dicts and back for Hypothesis-generated classes.
+
+ Private attributes don't round-trip (the attribute name is different
+ than the initializer argument).
+ """
+ instance = cls()
+ dict_instance = asdict(instance, dict_factory=dict_class)
+
+ assert isinstance(dict_instance, dict_class)
+
+ roundtrip_instance = cls(**dict_instance)
+
+ assert instance == roundtrip_instance
+
+ @given(simple_classes())
+ def test_asdict_preserve_order(self, cls):
+ """
+ Field order should be preserved when dumping to OrderedDicts.
+ """
+ instance = cls()
+ dict_instance = asdict(instance, dict_factory=OrderedDict)
+
+ assert [a.name for a in fields(cls)] == list(dict_instance.keys())
+
+
+class TestAsTuple(object):
+ """
+ Tests for `astuple`.
+ """
+ @given(st.sampled_from(SEQUENCE_TYPES))
+ def test_shallow(self, C, tuple_factory):
+ """
+ Shallow astuple returns correct dict.
+ """
+ assert (tuple_factory([1, 2]) ==
+ astuple(C(x=1, y=2), False, tuple_factory=tuple_factory))
+
+ @given(st.sampled_from(SEQUENCE_TYPES))
+ def test_recurse(self, C, tuple_factory):
+ """
+ Deep astuple returns correct tuple.
+ """
+ assert (tuple_factory([tuple_factory([1, 2]),
+ tuple_factory([3, 4])])
+ == astuple(C(
+ C(1, 2),
+ C(3, 4),
+ ),
+ tuple_factory=tuple_factory))
+
+ @given(nested_classes, st.sampled_from(SEQUENCE_TYPES))
+ @settings(suppress_health_check=[HealthCheck.too_slow])
+ def test_recurse_property(self, cls, tuple_class):
+ """
+ Property tests for recursive astuple.
+ """
+ obj = cls()
+ obj_tuple = astuple(obj, tuple_factory=tuple_class)
+
+ def assert_proper_tuple_class(obj, obj_tuple):
+ assert isinstance(obj_tuple, tuple_class)
+ for index, field in enumerate(fields(obj.__class__)):
+ field_val = getattr(obj, field.name)
+ if has(field_val.__class__):
+ # This field holds a class, recurse the assertions.
+ assert_proper_tuple_class(field_val, obj_tuple[index])
+
+ assert_proper_tuple_class(obj, obj_tuple)
+
+ @given(nested_classes, st.sampled_from(SEQUENCE_TYPES))
+ @settings(suppress_health_check=[HealthCheck.too_slow])
+ def test_recurse_retain(self, cls, tuple_class):
+ """
+ Property tests for asserting collection types are retained.
+ """
+ obj = cls()
+ obj_tuple = astuple(obj, tuple_factory=tuple_class,
+ retain_collection_types=True)
+
+ def assert_proper_col_class(obj, obj_tuple):
+ # Iterate over all attributes, and if they are lists or mappings
+ # in the original, assert they are the same class in the dumped.
+ for index, field in enumerate(fields(obj.__class__)):
+ field_val = getattr(obj, field.name)
+ if has(field_val.__class__):
+ # This field holds a class, recurse the assertions.
+ assert_proper_col_class(field_val, obj_tuple[index])
+ elif isinstance(field_val, (list, tuple)):
+ # This field holds a sequence of something.
+ expected_type = type(obj_tuple[index])
+ assert type(field_val) is expected_type # noqa: E721
+ for obj_e, obj_tuple_e in zip(field_val, obj_tuple[index]):
+ if has(obj_e.__class__):
+ assert_proper_col_class(obj_e, obj_tuple_e)
+ elif isinstance(field_val, dict):
+ orig = field_val
+ tupled = obj_tuple[index]
+ assert type(orig) is type(tupled) # noqa: E721
+ for obj_e, obj_tuple_e in zip(orig.items(),
+ tupled.items()):
+ if has(obj_e[0].__class__): # Dict key
+ assert_proper_col_class(obj_e[0], obj_tuple_e[0])
+ if has(obj_e[1].__class__): # Dict value
+ assert_proper_col_class(obj_e[1], obj_tuple_e[1])
+
+ assert_proper_col_class(obj, obj_tuple)
+
+ @given(st.sampled_from(SEQUENCE_TYPES))
+ def test_filter(self, C, tuple_factory):
+ """
+ Attributes that are supposed to be skipped are skipped.
+ """
+ assert tuple_factory([tuple_factory([1, ]), ]) == astuple(C(
+ C(1, 2),
+ C(3, 4),
+ ), filter=lambda a, v: a.name != "y", tuple_factory=tuple_factory)
+
+ @given(container=st.sampled_from(SEQUENCE_TYPES))
+ def test_lists_tuples(self, container, C):
+ """
+ If recurse is True, also recurse into lists.
+ """
+ assert ((1, [(2, 3), (4, 5), "a"])
+ == astuple(C(1, container([C(2, 3), C(4, 5), "a"])))
+ )
+
+ @given(st.sampled_from(SEQUENCE_TYPES))
+ def test_dicts(self, C, tuple_factory):
+ """
+ If recurse is True, also recurse into dicts.
+ """
+ res = astuple(C(1, {"a": C(4, 5)}), tuple_factory=tuple_factory)
+ assert tuple_factory([1, {"a": tuple_factory([4, 5])}]) == res
+ assert isinstance(res, tuple_factory)
+
+ @given(container=st.sampled_from(SEQUENCE_TYPES))
+ def test_lists_tuples_retain_type(self, container, C):
+ """
+ If recurse and retain_collection_types are True, also recurse
+ into lists and do not convert them into list.
+ """
+ assert (
+ (1, container([(2, 3), (4, 5), "a"]))
+ == astuple(C(1, container([C(2, 3), C(4, 5), "a"])),
+ retain_collection_types=True))
+
+ @given(container=st.sampled_from(MAPPING_TYPES))
+ def test_dicts_retain_type(self, container, C):
+ """
+ If recurse and retain_collection_types are True, also recurse
+ into lists and do not convert them into list.
+ """
+ assert (
+ (1, container({"a": (4, 5)}))
+ == astuple(C(1, container({"a": C(4, 5)})),
+ retain_collection_types=True))
+
+ @given(simple_classes(), st.sampled_from(SEQUENCE_TYPES))
+ def test_roundtrip(self, cls, tuple_class):
+ """
+ Test dumping to tuple and back for Hypothesis-generated classes.
+ """
+ instance = cls()
+ tuple_instance = astuple(instance, tuple_factory=tuple_class)
+
+ assert isinstance(tuple_instance, tuple_class)
+
+ roundtrip_instance = cls(*tuple_instance)
+
+ assert instance == roundtrip_instance
+
+
+class TestHas(object):
+ """
+ Tests for `has`.
+ """
+ def test_positive(self, C):
+ """
+ Returns `True` on decorated classes.
+ """
+ assert has(C)
+
+ def test_positive_empty(self):
+ """
+ Returns `True` on decorated classes even if there are no attributes.
+ """
+ @attr.s
+ class D(object):
+ pass
+
+ assert has(D)
+
+ def test_negative(self):
+ """
+ Returns `False` on non-decorated classes.
+ """
+ assert not has(object)
+
+
+class TestAssoc(object):
+ """
+ Tests for `assoc`.
+ """
+ @given(slots=st.booleans(), frozen=st.booleans())
+ def test_empty(self, slots, frozen):
+ """
+ Empty classes without changes get copied.
+ """
+ @attr.s(slots=slots, frozen=frozen)
+ class C(object):
+ pass
+
+ i1 = C()
+ with pytest.deprecated_call():
+ i2 = assoc(i1)
+
+ assert i1 is not i2
+ assert i1 == i2
+
+ @given(simple_classes())
+ def test_no_changes(self, C):
+ """
+ No changes means a verbatim copy.
+ """
+ i1 = C()
+ with pytest.deprecated_call():
+ i2 = assoc(i1)
+
+ assert i1 is not i2
+ assert i1 == i2
+
+ @given(simple_classes(), st.data())
+ def test_change(self, C, data):
+ """
+ Changes work.
+ """
+ # Take the first attribute, and change it.
+ assume(fields(C)) # Skip classes with no attributes.
+ field_names = [a.name for a in fields(C)]
+ original = C()
+ chosen_names = data.draw(st.sets(st.sampled_from(field_names)))
+ change_dict = {name: data.draw(st.integers())
+ for name in chosen_names}
+
+ with pytest.deprecated_call():
+ changed = assoc(original, **change_dict)
+
+ for k, v in change_dict.items():
+ assert getattr(changed, k) == v
+
+ @given(simple_classes())
+ def test_unknown(self, C):
+ """
+ Wanting to change an unknown attribute raises an
+ AttrsAttributeNotFoundError.
+ """
+ # No generated class will have a four letter attribute.
+ with pytest.raises(AttrsAttributeNotFoundError) as e, \
+ pytest.deprecated_call():
+ assoc(C(), aaaa=2)
+
+ assert (
+ "aaaa is not an attrs attribute on {cls!r}.".format(cls=C),
+ ) == e.value.args
+
+ def test_frozen(self):
+ """
+ Works on frozen classes.
+ """
+ @attr.s(frozen=True)
+ class C(object):
+ x = attr.ib()
+ y = attr.ib()
+
+ with pytest.deprecated_call():
+ assert C(3, 2) == assoc(C(1, 2), x=3)
+
+ def test_warning(self):
+ """
+ DeprecationWarning points to the correct file.
+ """
+ @attr.s
+ class C(object):
+ x = attr.ib()
+
+ with pytest.warns(DeprecationWarning) as wi:
+ assert C(2) == assoc(C(1), x=2)
+
+ assert __file__ == wi.list[0].filename
+
+
+class TestEvolve(object):
+ """
+ Tests for `evolve`.
+ """
+ @given(slots=st.booleans(), frozen=st.booleans())
+ def test_empty(self, slots, frozen):
+ """
+ Empty classes without changes get copied.
+ """
+ @attr.s(slots=slots, frozen=frozen)
+ class C(object):
+ pass
+
+ i1 = C()
+ i2 = evolve(i1)
+
+ assert i1 is not i2
+ assert i1 == i2
+
+ @given(simple_classes())
+ def test_no_changes(self, C):
+ """
+ No changes means a verbatim copy.
+ """
+ i1 = C()
+ i2 = evolve(i1)
+
+ assert i1 is not i2
+ assert i1 == i2
+
+ @given(simple_classes(), st.data())
+ def test_change(self, C, data):
+ """
+ Changes work.
+ """
+ # Take the first attribute, and change it.
+ assume(fields(C)) # Skip classes with no attributes.
+ field_names = [a.name for a in fields(C)]
+ original = C()
+ chosen_names = data.draw(st.sets(st.sampled_from(field_names)))
+ # We pay special attention to private attributes, they should behave
+ # like in `__init__`.
+ change_dict = {name.replace('_', ''): data.draw(st.integers())
+ for name in chosen_names}
+ changed = evolve(original, **change_dict)
+ for name in chosen_names:
+ assert getattr(changed, name) == change_dict[name.replace('_', '')]
+
+ @given(simple_classes())
+ def test_unknown(self, C):
+ """
+ Wanting to change an unknown attribute raises an
+ AttrsAttributeNotFoundError.
+ """
+ # No generated class will have a four letter attribute.
+ with pytest.raises(TypeError) as e:
+ evolve(C(), aaaa=2)
+ expected = "__init__() got an unexpected keyword argument 'aaaa'"
+ assert (expected,) == e.value.args
+
+ def test_validator_failure(self):
+ """
+ TypeError isn't swallowed when validation fails within evolve.
+ """
+ @attr.s
+ class C(object):
+ a = attr.ib(validator=instance_of(int))
+
+ with pytest.raises(TypeError) as e:
+ evolve(C(a=1), a="some string")
+ m = e.value.args[0]
+
+ assert m.startswith("'a' must be <{type} 'int'>".format(type=TYPE))
+
+ def test_private(self):
+ """
+ evolve() acts as `__init__` with regards to private attributes.
+ """
+ @attr.s
+ class C(object):
+ _a = attr.ib()
+
+ assert evolve(C(1), a=2)._a == 2
+
+ with pytest.raises(TypeError):
+ evolve(C(1), _a=2)
+
+ with pytest.raises(TypeError):
+ evolve(C(1), a=3, _a=2)
+
+ def test_non_init_attrs(self):
+ """
+ evolve() handles `init=False` attributes.
+ """
+ @attr.s
+ class C(object):
+ a = attr.ib()
+ b = attr.ib(init=False, default=0)
+
+ assert evolve(C(1), a=2).a == 2
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_init_subclass.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_init_subclass.py
new file mode 100644
index 00000000000..b66130ede47
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_init_subclass.py
@@ -0,0 +1,44 @@
+"""
+Tests for `__init_subclass__` related tests.
+
+Python 3.6+ only.
+"""
+
+import pytest
+
+import attr
+
+
+@pytest.mark.parametrize("slots", [True, False])
+def test_init_subclass_vanilla(slots):
+ """
+ `super().__init_subclass__` can be used if the subclass is not an attrs
+ class both with dict and slots classes.
+ """
+ @attr.s(slots=slots)
+ class Base:
+ def __init_subclass__(cls, param, **kw):
+ super().__init_subclass__(**kw)
+ cls.param = param
+
+ class Vanilla(Base, param="foo"):
+ pass
+
+ assert "foo" == Vanilla().param
+
+
+def test_init_subclass_attrs():
+ """
+ `__init_subclass__` works with attrs classes as long as slots=False.
+ """
+ @attr.s(slots=False)
+ class Base:
+ def __init_subclass__(cls, param, **kw):
+ super().__init_subclass__(**kw)
+ cls.param = param
+
+ @attr.s
+ class Attrs(Base, param="foo"):
+ pass
+
+ assert "foo" == Attrs().param
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_make.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_make.py
new file mode 100644
index 00000000000..f8c80228ae8
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_make.py
@@ -0,0 +1,866 @@
+"""
+Tests for `attr._make`.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+import inspect
+import sys
+
+from operator import attrgetter
+
+import pytest
+
+from hypothesis import given
+from hypothesis.strategies import booleans, integers, lists, sampled_from, text
+
+import attr
+
+from attr import _config
+from attr._compat import PY2
+from attr._make import (
+ Attribute, Factory, _AndValidator, _Attributes, _ClassBuilder,
+ _CountingAttr, _transform_attrs, and_, fields, make_class, validate
+)
+from attr.exceptions import DefaultAlreadySetError, NotAnAttrsClassError
+
+from .utils import (
+ gen_attr_names, list_of_attrs, simple_attr, simple_attrs,
+ simple_attrs_without_metadata, simple_classes
+)
+
+
+attrs_st = simple_attrs.map(lambda c: Attribute.from_counting_attr("name", c))
+
+
+class TestCountingAttr(object):
+ """
+ Tests for `attr`.
+ """
+ def test_returns_Attr(self):
+ """
+ Returns an instance of _CountingAttr.
+ """
+ a = attr.ib()
+
+ assert isinstance(a, _CountingAttr)
+
+ def test_validators_lists_to_wrapped_tuples(self):
+ """
+ If a list is passed as validator, it's just converted to a tuple.
+ """
+ def v1(_, __):
+ pass
+
+ def v2(_, __):
+ pass
+
+ a = attr.ib(validator=[v1, v2])
+
+ assert _AndValidator((v1, v2,)) == a._validator
+
+ def test_validator_decorator_single(self):
+ """
+ If _CountingAttr.validator is used as a decorator and there is no
+ decorator set, the decorated method is used as the validator.
+ """
+ a = attr.ib()
+
+ @a.validator
+ def v():
+ pass
+
+ assert v == a._validator
+
+ @pytest.mark.parametrize("wrap", [
+ lambda v: v,
+ lambda v: [v],
+ lambda v: and_(v)
+
+ ])
+ def test_validator_decorator(self, wrap):
+ """
+ If _CountingAttr.validator is used as a decorator and there is already
+ a decorator set, the decorators are composed using `and_`.
+ """
+ def v(_, __):
+ pass
+
+ a = attr.ib(validator=wrap(v))
+
+ @a.validator
+ def v2(self, _, __):
+ pass
+
+ assert _AndValidator((v, v2,)) == a._validator
+
+ def test_default_decorator_already_set(self):
+ """
+ Raise DefaultAlreadySetError if the decorator is used after a default
+ has been set.
+ """
+ a = attr.ib(default=42)
+
+ with pytest.raises(DefaultAlreadySetError):
+ @a.default
+ def f(self):
+ pass
+
+ def test_default_decorator_sets(self):
+ """
+ Decorator wraps the method in a Factory with pass_self=True and sets
+ the default.
+ """
+ a = attr.ib()
+
+ @a.default
+ def f(self):
+ pass
+
+ assert Factory(f, True) == a._default
+
+
+def make_tc():
+ class TransformC(object):
+ z = attr.ib()
+ y = attr.ib()
+ x = attr.ib()
+ a = 42
+ return TransformC
+
+
+class TestTransformAttrs(object):
+ """
+ Tests for `_transform_attrs`.
+ """
+ def test_no_modifications(self):
+ """
+ Doesn't attach __attrs_attrs__ to the class anymore.
+ """
+ C = make_tc()
+ _transform_attrs(C, None, False)
+
+ assert None is getattr(C, "__attrs_attrs__", None)
+
+ def test_normal(self):
+ """
+ Transforms every `_CountingAttr` and leaves others (a) be.
+ """
+ C = make_tc()
+ attrs, _, = _transform_attrs(C, None, False)
+
+ assert ["z", "y", "x"] == [a.name for a in attrs]
+
+ def test_empty(self):
+ """
+ No attributes works as expected.
+ """
+ @attr.s
+ class C(object):
+ pass
+
+ assert _Attributes(((), [])) == _transform_attrs(C, None, False)
+
+ def test_transforms_to_attribute(self):
+ """
+ All `_CountingAttr`s are transformed into `Attribute`s.
+ """
+ C = make_tc()
+ attrs, super_attrs = _transform_attrs(C, None, False)
+
+ assert [] == super_attrs
+ assert 3 == len(attrs)
+ assert all(isinstance(a, Attribute) for a in attrs)
+
+ def test_conflicting_defaults(self):
+ """
+ Raises `ValueError` if attributes with defaults are followed by
+ mandatory attributes.
+ """
+ class C(object):
+ x = attr.ib(default=None)
+ y = attr.ib()
+
+ with pytest.raises(ValueError) as e:
+ _transform_attrs(C, None, False)
+ assert (
+ "No mandatory attributes allowed after an attribute with a "
+ "default value or factory. Attribute in question: Attribute"
+ "(name='y', default=NOTHING, validator=None, repr=True, "
+ "cmp=True, hash=None, init=True, convert=None, "
+ "metadata=mappingproxy({}), type=None)",
+ ) == e.value.args
+
+ def test_these(self):
+ """
+ If these is passed, use it and ignore body and super classes.
+ """
+ class Base(object):
+ z = attr.ib()
+
+ class C(Base):
+ y = attr.ib()
+
+ attrs, super_attrs = _transform_attrs(C, {"x": attr.ib()}, False)
+
+ assert [] == super_attrs
+ assert (
+ simple_attr("x"),
+ ) == attrs
+
+ def test_multiple_inheritance(self):
+ """
+ Order of attributes doesn't get mixed up by multiple inheritance.
+
+ See #285
+ """
+ @attr.s
+ class A(object):
+ a1 = attr.ib(default="a1")
+ a2 = attr.ib(default="a2")
+
+ @attr.s
+ class B(A):
+ b1 = attr.ib(default="b1")
+ b2 = attr.ib(default="b2")
+
+ @attr.s
+ class C(B, A):
+ c1 = attr.ib(default="c1")
+ c2 = attr.ib(default="c2")
+
+ @attr.s
+ class D(A):
+ d1 = attr.ib(default="d1")
+ d2 = attr.ib(default="d2")
+
+ @attr.s
+ class E(D, C):
+ e1 = attr.ib(default="e1")
+ e2 = attr.ib(default="e2")
+
+ assert (
+ "E(a1='a1', a2='a2', b1='b1', b2='b2', c1='c1', c2='c2', d1='d1', "
+ "d2='d2', e1='e1', e2='e2')"
+ ) == repr(E())
+
+
+class TestAttributes(object):
+ """
+ Tests for the `attrs`/`attr.s` class decorator.
+ """
+ @pytest.mark.skipif(not PY2, reason="No old-style classes in Py3")
+ def test_catches_old_style(self):
+ """
+ Raises TypeError on old-style classes.
+ """
+ with pytest.raises(TypeError) as e:
+ @attr.s
+ class C:
+ pass
+
+ assert ("attrs only works with new-style classes.",) == e.value.args
+
+ def test_sets_attrs(self):
+ """
+ Sets the `__attrs_attrs__` class attribute with a list of `Attribute`s.
+ """
+ @attr.s
+ class C(object):
+ x = attr.ib()
+
+ assert "x" == C.__attrs_attrs__[0].name
+ assert all(isinstance(a, Attribute) for a in C.__attrs_attrs__)
+
+ def test_empty(self):
+ """
+ No attributes, no problems.
+ """
+ @attr.s
+ class C3(object):
+ pass
+
+ assert "C3()" == repr(C3())
+ assert C3() == C3()
+
+ @given(attr=attrs_st, attr_name=sampled_from(Attribute.__slots__))
+ def test_immutable(self, attr, attr_name):
+ """
+ Attribute instances are immutable.
+ """
+ with pytest.raises(AttributeError):
+ setattr(attr, attr_name, 1)
+
+ @pytest.mark.parametrize("method_name", [
+ "__repr__",
+ "__eq__",
+ "__hash__",
+ "__init__",
+ ])
+ def test_adds_all_by_default(self, method_name):
+ """
+ If no further arguments are supplied, all add_XXX functions except
+ add_hash are applied. __hash__ is set to None.
+ """
+ # Set the method name to a sentinel and check whether it has been
+ # overwritten afterwards.
+ sentinel = object()
+
+ class C(object):
+ x = attr.ib()
+
+ setattr(C, method_name, sentinel)
+
+ C = attr.s(C)
+ meth = getattr(C, method_name)
+
+ assert sentinel != meth
+ if method_name == "__hash__":
+ assert meth is None
+
+ @pytest.mark.parametrize("arg_name, method_name", [
+ ("repr", "__repr__"),
+ ("cmp", "__eq__"),
+ ("hash", "__hash__"),
+ ("init", "__init__"),
+ ])
+ def test_respects_add_arguments(self, arg_name, method_name):
+ """
+ If a certain `add_XXX` is `False`, `__XXX__` is not added to the class.
+ """
+ # Set the method name to a sentinel and check whether it has been
+ # overwritten afterwards.
+ sentinel = object()
+
+ am_args = {
+ "repr": True,
+ "cmp": True,
+ "hash": True,
+ "init": True
+ }
+ am_args[arg_name] = False
+
+ class C(object):
+ x = attr.ib()
+
+ setattr(C, method_name, sentinel)
+
+ C = attr.s(**am_args)(C)
+
+ assert sentinel == getattr(C, method_name)
+
+ @pytest.mark.skipif(PY2, reason="__qualname__ is PY3-only.")
+ @given(slots_outer=booleans(), slots_inner=booleans())
+ def test_repr_qualname(self, slots_outer, slots_inner):
+ """
+ On Python 3, the name in repr is the __qualname__.
+ """
+ @attr.s(slots=slots_outer)
+ class C(object):
+ @attr.s(slots=slots_inner)
+ class D(object):
+ pass
+
+ assert "C.D()" == repr(C.D())
+ assert "GC.D()" == repr(GC.D())
+
+ @given(slots_outer=booleans(), slots_inner=booleans())
+ def test_repr_fake_qualname(self, slots_outer, slots_inner):
+ """
+ Setting repr_ns overrides a potentially guessed namespace.
+ """
+ @attr.s(slots=slots_outer)
+ class C(object):
+ @attr.s(repr_ns="C", slots=slots_inner)
+ class D(object):
+ pass
+ assert "C.D()" == repr(C.D())
+
+ @pytest.mark.skipif(PY2, reason="__qualname__ is PY3-only.")
+ @given(slots_outer=booleans(), slots_inner=booleans())
+ def test_name_not_overridden(self, slots_outer, slots_inner):
+ """
+ On Python 3, __name__ is different from __qualname__.
+ """
+ @attr.s(slots=slots_outer)
+ class C(object):
+ @attr.s(slots=slots_inner)
+ class D(object):
+ pass
+
+ assert C.D.__name__ == "D"
+ assert C.D.__qualname__ == C.__qualname__ + ".D"
+
+ @given(with_validation=booleans())
+ def test_post_init(self, with_validation, monkeypatch):
+ """
+ Verify that __attrs_post_init__ gets called if defined.
+ """
+ monkeypatch.setattr(_config, "_run_validators", with_validation)
+
+ @attr.s
+ class C(object):
+ x = attr.ib()
+ y = attr.ib()
+
+ def __attrs_post_init__(self2):
+ self2.z = self2.x + self2.y
+
+ c = C(x=10, y=20)
+
+ assert 30 == getattr(c, 'z', None)
+
+ def test_types(self):
+ """
+ Sets the `Attribute.type` attr from type argument.
+ """
+ @attr.s
+ class C(object):
+ x = attr.ib(type=int)
+ y = attr.ib(type=str)
+ z = attr.ib()
+
+ assert int is fields(C).x.type
+ assert str is fields(C).y.type
+ assert None is fields(C).z.type
+
+ @pytest.mark.parametrize("slots", [True, False])
+ def test_clean_class(self, slots):
+ """
+ Attribute definitions do not appear on the class body after @attr.s.
+ """
+ @attr.s(slots=slots)
+ class C(object):
+ x = attr.ib()
+
+ x = getattr(C, "x", None)
+
+ assert not isinstance(x, _CountingAttr)
+
+
+@attr.s
+class GC(object):
+ @attr.s
+ class D(object):
+ pass
+
+
+class TestMakeClass(object):
+ """
+ Tests for `make_class`.
+ """
+ @pytest.mark.parametrize("ls", [
+ list,
+ tuple
+ ])
+ def test_simple(self, ls):
+ """
+ Passing a list of strings creates attributes with default args.
+ """
+ C1 = make_class("C1", ls(["a", "b"]))
+
+ @attr.s
+ class C2(object):
+ a = attr.ib()
+ b = attr.ib()
+
+ assert C1.__attrs_attrs__ == C2.__attrs_attrs__
+
+ def test_dict(self):
+ """
+ Passing a dict of name: _CountingAttr creates an equivalent class.
+ """
+ C1 = make_class("C1", {
+ "a": attr.ib(default=42),
+ "b": attr.ib(default=None),
+ })
+
+ @attr.s
+ class C2(object):
+ a = attr.ib(default=42)
+ b = attr.ib(default=None)
+
+ assert C1.__attrs_attrs__ == C2.__attrs_attrs__
+
+ def test_attr_args(self):
+ """
+ attributes_arguments are passed to attributes
+ """
+ C = make_class("C", ["x"], repr=False)
+
+ assert repr(C(1)).startswith("<tests.test_make.C object at 0x")
+
+ def test_catches_wrong_attrs_type(self):
+ """
+ Raise `TypeError` if an invalid type for attrs is passed.
+ """
+ with pytest.raises(TypeError) as e:
+ make_class("C", object())
+
+ assert (
+ "attrs argument must be a dict or a list.",
+ ) == e.value.args
+
+ def test_bases(self):
+ """
+ Parameter bases default to (object,) and subclasses correctly
+ """
+ class D(object):
+ pass
+
+ cls = make_class("C", {})
+
+ assert cls.__mro__[-1] == object
+
+ cls = make_class("C", {}, bases=(D,))
+
+ assert D in cls.__mro__
+ assert isinstance(cls(), D)
+
+ @pytest.mark.parametrize("slots", [True, False])
+ def test_clean_class(self, slots):
+ """
+ Attribute definitions do not appear on the class body.
+ """
+ C = make_class("C", ["x"], slots=slots)
+
+ x = getattr(C, "x", None)
+
+ assert not isinstance(x, _CountingAttr)
+
+ def test_missing_sys_getframe(self, monkeypatch):
+ """
+ `make_class()` does not fail when `sys._getframe()` is not available.
+ """
+ monkeypatch.delattr(sys, '_getframe')
+ C = make_class("C", ["x"])
+
+ assert 1 == len(C.__attrs_attrs__)
+
+
+class TestFields(object):
+ """
+ Tests for `fields`.
+ """
+ def test_instance(self, C):
+ """
+ Raises `TypeError` on non-classes.
+ """
+ with pytest.raises(TypeError) as e:
+ fields(C(1, 2))
+
+ assert "Passed object must be a class." == e.value.args[0]
+
+ def test_handler_non_attrs_class(self, C):
+ """
+ Raises `ValueError` if passed a non-``attrs`` instance.
+ """
+ with pytest.raises(NotAnAttrsClassError) as e:
+ fields(object)
+ assert (
+ "{o!r} is not an attrs-decorated class.".format(o=object)
+ ) == e.value.args[0]
+
+ @given(simple_classes())
+ def test_fields(self, C):
+ """
+ Returns a list of `Attribute`a.
+ """
+ assert all(isinstance(a, Attribute) for a in fields(C))
+
+ @given(simple_classes())
+ def test_fields_properties(self, C):
+ """
+ Fields returns a tuple with properties.
+ """
+ for attribute in fields(C):
+ assert getattr(fields(C), attribute.name) is attribute
+
+
+class TestConvert(object):
+ """
+ Tests for attribute conversion.
+ """
+ def test_convert(self):
+ """
+ Return value of convert is used as the attribute's value.
+ """
+ C = make_class("C", {
+ "x": attr.ib(convert=lambda v: v + 1),
+ "y": attr.ib(),
+ })
+ c = C(1, 2)
+
+ assert c.x == 2
+ assert c.y == 2
+
+ @given(integers(), booleans())
+ def test_convert_property(self, val, init):
+ """
+ Property tests for attributes with convert.
+ """
+ C = make_class("C", {
+ "y": attr.ib(),
+ "x": attr.ib(init=init, default=val, convert=lambda v: v + 1),
+ })
+ c = C(2)
+
+ assert c.x == val + 1
+ assert c.y == 2
+
+ @given(integers(), booleans())
+ def test_convert_factory_property(self, val, init):
+ """
+ Property tests for attributes with convert, and a factory default.
+ """
+ C = make_class("C", {
+ "y": attr.ib(),
+ "x": attr.ib(
+ init=init,
+ default=Factory(lambda: val),
+ convert=lambda v: v + 1),
+ })
+ c = C(2)
+
+ assert c.x == val + 1
+ assert c.y == 2
+
+ def test_factory_takes_self(self):
+ """
+ If takes_self on factories is True, self is passed.
+ """
+ C = make_class("C", {
+ "x": attr.ib(
+ default=Factory((lambda self: self), takes_self=True)
+ ),
+ })
+
+ i = C()
+
+ assert i is i.x
+
+ def test_factory_hashable(self):
+ """
+ Factory is hashable.
+ """
+ assert hash(Factory(None, False)) == hash(Factory(None, False))
+
+ def test_convert_before_validate(self):
+ """
+ Validation happens after conversion.
+ """
+ def validator(inst, attr, val):
+ raise RuntimeError("foo")
+ C = make_class(
+ "C", {
+ "x": attr.ib(validator=validator, convert=lambda v: 1 / 0),
+ "y": attr.ib(),
+ })
+ with pytest.raises(ZeroDivisionError):
+ C(1, 2)
+
+ def test_frozen(self):
+ """
+ Converters circumvent immutability.
+ """
+ C = make_class("C", {
+ "x": attr.ib(convert=lambda v: int(v)),
+ }, frozen=True)
+ C("1")
+
+
+class TestValidate(object):
+ """
+ Tests for `validate`.
+ """
+ def test_success(self):
+ """
+ If the validator succeeds, nothing gets raised.
+ """
+ C = make_class("C", {
+ "x": attr.ib(validator=lambda *a: None),
+ "y": attr.ib()
+ })
+ validate(C(1, 2))
+
+ def test_propagates(self):
+ """
+ The exception of the validator is handed through.
+ """
+ def raiser(_, __, value):
+ if value == 42:
+ raise FloatingPointError
+
+ C = make_class("C", {"x": attr.ib(validator=raiser)})
+ i = C(1)
+ i.x = 42
+
+ with pytest.raises(FloatingPointError):
+ validate(i)
+
+ def test_run_validators(self):
+ """
+ Setting `_run_validators` to False prevents validators from running.
+ """
+ _config._run_validators = False
+ obj = object()
+
+ def raiser(_, __, ___):
+ raise Exception(obj)
+
+ C = make_class("C", {"x": attr.ib(validator=raiser)})
+ c = C(1)
+ validate(c)
+ assert 1 == c.x
+ _config._run_validators = True
+
+ with pytest.raises(Exception):
+ validate(c)
+
+ with pytest.raises(Exception) as e:
+ C(1)
+ assert (obj,) == e.value.args
+
+ def test_multiple_validators(self):
+ """
+ If a list is passed as a validator, all of its items are treated as one
+ and must pass.
+ """
+ def v1(_, __, value):
+ if value == 23:
+ raise TypeError("omg")
+
+ def v2(_, __, value):
+ if value == 42:
+ raise ValueError("omg")
+
+ C = make_class("C", {"x": attr.ib(validator=[v1, v2])})
+
+ validate(C(1))
+
+ with pytest.raises(TypeError) as e:
+ C(23)
+
+ assert "omg" == e.value.args[0]
+
+ with pytest.raises(ValueError) as e:
+ C(42)
+
+ assert "omg" == e.value.args[0]
+
+ def test_multiple_empty(self):
+ """
+ Empty list/tuple for validator is the same as None.
+ """
+ C1 = make_class("C", {"x": attr.ib(validator=[])})
+ C2 = make_class("C", {"x": attr.ib(validator=None)})
+
+ assert inspect.getsource(C1.__init__) == inspect.getsource(C2.__init__)
+
+
+# Hypothesis seems to cache values, so the lists of attributes come out
+# unsorted.
+sorted_lists_of_attrs = list_of_attrs.map(
+ lambda l: sorted(l, key=attrgetter("counter")))
+
+
+class TestMetadata(object):
+ """
+ Tests for metadata handling.
+ """
+ @given(sorted_lists_of_attrs)
+ def test_metadata_present(self, list_of_attrs):
+ """
+ Assert dictionaries are copied and present.
+ """
+ C = make_class("C", dict(zip(gen_attr_names(), list_of_attrs)))
+
+ for hyp_attr, class_attr in zip(list_of_attrs, fields(C)):
+ if hyp_attr.metadata is None:
+ # The default is a singleton empty dict.
+ assert class_attr.metadata is not None
+ assert len(class_attr.metadata) == 0
+ else:
+ assert hyp_attr.metadata == class_attr.metadata
+
+ # Once more, just to assert getting items and iteration.
+ for k in class_attr.metadata:
+ assert hyp_attr.metadata[k] == class_attr.metadata[k]
+ assert (hyp_attr.metadata.get(k) ==
+ class_attr.metadata.get(k))
+
+ @given(simple_classes(), text())
+ def test_metadata_immutability(self, C, string):
+ """
+ The metadata dict should be best-effort immutable.
+ """
+ for a in fields(C):
+ with pytest.raises(TypeError):
+ a.metadata[string] = string
+ with pytest.raises(AttributeError):
+ a.metadata.update({string: string})
+ with pytest.raises(AttributeError):
+ a.metadata.clear()
+ with pytest.raises(AttributeError):
+ a.metadata.setdefault(string, string)
+
+ for k in a.metadata:
+ # For some reason, Python 3's MappingProxyType throws an
+ # IndexError for deletes on a large integer key.
+ with pytest.raises((TypeError, IndexError)):
+ del a.metadata[k]
+ with pytest.raises(AttributeError):
+ a.metadata.pop(k)
+ with pytest.raises(AttributeError):
+ a.metadata.popitem()
+
+ @given(lists(simple_attrs_without_metadata, min_size=2, max_size=5))
+ def test_empty_metadata_singleton(self, list_of_attrs):
+ """
+ All empty metadata attributes share the same empty metadata dict.
+ """
+ C = make_class("C", dict(zip(gen_attr_names(), list_of_attrs)))
+ for a in fields(C)[1:]:
+ assert a.metadata is fields(C)[0].metadata
+
+
+class TestClassBuilder(object):
+ """
+ Tests for `_ClassBuilder`.
+ """
+ def test_repr_str(self):
+ """
+ Trying to add a `__str__` without having a `__repr__` raises a
+ ValueError.
+ """
+ with pytest.raises(ValueError) as ei:
+ make_class("C", {}, repr=False, str=True)
+
+ assert (
+ "__str__ can only be generated if a __repr__ exists.",
+ ) == ei.value.args
+
+ def test_repr(self):
+ """
+ repr of builder itself makes sense.
+ """
+ class C(object):
+ pass
+
+ b = _ClassBuilder(C, None, True, True, False)
+
+ assert "<_ClassBuilder(cls=C)>" == repr(b)
+
+ def test_returns_self(self):
+ """
+ All methods return the builder for chaining.
+ """
+ class C(object):
+ x = attr.ib()
+
+ b = _ClassBuilder(C, None, True, True, False)
+
+ cls = b.add_cmp().add_hash().add_init().add_repr("ns").add_str() \
+ .build_class()
+
+ assert "ns.C(x=1)" == repr(cls(1))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_slots.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_slots.py
new file mode 100644
index 00000000000..516a797b757
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_slots.py
@@ -0,0 +1,423 @@
+"""
+Unit tests for slot-related functionality.
+"""
+
+import pytest
+
+import attr
+
+from attr._compat import PY2, PYPY, just_warn, make_set_closure_cell
+
+
+# Pympler doesn't work on PyPy.
+try:
+ from pympler.asizeof import asizeof
+ has_pympler = True
+except BaseException: # Won't be an import error.
+ has_pympler = False
+
+
+@attr.s
+class C1(object):
+ x = attr.ib(validator=attr.validators.instance_of(int))
+ y = attr.ib()
+
+ def method(self):
+ return self.x
+
+ @classmethod
+ def classmethod(cls):
+ return "clsmethod"
+
+ @staticmethod
+ def staticmethod():
+ return "staticmethod"
+
+ if not PY2:
+ def my_class(self):
+ return __class__ # NOQA: F821
+
+ def my_super(self):
+ """Just to test out the no-arg super."""
+ return super().__repr__()
+
+
+@attr.s(slots=True, hash=True)
+class C1Slots(object):
+ x = attr.ib(validator=attr.validators.instance_of(int))
+ y = attr.ib()
+
+ def method(self):
+ return self.x
+
+ @classmethod
+ def classmethod(cls):
+ return "clsmethod"
+
+ @staticmethod
+ def staticmethod():
+ return "staticmethod"
+
+ if not PY2:
+ def my_class(self):
+ return __class__ # NOQA: F821
+
+ def my_super(self):
+ """Just to test out the no-arg super."""
+ return super().__repr__()
+
+
+def test_slots_being_used():
+ """
+ The class is really using __slots__.
+ """
+ non_slot_instance = C1(x=1, y="test")
+ slot_instance = C1Slots(x=1, y="test")
+
+ assert "__dict__" not in dir(slot_instance)
+ assert "__slots__" in dir(slot_instance)
+
+ assert "__dict__" in dir(non_slot_instance)
+ assert "__slots__" not in dir(non_slot_instance)
+
+ assert set(["x", "y"]) == set(slot_instance.__slots__)
+
+ if has_pympler:
+ assert asizeof(slot_instance) < asizeof(non_slot_instance)
+
+ non_slot_instance.t = "test"
+ with pytest.raises(AttributeError):
+ slot_instance.t = "test"
+
+ assert 1 == non_slot_instance.method()
+ assert 1 == slot_instance.method()
+
+ assert attr.fields(C1Slots) == attr.fields(C1)
+ assert attr.asdict(slot_instance) == attr.asdict(non_slot_instance)
+
+
+def test_basic_attr_funcs():
+ """
+ Comparison, `__eq__`, `__hash__`, `__repr__`, `attrs.asdict` work.
+ """
+ a = C1Slots(x=1, y=2)
+ b = C1Slots(x=1, y=3)
+ a_ = C1Slots(x=1, y=2)
+
+ # Comparison.
+ assert b > a
+
+ assert a_ == a
+
+ # Hashing.
+ hash(b) # Just to assert it doesn't raise.
+
+ # Repr.
+ assert "C1Slots(x=1, y=2)" == repr(a)
+
+ assert {"x": 1, "y": 2} == attr.asdict(a)
+
+
+def test_inheritance_from_nonslots():
+ """
+ Inheritance from a non-slot class works.
+
+ Note that a slots class inheriting from an ordinary class loses most of the
+ benefits of slots classes, but it should still work.
+ """
+ @attr.s(slots=True, hash=True)
+ class C2Slots(C1):
+ z = attr.ib()
+
+ c2 = C2Slots(x=1, y=2, z="test")
+
+ assert 1 == c2.x
+ assert 2 == c2.y
+ assert "test" == c2.z
+
+ c2.t = "test" # This will work, using the base class.
+
+ assert "test" == c2.t
+
+ assert 1 == c2.method()
+ assert "clsmethod" == c2.classmethod()
+ assert "staticmethod" == c2.staticmethod()
+
+ assert set(["z"]) == set(C2Slots.__slots__)
+
+ c3 = C2Slots(x=1, y=3, z="test")
+
+ assert c3 > c2
+
+ c2_ = C2Slots(x=1, y=2, z="test")
+
+ assert c2 == c2_
+
+ assert "C2Slots(x=1, y=2, z='test')" == repr(c2)
+
+ hash(c2) # Just to assert it doesn't raise.
+
+ assert {"x": 1, "y": 2, "z": "test"} == attr.asdict(c2)
+
+
+def test_nonslots_these():
+ """
+ Enhancing a non-slots class using 'these' works.
+
+ This will actually *replace* the class with another one, using slots.
+ """
+ class SimpleOrdinaryClass(object):
+ def __init__(self, x, y, z):
+ self.x = x
+ self.y = y
+ self.z = z
+
+ def method(self):
+ return self.x
+
+ @classmethod
+ def classmethod(cls):
+ return "clsmethod"
+
+ @staticmethod
+ def staticmethod():
+ return "staticmethod"
+
+ C2Slots = attr.s(these={"x": attr.ib(), "y": attr.ib(), "z": attr.ib()},
+ init=False, slots=True, hash=True)(SimpleOrdinaryClass)
+
+ c2 = C2Slots(x=1, y=2, z="test")
+ assert 1 == c2.x
+ assert 2 == c2.y
+ assert "test" == c2.z
+ with pytest.raises(AttributeError):
+ c2.t = "test" # We have slots now.
+
+ assert 1 == c2.method()
+ assert "clsmethod" == c2.classmethod()
+ assert "staticmethod" == c2.staticmethod()
+
+ assert set(["x", "y", "z"]) == set(C2Slots.__slots__)
+
+ c3 = C2Slots(x=1, y=3, z="test")
+ assert c3 > c2
+ c2_ = C2Slots(x=1, y=2, z="test")
+ assert c2 == c2_
+
+ assert "SimpleOrdinaryClass(x=1, y=2, z='test')" == repr(c2)
+
+ hash(c2) # Just to assert it doesn't raise.
+
+ assert {"x": 1, "y": 2, "z": "test"} == attr.asdict(c2)
+
+
+def test_inheritance_from_slots():
+ """
+ Inheriting from an attr slot class works.
+ """
+ @attr.s(slots=True, hash=True)
+ class C2Slots(C1Slots):
+ z = attr.ib()
+
+ @attr.s(slots=True, hash=True)
+ class C2(C1):
+ z = attr.ib()
+
+ c2 = C2Slots(x=1, y=2, z="test")
+ assert 1 == c2.x
+ assert 2 == c2.y
+ assert "test" == c2.z
+
+ assert set(["z"]) == set(C2Slots.__slots__)
+
+ assert 1 == c2.method()
+ assert "clsmethod" == c2.classmethod()
+ assert "staticmethod" == c2.staticmethod()
+
+ with pytest.raises(AttributeError):
+ c2.t = "test"
+
+ non_slot_instance = C2(x=1, y=2, z="test")
+ if has_pympler:
+ assert asizeof(c2) < asizeof(non_slot_instance)
+
+ c3 = C2Slots(x=1, y=3, z="test")
+ assert c3 > c2
+ c2_ = C2Slots(x=1, y=2, z="test")
+ assert c2 == c2_
+
+ assert "C2Slots(x=1, y=2, z='test')" == repr(c2)
+
+ hash(c2) # Just to assert it doesn't raise.
+
+ assert {"x": 1, "y": 2, "z": "test"} == attr.asdict(c2)
+
+
+def test_bare_inheritance_from_slots():
+ """
+ Inheriting from a bare attr slot class works.
+ """
+ @attr.s(init=False, cmp=False, hash=False, repr=False, slots=True)
+ class C1BareSlots(object):
+ x = attr.ib(validator=attr.validators.instance_of(int))
+ y = attr.ib()
+
+ def method(self):
+ return self.x
+
+ @classmethod
+ def classmethod(cls):
+ return "clsmethod"
+
+ @staticmethod
+ def staticmethod():
+ return "staticmethod"
+
+ @attr.s(init=False, cmp=False, hash=False, repr=False)
+ class C1Bare(object):
+ x = attr.ib(validator=attr.validators.instance_of(int))
+ y = attr.ib()
+
+ def method(self):
+ return self.x
+
+ @classmethod
+ def classmethod(cls):
+ return "clsmethod"
+
+ @staticmethod
+ def staticmethod():
+ return "staticmethod"
+
+ @attr.s(slots=True, hash=True)
+ class C2Slots(C1BareSlots):
+ z = attr.ib()
+
+ @attr.s(slots=True, hash=True)
+ class C2(C1Bare):
+ z = attr.ib()
+
+ c2 = C2Slots(x=1, y=2, z="test")
+ assert 1 == c2.x
+ assert 2 == c2.y
+ assert "test" == c2.z
+
+ assert 1 == c2.method()
+ assert "clsmethod" == c2.classmethod()
+ assert "staticmethod" == c2.staticmethod()
+
+ with pytest.raises(AttributeError):
+ c2.t = "test"
+
+ non_slot_instance = C2(x=1, y=2, z="test")
+ if has_pympler:
+ assert asizeof(c2) < asizeof(non_slot_instance)
+
+ c3 = C2Slots(x=1, y=3, z="test")
+ assert c3 > c2
+ c2_ = C2Slots(x=1, y=2, z="test")
+ assert c2 == c2_
+
+ assert "C2Slots(x=1, y=2, z='test')" == repr(c2)
+
+ hash(c2) # Just to assert it doesn't raise.
+
+ assert {"x": 1, "y": 2, "z": "test"} == attr.asdict(c2)
+
+
+@pytest.mark.skipif(PY2, reason="closure cell rewriting is PY3-only.")
+class TestClosureCellRewriting(object):
+ def test_closure_cell_rewriting(self):
+ """
+ Slot classes support proper closure cell rewriting.
+
+ This affects features like `__class__` and the no-arg super().
+ """
+ non_slot_instance = C1(x=1, y="test")
+ slot_instance = C1Slots(x=1, y="test")
+
+ assert non_slot_instance.my_class() is C1
+ assert slot_instance.my_class() is C1Slots
+
+ # Just assert they return something, and not an exception.
+ assert non_slot_instance.my_super()
+ assert slot_instance.my_super()
+
+ def test_inheritance(self):
+ """
+ Slot classes support proper closure cell rewriting when inheriting.
+
+ This affects features like `__class__` and the no-arg super().
+ """
+ @attr.s
+ class C2(C1):
+ def my_subclass(self):
+ return __class__ # NOQA: F821
+
+ @attr.s
+ class C2Slots(C1Slots):
+ def my_subclass(self):
+ return __class__ # NOQA: F821
+
+ non_slot_instance = C2(x=1, y="test")
+ slot_instance = C2Slots(x=1, y="test")
+
+ assert non_slot_instance.my_class() is C1
+ assert slot_instance.my_class() is C1Slots
+
+ # Just assert they return something, and not an exception.
+ assert non_slot_instance.my_super()
+ assert slot_instance.my_super()
+
+ assert non_slot_instance.my_subclass() is C2
+ assert slot_instance.my_subclass() is C2Slots
+
+ @pytest.mark.parametrize("slots", [True, False])
+ def test_cls_static(self, slots):
+ """
+ Slot classes support proper closure cell rewriting for class- and
+ static methods.
+ """
+ # Python can reuse closure cells, so we create new classes just for
+ # this test.
+
+ @attr.s(slots=slots)
+ class C:
+ @classmethod
+ def clsmethod(cls):
+ return __class__ # noqa: F821
+
+ assert C.clsmethod() is C
+
+ @attr.s(slots=slots)
+ class D:
+ @staticmethod
+ def statmethod():
+ return __class__ # noqa: F821
+
+ assert D.statmethod() is D
+
+ @pytest.mark.skipif(
+ PYPY,
+ reason="ctypes are used only on CPython"
+ )
+ def test_missing_ctypes(self, monkeypatch):
+ """
+ Keeps working if ctypes is missing.
+
+ A warning is emitted that points to the actual code.
+ """
+ monkeypatch.setattr(attr._compat, "import_ctypes", lambda: None)
+ func = make_set_closure_cell()
+
+ with pytest.warns(RuntimeWarning) as wr:
+ func()
+
+ w = wr.pop()
+ assert __file__ == w.filename
+ assert (
+ "Missing ctypes. Some features like bare super() or accessing "
+ "__class__ will not work with slots classes.",
+ ) == w.message.args
+
+ assert just_warn is func
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_validators.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_validators.py
new file mode 100644
index 00000000000..9722da27fdb
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/test_validators.py
@@ -0,0 +1,266 @@
+"""
+Tests for `attr.validators`.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+import pytest
+import zope.interface
+
+import attr
+
+from attr import validators as validator_module
+from attr import has
+from attr._compat import TYPE
+from attr.validators import and_, in_, instance_of, optional, provides
+
+from .utils import simple_attr
+
+
+class TestInstanceOf(object):
+ """
+ Tests for `instance_of`.
+ """
+ def test_success(self):
+ """
+ Nothing happens if types match.
+ """
+ v = instance_of(int)
+ v(None, simple_attr("test"), 42)
+
+ def test_subclass(self):
+ """
+ Subclasses are accepted too.
+ """
+ v = instance_of(int)
+ # yep, bools are a subclass of int :(
+ v(None, simple_attr("test"), True)
+
+ def test_fail(self):
+ """
+ Raises `TypeError` on wrong types.
+ """
+ v = instance_of(int)
+ a = simple_attr("test")
+ with pytest.raises(TypeError) as e:
+ v(None, a, "42")
+ assert (
+ "'test' must be <{type} 'int'> (got '42' that is a <{type} "
+ "'str'>).".format(type=TYPE),
+ a, int, "42",
+
+ ) == e.value.args
+
+ def test_repr(self):
+ """
+ Returned validator has a useful `__repr__`.
+ """
+ v = instance_of(int)
+ assert (
+ "<instance_of validator for type <{type} 'int'>>"
+ .format(type=TYPE)
+ ) == repr(v)
+
+
+def always_pass(_, __, ___):
+ """
+ Toy validator that always passses.
+ """
+
+
+def always_fail(_, __, ___):
+ """
+ Toy validator that always fails.
+ """
+ 0/0
+
+
+class TestAnd(object):
+ def test_success(self):
+ """
+ Succeeds if all wrapped validators succeed.
+ """
+ v = and_(instance_of(int), always_pass)
+
+ v(None, simple_attr("test"), 42)
+
+ def test_fail(self):
+ """
+ Fails if any wrapped validator fails.
+ """
+ v = and_(instance_of(int), always_fail)
+
+ with pytest.raises(ZeroDivisionError):
+ v(None, simple_attr("test"), 42)
+
+ def test_sugar(self):
+ """
+ `and_(v1, v2, v3)` and `[v1, v2, v3]` are equivalent.
+ """
+ @attr.s
+ class C(object):
+ a1 = attr.ib("a1", validator=and_(
+ instance_of(int),
+ ))
+ a2 = attr.ib("a2", validator=[
+ instance_of(int),
+ ])
+
+ assert C.__attrs_attrs__[0].validator == C.__attrs_attrs__[1].validator
+
+
+class IFoo(zope.interface.Interface):
+ """
+ An interface.
+ """
+ def f():
+ """
+ A function called f.
+ """
+
+
+class TestProvides(object):
+ """
+ Tests for `provides`.
+ """
+ def test_success(self):
+ """
+ Nothing happens if value provides requested interface.
+ """
+ @zope.interface.implementer(IFoo)
+ class C(object):
+ def f(self):
+ pass
+
+ v = provides(IFoo)
+ v(None, simple_attr("x"), C())
+
+ def test_fail(self):
+ """
+ Raises `TypeError` if interfaces isn't provided by value.
+ """
+ value = object()
+ a = simple_attr("x")
+
+ v = provides(IFoo)
+ with pytest.raises(TypeError) as e:
+ v(None, a, value)
+ assert (
+ "'x' must provide {interface!r} which {value!r} doesn't."
+ .format(interface=IFoo, value=value),
+ a, IFoo, value,
+ ) == e.value.args
+
+ def test_repr(self):
+ """
+ Returned validator has a useful `__repr__`.
+ """
+ v = provides(IFoo)
+ assert (
+ "<provides validator for interface {interface!r}>"
+ .format(interface=IFoo)
+ ) == repr(v)
+
+
+@pytest.mark.parametrize("validator", [
+ instance_of(int),
+ [always_pass, instance_of(int)],
+])
+class TestOptional(object):
+ """
+ Tests for `optional`.
+ """
+ def test_success(self, validator):
+ """
+ Nothing happens if validator succeeds.
+ """
+ v = optional(validator)
+ v(None, simple_attr("test"), 42)
+
+ def test_success_with_none(self, validator):
+ """
+ Nothing happens if None.
+ """
+ v = optional(validator)
+ v(None, simple_attr("test"), None)
+
+ def test_fail(self, validator):
+ """
+ Raises `TypeError` on wrong types.
+ """
+ v = optional(validator)
+ a = simple_attr("test")
+ with pytest.raises(TypeError) as e:
+ v(None, a, "42")
+ assert (
+ "'test' must be <{type} 'int'> (got '42' that is a <{type} "
+ "'str'>).".format(type=TYPE),
+ a, int, "42",
+
+ ) == e.value.args
+
+ def test_repr(self, validator):
+ """
+ Returned validator has a useful `__repr__`.
+ """
+ v = optional(validator)
+
+ if isinstance(validator, list):
+ assert (
+ ("<optional validator for _AndValidator(_validators=[{func}, "
+ "<instance_of validator for type <{type} 'int'>>]) or None>")
+ .format(func=repr(always_pass), type=TYPE)
+ ) == repr(v)
+ else:
+ assert (
+ ("<optional validator for <instance_of validator for type "
+ "<{type} 'int'>> or None>")
+ .format(type=TYPE)
+ ) == repr(v)
+
+
+class TestIn_(object):
+ """
+ Tests for `in_`.
+ """
+ def test_success_with_value(self):
+ """
+ If the value is in our options, nothing happens.
+ """
+ v = in_([1, 2, 3])
+ a = simple_attr("test")
+ v(1, a, 3)
+
+ def test_fail(self):
+ """
+ Raise ValueError if the value is outside our options.
+ """
+ v = in_([1, 2, 3])
+ a = simple_attr("test")
+ with pytest.raises(ValueError) as e:
+ v(None, a, None)
+ assert (
+ "'test' must be in [1, 2, 3] (got None)",
+ ) == e.value.args
+
+ def test_repr(self):
+ """
+ Returned validator has a useful `__repr__`.
+ """
+ v = in_([3, 4, 5])
+ assert(
+ ("<in_ validator with options [3, 4, 5]>")
+ ) == repr(v)
+
+
+def test_hashability():
+ """
+ Validator classes are hashable.
+ """
+ for obj_name in dir(validator_module):
+ obj = getattr(validator_module, obj_name)
+ if not has(obj):
+ continue
+ hash_func = getattr(obj, '__hash__', None)
+ assert hash_func is not None
+ assert hash_func is not object.__hash__
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/utils.py b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/utils.py
new file mode 100644
index 00000000000..36b624981e9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tests/utils.py
@@ -0,0 +1,237 @@
+"""
+Common helper functions for tests.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+import keyword
+import string
+
+from collections import OrderedDict
+
+from hypothesis import strategies as st
+
+import attr
+
+from attr import Attribute
+from attr._make import NOTHING, make_class
+
+
+def simple_class(cmp=False, repr=False, hash=False, str=False, slots=False,
+ frozen=False):
+ """
+ Return a new simple class.
+ """
+ return make_class(
+ "C", ["a", "b"],
+ cmp=cmp, repr=repr, hash=hash, init=True, slots=slots, str=str,
+ frozen=frozen,
+ )
+
+
+def simple_attr(name, default=NOTHING, validator=None, repr=True,
+ cmp=True, hash=None, init=True):
+ """
+ Return an attribute with a name and no other bells and whistles.
+ """
+ return Attribute(
+ name=name, default=default, validator=validator, repr=repr,
+ cmp=cmp, hash=hash, init=init
+ )
+
+
+class TestSimpleClass(object):
+ """
+ Tests for the testing helper function `make_class`.
+ """
+ def test_returns_class(self):
+ """
+ Returns a class object.
+ """
+ assert type is simple_class().__class__
+
+ def returns_distinct_classes(self):
+ """
+ Each call returns a completely new class.
+ """
+ assert simple_class() is not simple_class()
+
+
+def gen_attr_names():
+ """
+ Generate names for attributes, 'a'...'z', then 'aa'...'zz'.
+
+ ~702 different attribute names should be enough in practice.
+
+ Some short strings (such as 'as') are keywords, so we skip them.
+ """
+ lc = string.ascii_lowercase
+ for c in lc:
+ yield c
+ for outer in lc:
+ for inner in lc:
+ res = outer + inner
+ if keyword.iskeyword(res):
+ continue
+ yield outer + inner
+
+
+def maybe_underscore_prefix(source):
+ """
+ A generator to sometimes prepend an underscore.
+ """
+ to_underscore = False
+ for val in source:
+ yield val if not to_underscore else '_' + val
+ to_underscore = not to_underscore
+
+
+def _create_hyp_class(attrs):
+ """
+ A helper function for Hypothesis to generate attrs classes.
+ """
+ return make_class(
+ "HypClass", dict(zip(gen_attr_names(), attrs))
+ )
+
+
+def _create_hyp_nested_strategy(simple_class_strategy):
+ """
+ Create a recursive attrs class.
+
+ Given a strategy for building (simpler) classes, create and return
+ a strategy for building classes that have as an attribute: either just
+ the simpler class, a list of simpler classes, a tuple of simpler classes,
+ an ordered dict or a dict mapping the string "cls" to a simpler class.
+ """
+ # Use a tuple strategy to combine simple attributes and an attr class.
+ def just_class(tup):
+ combined_attrs = list(tup[0])
+ combined_attrs.append(attr.ib(default=attr.Factory(tup[1])))
+ return _create_hyp_class(combined_attrs)
+
+ def list_of_class(tup):
+ default = attr.Factory(lambda: [tup[1]()])
+ combined_attrs = list(tup[0])
+ combined_attrs.append(attr.ib(default=default))
+ return _create_hyp_class(combined_attrs)
+
+ def tuple_of_class(tup):
+ default = attr.Factory(lambda: (tup[1](),))
+ combined_attrs = list(tup[0])
+ combined_attrs.append(attr.ib(default=default))
+ return _create_hyp_class(combined_attrs)
+
+ def dict_of_class(tup):
+ default = attr.Factory(lambda: {"cls": tup[1]()})
+ combined_attrs = list(tup[0])
+ combined_attrs.append(attr.ib(default=default))
+ return _create_hyp_class(combined_attrs)
+
+ def ordereddict_of_class(tup):
+ default = attr.Factory(lambda: OrderedDict([("cls", tup[1]())]))
+ combined_attrs = list(tup[0])
+ combined_attrs.append(attr.ib(default=default))
+ return _create_hyp_class(combined_attrs)
+
+ # A strategy producing tuples of the form ([list of attributes], <given
+ # class strategy>).
+ attrs_and_classes = st.tuples(list_of_attrs, simple_class_strategy)
+
+ return st.one_of(attrs_and_classes.map(just_class),
+ attrs_and_classes.map(list_of_class),
+ attrs_and_classes.map(tuple_of_class),
+ attrs_and_classes.map(dict_of_class),
+ attrs_and_classes.map(ordereddict_of_class))
+
+
+bare_attrs = st.just(attr.ib(default=None))
+int_attrs = st.integers().map(lambda i: attr.ib(default=i))
+str_attrs = st.text().map(lambda s: attr.ib(default=s))
+float_attrs = st.floats().map(lambda f: attr.ib(default=f))
+dict_attrs = (st.dictionaries(keys=st.text(), values=st.integers())
+ .map(lambda d: attr.ib(default=d)))
+
+simple_attrs_without_metadata = (bare_attrs | int_attrs | str_attrs |
+ float_attrs | dict_attrs)
+
+
+@st.composite
+def simple_attrs_with_metadata(draw):
+ """
+ Create a simple attribute with arbitrary metadata.
+ """
+ c_attr = draw(simple_attrs)
+ keys = st.booleans() | st.binary() | st.integers() | st.text()
+ vals = st.booleans() | st.binary() | st.integers() | st.text()
+ metadata = draw(st.dictionaries(keys=keys, values=vals))
+
+ return attr.ib(c_attr._default, c_attr._validator, c_attr.repr,
+ c_attr.cmp, c_attr.hash, c_attr.init, c_attr.convert,
+ metadata)
+
+
+simple_attrs = simple_attrs_without_metadata | simple_attrs_with_metadata()
+
+# Python functions support up to 255 arguments.
+list_of_attrs = st.lists(simple_attrs, average_size=3, max_size=9)
+
+
+@st.composite
+def simple_classes(draw, slots=None, frozen=None, private_attrs=None):
+ """
+ A strategy that generates classes with default non-attr attributes.
+
+ For example, this strategy might generate a class such as:
+
+ @attr.s(slots=True, frozen=True)
+ class HypClass:
+ a = attr.ib(default=1)
+ _b = attr.ib(default=None)
+ c = attr.ib(default='text')
+ _d = attr.ib(default=1.0)
+ c = attr.ib(default={'t': 1})
+
+ By default, all combinations of slots and frozen classes will be generated.
+ If `slots=True` is passed in, only slots classes will be generated, and
+ if `slots=False` is passed in, no slot classes will be generated. The same
+ applies to `frozen`.
+
+ By default, some attributes will be private (i.e. prefixed with an
+ underscore). If `private_attrs=True` is passed in, all attributes will be
+ private, and if `private_attrs=False`, no attributes will be private.
+ """
+ attrs = draw(list_of_attrs)
+ frozen_flag = draw(st.booleans()) if frozen is None else frozen
+ slots_flag = draw(st.booleans()) if slots is None else slots
+
+ if private_attrs is None:
+ attr_names = maybe_underscore_prefix(gen_attr_names())
+ elif private_attrs is True:
+ attr_names = ('_' + n for n in gen_attr_names())
+ elif private_attrs is False:
+ attr_names = gen_attr_names()
+
+ cls_dict = dict(zip(attr_names, attrs))
+ post_init_flag = draw(st.booleans())
+ if post_init_flag:
+ def post_init(self):
+ pass
+ cls_dict["__attrs_post_init__"] = post_init
+
+ return make_class(
+ "HypClass",
+ cls_dict,
+ slots=slots_flag,
+ frozen=frozen_flag,
+ )
+
+
+# st.recursive works by taking a base strategy (in this case, simple_classes)
+# and a special function. This function receives a strategy, and returns
+# another strategy (building on top of the base strategy).
+nested_classes = st.recursive(
+ simple_classes(),
+ _create_hyp_nested_strategy,
+ max_leaves=10
+)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/attrs/tox.ini b/tests/wpt/web-platform-tests/tools/third_party/attrs/tox.ini
new file mode 100644
index 00000000000..e02c3301025
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/attrs/tox.ini
@@ -0,0 +1,83 @@
+[tox]
+envlist = isort,py27,py34,py35,py36,pypy,pypy3,flake8,manifest,docs,readme,changelog,coverage-report
+
+
+[testenv]
+# Prevent random setuptools/pip breakages like
+# https://github.com/pypa/setuptools/issues/1042 from breaking our builds.
+setenv =
+ VIRTUALENV_NO_DOWNLOAD=1
+deps = -rdev-requirements.txt
+commands = python -m pytest {posargs}
+
+
+[testenv:py27]
+deps = -rdev-requirements.txt
+commands = coverage run --parallel -m pytest {posargs}
+
+
+[testenv:py36]
+deps = -rdev-requirements.txt
+commands = coverage run --parallel -m pytest {posargs}
+
+
+# Uses default basepython otherwise reporting doesn't work on Travis where
+# Python 3.6 is only available in 3.6 jobs.
+[testenv:coverage-report]
+deps = coverage
+skip_install = true
+commands =
+ coverage combine
+ coverage report
+
+
+[testenv:flake8]
+basepython = python3.6
+# Needs a full install so isort can determine own/foreign imports.
+deps =
+ -rdev-requirements.txt
+ flake8
+ flake8-isort
+commands = flake8 src tests setup.py conftest.py docs/conf.py
+
+
+[testenv:isort]
+basepython = python3.6
+# Needs a full install so isort can determine own/foreign imports.
+deps =
+ -rdev-requirements.txt
+ isort
+commands =
+ isort --recursive setup.py conftest.py src tests
+
+
+[testenv:docs]
+basepython = python3.6
+setenv =
+ PYTHONHASHSEED = 0
+deps = -rdocs-requirements.txt
+commands =
+ sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html
+ sphinx-build -W -b doctest -d {envtmpdir}/doctrees docs docs/_build/html
+ python -m doctest README.rst
+
+
+[testenv:manifest]
+basepython = python3.6
+deps = check-manifest
+skip_install = true
+commands = check-manifest
+
+
+[testenv:readme]
+basepython = python3.6
+deps = readme_renderer
+skip_install = true
+commands = python setup.py check -r -s
+
+
+[testenv:changelog]
+basepython = python3.6
+deps = towncrier
+skip_install = true
+commands = towncrier --draft
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/.travis.yml b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/.travis.yml
new file mode 100644
index 00000000000..d2a7ab3051a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/.travis.yml
@@ -0,0 +1,17 @@
+language: python
+python:
+ - 2.6
+ - 2.7
+ - 3.2
+ - 3.3
+ - pypy
+install:
+ - pip install -r requirements/development.txt -r requirements/production.txt
+ - python setup.py install
+script:
+ - coverage run setup.py test
+ - coverage report --show-missing
+after_success:
+ - coveralls
+notifications:
+ email: aaron.iles+travis-ci@gmail.com
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/CHANGELOG b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/CHANGELOG
new file mode 100644
index 00000000000..602eec5e7c2
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/CHANGELOG
@@ -0,0 +1,19 @@
+Changelog
+---------
+
+0.4 (2013-12-20)
+````````````````
+* Fix unbound methods getting their first parameter curried
+* Publish Python wheel packages
+
+0.3 (2013-05-29)
+````````````````
+* Fix annotation formatting of builtin types on Python 2.x
+
+0.2 (2012-01-07)
+````````````````
+* PyPy compatability
+
+0.1 (2012-01-06)
+````````````````
+* Initial release
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/LICENSE b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/LICENSE
new file mode 100644
index 00000000000..3e563d6fbd4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2013 Aaron Iles
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/MANIFEST.in b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/MANIFEST.in
new file mode 100644
index 00000000000..f0abb42f04a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/MANIFEST.in
@@ -0,0 +1,7 @@
+recursive-include docs *
+recursive-include tests *.py
+include *.py
+include CHANGELOG
+include LICENSE
+include MANIFEST.in
+include README.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/Makefile b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/Makefile
new file mode 100644
index 00000000000..e2329231b56
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/Makefile
@@ -0,0 +1,39 @@
+SHELL := /bin/bash
+
+deps:
+ pip install --upgrade \
+ -r requirements/development.txt \
+ -r requirements/production.txt
+
+sdist:
+ python setup.py sdist
+ python setup.py bdist_wheel
+
+register:
+ python setup.py register
+ python setup.py sdist upload
+ python setup.py bdist_wheel upload
+
+site:
+ cd docs; make html
+
+test:
+ coverage run setup.py test
+
+unittest:
+ coverage run -m unittest discover
+
+lint:
+ flake8 --exit-zero funcsigs tests
+
+coverage:
+ coverage report --show-missing
+
+clean:
+ python setup.py clean --all
+ find . -type f -name "*.pyc" -exec rm '{}' +
+ find . -type d -name "__pycache__" -exec rmdir '{}' +
+ rm -rf *.egg-info .coverage
+ cd docs; make clean
+
+docs: site
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/README.rst b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/README.rst
new file mode 100644
index 00000000000..f04b7b422cf
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/README.rst
@@ -0,0 +1,83 @@
+funcsigs
+========
+
+``funcsigs`` is a backport of the `PEP 362`_ function signature features from
+Python 3.3's `inspect`_ module. The backport is compatible with Python 2.6, 2.7
+as well as 3.2 and up.
+
+|pypi_version|
+
+Documentation
+-------------
+
+The reference documentation is standard library documentation for the
+`inspect`_ module in Python3. This documentation has been included in the
+``funcsigs`` package documentation hosted on `Read The Docs`_.
+
+Example
+-------
+
+To obtain a signature object, pass the target function to the
+``funcsigs.signature`` function. ::
+
+ >>> from funcsigs import signature
+ >>> def foo(a, b=None, *args, **kwargs):
+ ... pass
+
+ >>> sig = signature(foo)
+
+For the details of the signature object, refer to the either the package of
+standard library documentation.
+
+Compatability
+-------------
+
+The ``funcsigs`` backport has been tested against:
+
+* CPython 2.6
+* CPython 2.7
+* CPython 3.2
+* PyPy 1.9
+
+Continuous integration testing is provided by `Travis CI`_.
+
+Under Python 2.x there is a compatability issue when a function is assigned to
+the ``__wrapped__`` property of a class after it has been constructed.
+Similiarily there under PyPy directly passing the ``__call__`` method of a
+builtin is also a compatability issues. Otherwise the functionality is
+believed to be uniform between both Python2 and Python3.
+
+Issues
+------
+
+Source code for ``funcsigs`` is hosted on `GitHub`_. Any bug reports or feature
+requests can be made using GitHub's `issues system`_. |build_status| |coverage|
+
+Copyright
+---------
+
+This is a derived work of CPython under the terms of the `PSF License
+Agreement`_. The original CPython inspect module, its unit tests and
+documentation are the copyright of the Python Software Foundation. The derived
+work is distributed under the `Apache License Version 2.0`_.
+
+.. _Apache License Version 2.0: http://opensource.org/licenses/Apache-2.0
+.. _GitHub: https://github.com/aliles/funcsigs
+.. _PSF License Agreement: http://docs.python.org/3/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python
+.. _Travis CI: http://travis-ci.org/
+.. _Read The Docs: http://funcsigs.readthedocs.org/
+.. _PEP 362: http://www.python.org/dev/peps/pep-0362/
+.. _inspect: http://docs.python.org/3/library/inspect.html#introspecting-callables-with-the-signature-object
+.. _issues system: https://github.com/alies/funcsigs/issues
+
+.. |build_status| image:: https://secure.travis-ci.org/aliles/funcsigs.png?branch=master
+ :target: http://travis-ci.org/#!/aliles/funcsigs
+ :alt: Current build status
+
+.. |coverage| image:: https://coveralls.io/repos/aliles/funcsigs/badge.png?branch=master
+ :target: https://coveralls.io/r/aliles/funcsigs?branch=master
+ :alt: Coverage status
+
+.. |pypi_version| image:: https://pypip.in/v/funcsigs/badge.png
+ :target: https://crate.io/packages/funcsigs/
+ :alt: Latest PyPI version
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/Makefile b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/Makefile
new file mode 100644
index 00000000000..f7ab3d16b40
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/Makefile
@@ -0,0 +1,153 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/funcsigs.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/funcsigs.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/funcsigs"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/funcsigs"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/_templates/page.html b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/_templates/page.html
new file mode 100644
index 00000000000..5e1e00bcafa
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/_templates/page.html
@@ -0,0 +1,9 @@
+{% extends "!page.html" %}
+{% block extrahead %}
+ <a href="https://github.com/aliles/funcsigs">
+ <img style="position: absolute; top: 0; right: 0; border: 0;"
+ src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"
+ alt="Fork me on GitHub">
+ </a>
+ {{ super() }}
+{% endblock %}
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/conf.py b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/conf.py
new file mode 100644
index 00000000000..c6e4194cc05
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/conf.py
@@ -0,0 +1,251 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# funcsigs documentation build configuration file, created by
+# sphinx-quickstart on Fri Apr 20 20:27:52 2012.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+sys.path.insert(0, os.path.abspath('..'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'funcsigs'
+copyright = '2013, Aaron Iles'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+from funcsigs import __version__
+version = '.'.join(__version__.split('.')[:2])
+# The full version, including alpha/beta/rc tags.
+release = __version__
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'agogo'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'funcsigsdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'funcsigs.tex', 'funcsigs Documentation',
+ 'Aaron Iles', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'funcsigs', 'funcsigs Documentation',
+ ['Aaron Iles'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'funcsigs', 'funcsigs Documentation',
+ 'Aaron Iles', 'funcsigs', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {
+ 'python3': ('http://docs.python.org/py3k', None),
+ 'python': ('http://docs.python.org/', None)
+}
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/index.rst b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/index.rst
new file mode 100644
index 00000000000..5d0f42f9368
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/docs/index.rst
@@ -0,0 +1,315 @@
+.. funcsigs documentation master file, created by
+ sphinx-quickstart on Fri Apr 20 20:27:52 2012.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Introducing funcsigs
+====================
+
+The Funcsigs Package
+--------------------
+
+*funcsigs* is a backport of the `PEP 362`_ function signature features from
+Python 3.3's `inspect`_ module. The backport is compatible with Python 2.6, 2.7
+as well as 3.2 and up.
+
+.. _PEP 362: http://www.python.org/dev/peps/pep-0362/
+.. _inspect: http://docs.python.org/3/library/inspect.html#introspecting-callables-with-the-signature-object
+
+Compatability
+`````````````
+
+The *funcsigs* backport has been tested against:
+
+* CPython 2.6
+* CPython 2.7
+* CPython 3.2
+* PyPy 1.9
+
+Continuous integration testing is provided by `Travis CI`_.
+
+Under Python 2.x there is a compatability issue when a function is assigned to
+the ``__wrapped__`` property of a class after it has been constructed.
+Similiarily there under PyPy directly passing the ``__call__`` method of a
+builtin is also a compatability issues. Otherwise the functionality is
+believed to be uniform between both Python2 and Python3.
+
+.. _Travis CI: http://travis-ci.org/
+
+Issues
+``````
+
+Source code for *funcsigs* is hosted on `GitHub`_. Any bug reports or feature
+requests can be made using GitHub's `issues system`_.
+
+.. _GitHub: https://github.com/aliles/funcsigs
+.. _issues system: https://github.com/alies/funcsigs/issues
+
+Introspecting callables with the Signature object
+-------------------------------------------------
+
+.. note::
+
+ This section of documentation is a direct repoduction of the Python
+ standard library documentation for the inspect module.
+
+The Signature object represents the call signature of a callable object and its
+return annotation. To retrieve a Signature object, use the :func:`signature`
+function.
+
+.. function:: signature(callable)
+
+ Return a :class:`Signature` object for the given ``callable``::
+
+ >>> from inspect import signature
+ >>> def foo(a, *, b:int, **kwargs):
+ ... pass
+
+ >>> sig = signature(foo)
+
+ >>> str(sig)
+ '(a, *, b:int, **kwargs)'
+
+ >>> str(sig.parameters['b'])
+ 'b:int'
+
+ >>> sig.parameters['b'].annotation
+ <class 'int'>
+
+ Accepts a wide range of python callables, from plain functions and classes to
+ :func:`functools.partial` objects.
+
+ .. note::
+
+ Some callables may not be introspectable in certain implementations of
+ Python. For example, in CPython, built-in functions defined in C provide
+ no metadata about their arguments.
+
+
+.. class:: Signature
+
+ A Signature object represents the call signature of a function and its return
+ annotation. For each parameter accepted by the function it stores a
+ :class:`Parameter` object in its :attr:`parameters` collection.
+
+ Signature objects are *immutable*. Use :meth:`Signature.replace` to make a
+ modified copy.
+
+ .. attribute:: Signature.empty
+
+ A special class-level marker to specify absence of a return annotation.
+
+ .. attribute:: Signature.parameters
+
+ An ordered mapping of parameters' names to the corresponding
+ :class:`Parameter` objects.
+
+ .. attribute:: Signature.return_annotation
+
+ The "return" annotation for the callable. If the callable has no "return"
+ annotation, this attribute is set to :attr:`Signature.empty`.
+
+ .. method:: Signature.bind(*args, **kwargs)
+
+ Create a mapping from positional and keyword arguments to parameters.
+ Returns :class:`BoundArguments` if ``*args`` and ``**kwargs`` match the
+ signature, or raises a :exc:`TypeError`.
+
+ .. method:: Signature.bind_partial(*args, **kwargs)
+
+ Works the same way as :meth:`Signature.bind`, but allows the omission of
+ some required arguments (mimics :func:`functools.partial` behavior.)
+ Returns :class:`BoundArguments`, or raises a :exc:`TypeError` if the
+ passed arguments do not match the signature.
+
+ .. method:: Signature.replace(*[, parameters][, return_annotation])
+
+ Create a new Signature instance based on the instance replace was invoked
+ on. It is possible to pass different ``parameters`` and/or
+ ``return_annotation`` to override the corresponding properties of the base
+ signature. To remove return_annotation from the copied Signature, pass in
+ :attr:`Signature.empty`.
+
+ ::
+
+ >>> def test(a, b):
+ ... pass
+ >>> sig = signature(test)
+ >>> new_sig = sig.replace(return_annotation="new return anno")
+ >>> str(new_sig)
+ "(a, b) -> 'new return anno'"
+
+
+.. class:: Parameter
+
+ Parameter objects are *immutable*. Instead of modifying a Parameter object,
+ you can use :meth:`Parameter.replace` to create a modified copy.
+
+ .. attribute:: Parameter.empty
+
+ A special class-level marker to specify absence of default values and
+ annotations.
+
+ .. attribute:: Parameter.name
+
+ The name of the parameter as a string. Must be a valid python identifier
+ name (with the exception of ``POSITIONAL_ONLY`` parameters, which can have
+ it set to ``None``).
+
+ .. attribute:: Parameter.default
+
+ The default value for the parameter. If the parameter has no default
+ value, this attribute is set to :attr:`Parameter.empty`.
+
+ .. attribute:: Parameter.annotation
+
+ The annotation for the parameter. If the parameter has no annotation,
+ this attribute is set to :attr:`Parameter.empty`.
+
+ .. attribute:: Parameter.kind
+
+ Describes how argument values are bound to the parameter. Possible values
+ (accessible via :class:`Parameter`, like ``Parameter.KEYWORD_ONLY``):
+
+ +------------------------+----------------------------------------------+
+ | Name | Meaning |
+ +========================+==============================================+
+ | *POSITIONAL_ONLY* | Value must be supplied as a positional |
+ | | argument. |
+ | | |
+ | | Python has no explicit syntax for defining |
+ | | positional-only parameters, but many built-in|
+ | | and extension module functions (especially |
+ | | those that accept only one or two parameters)|
+ | | accept them. |
+ +------------------------+----------------------------------------------+
+ | *POSITIONAL_OR_KEYWORD*| Value may be supplied as either a keyword or |
+ | | positional argument (this is the standard |
+ | | binding behaviour for functions implemented |
+ | | in Python.) |
+ +------------------------+----------------------------------------------+
+ | *VAR_POSITIONAL* | A tuple of positional arguments that aren't |
+ | | bound to any other parameter. This |
+ | | corresponds to a ``*args`` parameter in a |
+ | | Python function definition. |
+ +------------------------+----------------------------------------------+
+ | *KEYWORD_ONLY* | Value must be supplied as a keyword argument.|
+ | | Keyword only parameters are those which |
+ | | appear after a ``*`` or ``*args`` entry in a |
+ | | Python function definition. |
+ +------------------------+----------------------------------------------+
+ | *VAR_KEYWORD* | A dict of keyword arguments that aren't bound|
+ | | to any other parameter. This corresponds to a|
+ | | ``**kwargs`` parameter in a Python function |
+ | | definition. |
+ +------------------------+----------------------------------------------+
+
+ Example: print all keyword-only arguments without default values::
+
+ >>> def foo(a, b, *, c, d=10):
+ ... pass
+
+ >>> sig = signature(foo)
+ >>> for param in sig.parameters.values():
+ ... if (param.kind == param.KEYWORD_ONLY and
+ ... param.default is param.empty):
+ ... print('Parameter:', param)
+ Parameter: c
+
+ .. method:: Parameter.replace(*[, name][, kind][, default][, annotation])
+
+ Create a new Parameter instance based on the instance replaced was invoked
+ on. To override a :class:`Parameter` attribute, pass the corresponding
+ argument. To remove a default value or/and an annotation from a
+ Parameter, pass :attr:`Parameter.empty`.
+
+ ::
+
+ >>> from inspect import Parameter
+ >>> param = Parameter('foo', Parameter.KEYWORD_ONLY, default=42)
+ >>> str(param)
+ 'foo=42'
+
+ >>> str(param.replace()) # Will create a shallow copy of 'param'
+ 'foo=42'
+
+ >>> str(param.replace(default=Parameter.empty, annotation='spam'))
+ "foo:'spam'"
+
+
+.. class:: BoundArguments
+
+ Result of a :meth:`Signature.bind` or :meth:`Signature.bind_partial` call.
+ Holds the mapping of arguments to the function's parameters.
+
+ .. attribute:: BoundArguments.arguments
+
+ An ordered, mutable mapping (:class:`collections.OrderedDict`) of
+ parameters' names to arguments' values. Contains only explicitly bound
+ arguments. Changes in :attr:`arguments` will reflect in :attr:`args` and
+ :attr:`kwargs`.
+
+ Should be used in conjunction with :attr:`Signature.parameters` for any
+ argument processing purposes.
+
+ .. note::
+
+ Arguments for which :meth:`Signature.bind` or
+ :meth:`Signature.bind_partial` relied on a default value are skipped.
+ However, if needed, it is easy to include them.
+
+ ::
+
+ >>> def foo(a, b=10):
+ ... pass
+
+ >>> sig = signature(foo)
+ >>> ba = sig.bind(5)
+
+ >>> ba.args, ba.kwargs
+ ((5,), {})
+
+ >>> for param in sig.parameters.values():
+ ... if param.name not in ba.arguments:
+ ... ba.arguments[param.name] = param.default
+
+ >>> ba.args, ba.kwargs
+ ((5, 10), {})
+
+
+ .. attribute:: BoundArguments.args
+
+ A tuple of positional arguments values. Dynamically computed from the
+ :attr:`arguments` attribute.
+
+ .. attribute:: BoundArguments.kwargs
+
+ A dict of keyword arguments values. Dynamically computed from the
+ :attr:`arguments` attribute.
+
+ The :attr:`args` and :attr:`kwargs` properties can be used to invoke
+ functions::
+
+ def test(a, *, b):
+ ...
+
+ sig = signature(test)
+ ba = sig.bind(10, b=20)
+ test(*ba.args, **ba.kwargs)
+
+
+.. seealso::
+
+ :pep:`362` - Function Signature Object.
+ The detailed specification, implementation details and examples.
+
+Copyright
+---------
+
+*funcsigs* is a derived work of CPython under the terms of the `PSF License
+Agreement`_. The original CPython inspect module, its unit tests and
+documentation are the copyright of the Python Software Foundation. The derived
+work is distributed under the `Apache License Version 2.0`_.
+
+.. _PSF License Agreement: http://docs.python.org/3/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python
+.. _Apache License Version 2.0: http://opensource.org/licenses/Apache-2.0
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/__init__.py
new file mode 100644
index 00000000000..fd2f47b1d4d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/__init__.py
@@ -0,0 +1,818 @@
+# Copyright 2001-2013 Python Software Foundation; All Rights Reserved
+"""Function signature objects for callables
+
+Back port of Python 3.3's function signature tools from the inspect module,
+modified to be compatible with Python 2.6, 2.7 and 3.2+.
+"""
+from __future__ import absolute_import, division, print_function
+import itertools
+import functools
+import re
+import types
+
+try:
+ from collections import OrderedDict
+except ImportError:
+ from funcsigs.odict import OrderedDict
+
+from funcsigs.version import __version__
+
+__all__ = ['BoundArguments', 'Parameter', 'Signature', 'signature']
+
+
+_WrapperDescriptor = type(type.__call__)
+_MethodWrapper = type(all.__call__)
+
+_NonUserDefinedCallables = (_WrapperDescriptor,
+ _MethodWrapper,
+ types.BuiltinFunctionType)
+
+
+def formatannotation(annotation, base_module=None):
+ if isinstance(annotation, type):
+ if annotation.__module__ in ('builtins', '__builtin__', base_module):
+ return annotation.__name__
+ return annotation.__module__+'.'+annotation.__name__
+ return repr(annotation)
+
+
+def _get_user_defined_method(cls, method_name, *nested):
+ try:
+ if cls is type:
+ return
+ meth = getattr(cls, method_name)
+ for name in nested:
+ meth = getattr(meth, name, meth)
+ except AttributeError:
+ return
+ else:
+ if not isinstance(meth, _NonUserDefinedCallables):
+ # Once '__signature__' will be added to 'C'-level
+ # callables, this check won't be necessary
+ return meth
+
+
+def signature(obj):
+ '''Get a signature object for the passed callable.'''
+
+ if not callable(obj):
+ raise TypeError('{0!r} is not a callable object'.format(obj))
+
+ if isinstance(obj, types.MethodType):
+ sig = signature(obj.__func__)
+ if obj.__self__ is None:
+ # Unbound method: the first parameter becomes positional-only
+ if sig.parameters:
+ first = sig.parameters.values()[0].replace(
+ kind=_POSITIONAL_ONLY)
+ return sig.replace(
+ parameters=(first,) + tuple(sig.parameters.values())[1:])
+ else:
+ return sig
+ else:
+ # In this case we skip the first parameter of the underlying
+ # function (usually `self` or `cls`).
+ return sig.replace(parameters=tuple(sig.parameters.values())[1:])
+
+ try:
+ sig = obj.__signature__
+ except AttributeError:
+ pass
+ else:
+ if sig is not None:
+ return sig
+
+ try:
+ # Was this function wrapped by a decorator?
+ wrapped = obj.__wrapped__
+ except AttributeError:
+ pass
+ else:
+ return signature(wrapped)
+
+ if isinstance(obj, types.FunctionType):
+ return Signature.from_function(obj)
+
+ if isinstance(obj, functools.partial):
+ sig = signature(obj.func)
+
+ new_params = OrderedDict(sig.parameters.items())
+
+ partial_args = obj.args or ()
+ partial_keywords = obj.keywords or {}
+ try:
+ ba = sig.bind_partial(*partial_args, **partial_keywords)
+ except TypeError as ex:
+ msg = 'partial object {0!r} has incorrect arguments'.format(obj)
+ raise ValueError(msg)
+
+ for arg_name, arg_value in ba.arguments.items():
+ param = new_params[arg_name]
+ if arg_name in partial_keywords:
+ # We set a new default value, because the following code
+ # is correct:
+ #
+ # >>> def foo(a): print(a)
+ # >>> print(partial(partial(foo, a=10), a=20)())
+ # 20
+ # >>> print(partial(partial(foo, a=10), a=20)(a=30))
+ # 30
+ #
+ # So, with 'partial' objects, passing a keyword argument is
+ # like setting a new default value for the corresponding
+ # parameter
+ #
+ # We also mark this parameter with '_partial_kwarg'
+ # flag. Later, in '_bind', the 'default' value of this
+ # parameter will be added to 'kwargs', to simulate
+ # the 'functools.partial' real call.
+ new_params[arg_name] = param.replace(default=arg_value,
+ _partial_kwarg=True)
+
+ elif (param.kind not in (_VAR_KEYWORD, _VAR_POSITIONAL) and
+ not param._partial_kwarg):
+ new_params.pop(arg_name)
+
+ return sig.replace(parameters=new_params.values())
+
+ sig = None
+ if isinstance(obj, type):
+ # obj is a class or a metaclass
+
+ # First, let's see if it has an overloaded __call__ defined
+ # in its metaclass
+ call = _get_user_defined_method(type(obj), '__call__')
+ if call is not None:
+ sig = signature(call)
+ else:
+ # Now we check if the 'obj' class has a '__new__' method
+ new = _get_user_defined_method(obj, '__new__')
+ if new is not None:
+ sig = signature(new)
+ else:
+ # Finally, we should have at least __init__ implemented
+ init = _get_user_defined_method(obj, '__init__')
+ if init is not None:
+ sig = signature(init)
+ elif not isinstance(obj, _NonUserDefinedCallables):
+ # An object with __call__
+ # We also check that the 'obj' is not an instance of
+ # _WrapperDescriptor or _MethodWrapper to avoid
+ # infinite recursion (and even potential segfault)
+ call = _get_user_defined_method(type(obj), '__call__', 'im_func')
+ if call is not None:
+ sig = signature(call)
+
+ if sig is not None:
+ # For classes and objects we skip the first parameter of their
+ # __call__, __new__, or __init__ methods
+ return sig.replace(parameters=tuple(sig.parameters.values())[1:])
+
+ if isinstance(obj, types.BuiltinFunctionType):
+ # Raise a nicer error message for builtins
+ msg = 'no signature found for builtin function {0!r}'.format(obj)
+ raise ValueError(msg)
+
+ raise ValueError('callable {0!r} is not supported by signature'.format(obj))
+
+
+class _void(object):
+ '''A private marker - used in Parameter & Signature'''
+
+
+class _empty(object):
+ pass
+
+
+class _ParameterKind(int):
+ def __new__(self, *args, **kwargs):
+ obj = int.__new__(self, *args)
+ obj._name = kwargs['name']
+ return obj
+
+ def __str__(self):
+ return self._name
+
+ def __repr__(self):
+ return '<_ParameterKind: {0!r}>'.format(self._name)
+
+
+_POSITIONAL_ONLY = _ParameterKind(0, name='POSITIONAL_ONLY')
+_POSITIONAL_OR_KEYWORD = _ParameterKind(1, name='POSITIONAL_OR_KEYWORD')
+_VAR_POSITIONAL = _ParameterKind(2, name='VAR_POSITIONAL')
+_KEYWORD_ONLY = _ParameterKind(3, name='KEYWORD_ONLY')
+_VAR_KEYWORD = _ParameterKind(4, name='VAR_KEYWORD')
+
+
+class Parameter(object):
+ '''Represents a parameter in a function signature.
+
+ Has the following public attributes:
+
+ * name : str
+ The name of the parameter as a string.
+ * default : object
+ The default value for the parameter if specified. If the
+ parameter has no default value, this attribute is not set.
+ * annotation
+ The annotation for the parameter if specified. If the
+ parameter has no annotation, this attribute is not set.
+ * kind : str
+ Describes how argument values are bound to the parameter.
+ Possible values: `Parameter.POSITIONAL_ONLY`,
+ `Parameter.POSITIONAL_OR_KEYWORD`, `Parameter.VAR_POSITIONAL`,
+ `Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`.
+ '''
+
+ __slots__ = ('_name', '_kind', '_default', '_annotation', '_partial_kwarg')
+
+ POSITIONAL_ONLY = _POSITIONAL_ONLY
+ POSITIONAL_OR_KEYWORD = _POSITIONAL_OR_KEYWORD
+ VAR_POSITIONAL = _VAR_POSITIONAL
+ KEYWORD_ONLY = _KEYWORD_ONLY
+ VAR_KEYWORD = _VAR_KEYWORD
+
+ empty = _empty
+
+ def __init__(self, name, kind, default=_empty, annotation=_empty,
+ _partial_kwarg=False):
+
+ if kind not in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD,
+ _VAR_POSITIONAL, _KEYWORD_ONLY, _VAR_KEYWORD):
+ raise ValueError("invalid value for 'Parameter.kind' attribute")
+ self._kind = kind
+
+ if default is not _empty:
+ if kind in (_VAR_POSITIONAL, _VAR_KEYWORD):
+ msg = '{0} parameters cannot have default values'.format(kind)
+ raise ValueError(msg)
+ self._default = default
+ self._annotation = annotation
+
+ if name is None:
+ if kind != _POSITIONAL_ONLY:
+ raise ValueError("None is not a valid name for a "
+ "non-positional-only parameter")
+ self._name = name
+ else:
+ name = str(name)
+ if kind != _POSITIONAL_ONLY and not re.match(r'[a-z_]\w*$', name, re.I):
+ msg = '{0!r} is not a valid parameter name'.format(name)
+ raise ValueError(msg)
+ self._name = name
+
+ self._partial_kwarg = _partial_kwarg
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def default(self):
+ return self._default
+
+ @property
+ def annotation(self):
+ return self._annotation
+
+ @property
+ def kind(self):
+ return self._kind
+
+ def replace(self, name=_void, kind=_void, annotation=_void,
+ default=_void, _partial_kwarg=_void):
+ '''Creates a customized copy of the Parameter.'''
+
+ if name is _void:
+ name = self._name
+
+ if kind is _void:
+ kind = self._kind
+
+ if annotation is _void:
+ annotation = self._annotation
+
+ if default is _void:
+ default = self._default
+
+ if _partial_kwarg is _void:
+ _partial_kwarg = self._partial_kwarg
+
+ return type(self)(name, kind, default=default, annotation=annotation,
+ _partial_kwarg=_partial_kwarg)
+
+ def __str__(self):
+ kind = self.kind
+
+ formatted = self._name
+ if kind == _POSITIONAL_ONLY:
+ if formatted is None:
+ formatted = ''
+ formatted = '<{0}>'.format(formatted)
+
+ # Add annotation and default value
+ if self._annotation is not _empty:
+ formatted = '{0}:{1}'.format(formatted,
+ formatannotation(self._annotation))
+
+ if self._default is not _empty:
+ formatted = '{0}={1}'.format(formatted, repr(self._default))
+
+ if kind == _VAR_POSITIONAL:
+ formatted = '*' + formatted
+ elif kind == _VAR_KEYWORD:
+ formatted = '**' + formatted
+
+ return formatted
+
+ def __repr__(self):
+ return '<{0} at {1:#x} {2!r}>'.format(self.__class__.__name__,
+ id(self), self.name)
+
+ def __hash__(self):
+ msg = "unhashable type: '{0}'".format(self.__class__.__name__)
+ raise TypeError(msg)
+
+ def __eq__(self, other):
+ return (issubclass(other.__class__, Parameter) and
+ self._name == other._name and
+ self._kind == other._kind and
+ self._default == other._default and
+ self._annotation == other._annotation)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+
+class BoundArguments(object):
+ '''Result of `Signature.bind` call. Holds the mapping of arguments
+ to the function's parameters.
+
+ Has the following public attributes:
+
+ * arguments : OrderedDict
+ An ordered mutable mapping of parameters' names to arguments' values.
+ Does not contain arguments' default values.
+ * signature : Signature
+ The Signature object that created this instance.
+ * args : tuple
+ Tuple of positional arguments values.
+ * kwargs : dict
+ Dict of keyword arguments values.
+ '''
+
+ def __init__(self, signature, arguments):
+ self.arguments = arguments
+ self._signature = signature
+
+ @property
+ def signature(self):
+ return self._signature
+
+ @property
+ def args(self):
+ args = []
+ for param_name, param in self._signature.parameters.items():
+ if (param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or
+ param._partial_kwarg):
+ # Keyword arguments mapped by 'functools.partial'
+ # (Parameter._partial_kwarg is True) are mapped
+ # in 'BoundArguments.kwargs', along with VAR_KEYWORD &
+ # KEYWORD_ONLY
+ break
+
+ try:
+ arg = self.arguments[param_name]
+ except KeyError:
+ # We're done here. Other arguments
+ # will be mapped in 'BoundArguments.kwargs'
+ break
+ else:
+ if param.kind == _VAR_POSITIONAL:
+ # *args
+ args.extend(arg)
+ else:
+ # plain argument
+ args.append(arg)
+
+ return tuple(args)
+
+ @property
+ def kwargs(self):
+ kwargs = {}
+ kwargs_started = False
+ for param_name, param in self._signature.parameters.items():
+ if not kwargs_started:
+ if (param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or
+ param._partial_kwarg):
+ kwargs_started = True
+ else:
+ if param_name not in self.arguments:
+ kwargs_started = True
+ continue
+
+ if not kwargs_started:
+ continue
+
+ try:
+ arg = self.arguments[param_name]
+ except KeyError:
+ pass
+ else:
+ if param.kind == _VAR_KEYWORD:
+ # **kwargs
+ kwargs.update(arg)
+ else:
+ # plain keyword argument
+ kwargs[param_name] = arg
+
+ return kwargs
+
+ def __hash__(self):
+ msg = "unhashable type: '{0}'".format(self.__class__.__name__)
+ raise TypeError(msg)
+
+ def __eq__(self, other):
+ return (issubclass(other.__class__, BoundArguments) and
+ self.signature == other.signature and
+ self.arguments == other.arguments)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+
+class Signature(object):
+ '''A Signature object represents the overall signature of a function.
+ It stores a Parameter object for each parameter accepted by the
+ function, as well as information specific to the function itself.
+
+ A Signature object has the following public attributes and methods:
+
+ * parameters : OrderedDict
+ An ordered mapping of parameters' names to the corresponding
+ Parameter objects (keyword-only arguments are in the same order
+ as listed in `code.co_varnames`).
+ * return_annotation : object
+ The annotation for the return type of the function if specified.
+ If the function has no annotation for its return type, this
+ attribute is not set.
+ * bind(*args, **kwargs) -> BoundArguments
+ Creates a mapping from positional and keyword arguments to
+ parameters.
+ * bind_partial(*args, **kwargs) -> BoundArguments
+ Creates a partial mapping from positional and keyword arguments
+ to parameters (simulating 'functools.partial' behavior.)
+ '''
+
+ __slots__ = ('_return_annotation', '_parameters')
+
+ _parameter_cls = Parameter
+ _bound_arguments_cls = BoundArguments
+
+ empty = _empty
+
+ def __init__(self, parameters=None, return_annotation=_empty,
+ __validate_parameters__=True):
+ '''Constructs Signature from the given list of Parameter
+ objects and 'return_annotation'. All arguments are optional.
+ '''
+
+ if parameters is None:
+ params = OrderedDict()
+ else:
+ if __validate_parameters__:
+ params = OrderedDict()
+ top_kind = _POSITIONAL_ONLY
+
+ for idx, param in enumerate(parameters):
+ kind = param.kind
+ if kind < top_kind:
+ msg = 'wrong parameter order: {0} before {1}'
+ msg = msg.format(top_kind, param.kind)
+ raise ValueError(msg)
+ else:
+ top_kind = kind
+
+ name = param.name
+ if name is None:
+ name = str(idx)
+ param = param.replace(name=name)
+
+ if name in params:
+ msg = 'duplicate parameter name: {0!r}'.format(name)
+ raise ValueError(msg)
+ params[name] = param
+ else:
+ params = OrderedDict(((param.name, param)
+ for param in parameters))
+
+ self._parameters = params
+ self._return_annotation = return_annotation
+
+ @classmethod
+ def from_function(cls, func):
+ '''Constructs Signature for the given python function'''
+
+ if not isinstance(func, types.FunctionType):
+ raise TypeError('{0!r} is not a Python function'.format(func))
+
+ Parameter = cls._parameter_cls
+
+ # Parameter information.
+ func_code = func.__code__
+ pos_count = func_code.co_argcount
+ arg_names = func_code.co_varnames
+ positional = tuple(arg_names[:pos_count])
+ keyword_only_count = getattr(func_code, 'co_kwonlyargcount', 0)
+ keyword_only = arg_names[pos_count:(pos_count + keyword_only_count)]
+ annotations = getattr(func, '__annotations__', {})
+ defaults = func.__defaults__
+ kwdefaults = getattr(func, '__kwdefaults__', None)
+
+ if defaults:
+ pos_default_count = len(defaults)
+ else:
+ pos_default_count = 0
+
+ parameters = []
+
+ # Non-keyword-only parameters w/o defaults.
+ non_default_count = pos_count - pos_default_count
+ for name in positional[:non_default_count]:
+ annotation = annotations.get(name, _empty)
+ parameters.append(Parameter(name, annotation=annotation,
+ kind=_POSITIONAL_OR_KEYWORD))
+
+ # ... w/ defaults.
+ for offset, name in enumerate(positional[non_default_count:]):
+ annotation = annotations.get(name, _empty)
+ parameters.append(Parameter(name, annotation=annotation,
+ kind=_POSITIONAL_OR_KEYWORD,
+ default=defaults[offset]))
+
+ # *args
+ if func_code.co_flags & 0x04:
+ name = arg_names[pos_count + keyword_only_count]
+ annotation = annotations.get(name, _empty)
+ parameters.append(Parameter(name, annotation=annotation,
+ kind=_VAR_POSITIONAL))
+
+ # Keyword-only parameters.
+ for name in keyword_only:
+ default = _empty
+ if kwdefaults is not None:
+ default = kwdefaults.get(name, _empty)
+
+ annotation = annotations.get(name, _empty)
+ parameters.append(Parameter(name, annotation=annotation,
+ kind=_KEYWORD_ONLY,
+ default=default))
+ # **kwargs
+ if func_code.co_flags & 0x08:
+ index = pos_count + keyword_only_count
+ if func_code.co_flags & 0x04:
+ index += 1
+
+ name = arg_names[index]
+ annotation = annotations.get(name, _empty)
+ parameters.append(Parameter(name, annotation=annotation,
+ kind=_VAR_KEYWORD))
+
+ return cls(parameters,
+ return_annotation=annotations.get('return', _empty),
+ __validate_parameters__=False)
+
+ @property
+ def parameters(self):
+ try:
+ return types.MappingProxyType(self._parameters)
+ except AttributeError:
+ return OrderedDict(self._parameters.items())
+
+ @property
+ def return_annotation(self):
+ return self._return_annotation
+
+ def replace(self, parameters=_void, return_annotation=_void):
+ '''Creates a customized copy of the Signature.
+ Pass 'parameters' and/or 'return_annotation' arguments
+ to override them in the new copy.
+ '''
+
+ if parameters is _void:
+ parameters = self.parameters.values()
+
+ if return_annotation is _void:
+ return_annotation = self._return_annotation
+
+ return type(self)(parameters,
+ return_annotation=return_annotation)
+
+ def __hash__(self):
+ msg = "unhashable type: '{0}'".format(self.__class__.__name__)
+ raise TypeError(msg)
+
+ def __eq__(self, other):
+ if (not issubclass(type(other), Signature) or
+ self.return_annotation != other.return_annotation or
+ len(self.parameters) != len(other.parameters)):
+ return False
+
+ other_positions = dict((param, idx)
+ for idx, param in enumerate(other.parameters.keys()))
+
+ for idx, (param_name, param) in enumerate(self.parameters.items()):
+ if param.kind == _KEYWORD_ONLY:
+ try:
+ other_param = other.parameters[param_name]
+ except KeyError:
+ return False
+ else:
+ if param != other_param:
+ return False
+ else:
+ try:
+ other_idx = other_positions[param_name]
+ except KeyError:
+ return False
+ else:
+ if (idx != other_idx or
+ param != other.parameters[param_name]):
+ return False
+
+ return True
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def _bind(self, args, kwargs, partial=False):
+ '''Private method. Don't use directly.'''
+
+ arguments = OrderedDict()
+
+ parameters = iter(self.parameters.values())
+ parameters_ex = ()
+ arg_vals = iter(args)
+
+ if partial:
+ # Support for binding arguments to 'functools.partial' objects.
+ # See 'functools.partial' case in 'signature()' implementation
+ # for details.
+ for param_name, param in self.parameters.items():
+ if (param._partial_kwarg and param_name not in kwargs):
+ # Simulating 'functools.partial' behavior
+ kwargs[param_name] = param.default
+
+ while True:
+ # Let's iterate through the positional arguments and corresponding
+ # parameters
+ try:
+ arg_val = next(arg_vals)
+ except StopIteration:
+ # No more positional arguments
+ try:
+ param = next(parameters)
+ except StopIteration:
+ # No more parameters. That's it. Just need to check that
+ # we have no `kwargs` after this while loop
+ break
+ else:
+ if param.kind == _VAR_POSITIONAL:
+ # That's OK, just empty *args. Let's start parsing
+ # kwargs
+ break
+ elif param.name in kwargs:
+ if param.kind == _POSITIONAL_ONLY:
+ msg = '{arg!r} parameter is positional only, ' \
+ 'but was passed as a keyword'
+ msg = msg.format(arg=param.name)
+ raise TypeError(msg)
+ parameters_ex = (param,)
+ break
+ elif (param.kind == _VAR_KEYWORD or
+ param.default is not _empty):
+ # That's fine too - we have a default value for this
+ # parameter. So, lets start parsing `kwargs`, starting
+ # with the current parameter
+ parameters_ex = (param,)
+ break
+ else:
+ if partial:
+ parameters_ex = (param,)
+ break
+ else:
+ msg = '{arg!r} parameter lacking default value'
+ msg = msg.format(arg=param.name)
+ raise TypeError(msg)
+ else:
+ # We have a positional argument to process
+ try:
+ param = next(parameters)
+ except StopIteration:
+ raise TypeError('too many positional arguments')
+ else:
+ if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY):
+ # Looks like we have no parameter for this positional
+ # argument
+ raise TypeError('too many positional arguments')
+
+ if param.kind == _VAR_POSITIONAL:
+ # We have an '*args'-like argument, let's fill it with
+ # all positional arguments we have left and move on to
+ # the next phase
+ values = [arg_val]
+ values.extend(arg_vals)
+ arguments[param.name] = tuple(values)
+ break
+
+ if param.name in kwargs:
+ raise TypeError('multiple values for argument '
+ '{arg!r}'.format(arg=param.name))
+
+ arguments[param.name] = arg_val
+
+ # Now, we iterate through the remaining parameters to process
+ # keyword arguments
+ kwargs_param = None
+ for param in itertools.chain(parameters_ex, parameters):
+ if param.kind == _POSITIONAL_ONLY:
+ # This should never happen in case of a properly built
+ # Signature object (but let's have this check here
+ # to ensure correct behaviour just in case)
+ raise TypeError('{arg!r} parameter is positional only, '
+ 'but was passed as a keyword'. \
+ format(arg=param.name))
+
+ if param.kind == _VAR_KEYWORD:
+ # Memorize that we have a '**kwargs'-like parameter
+ kwargs_param = param
+ continue
+
+ param_name = param.name
+ try:
+ arg_val = kwargs.pop(param_name)
+ except KeyError:
+ # We have no value for this parameter. It's fine though,
+ # if it has a default value, or it is an '*args'-like
+ # parameter, left alone by the processing of positional
+ # arguments.
+ if (not partial and param.kind != _VAR_POSITIONAL and
+ param.default is _empty):
+ raise TypeError('{arg!r} parameter lacking default value'. \
+ format(arg=param_name))
+
+ else:
+ arguments[param_name] = arg_val
+
+ if kwargs:
+ if kwargs_param is not None:
+ # Process our '**kwargs'-like parameter
+ arguments[kwargs_param.name] = kwargs
+ else:
+ raise TypeError('too many keyword arguments')
+
+ return self._bound_arguments_cls(self, arguments)
+
+ def bind(self, *args, **kwargs):
+ '''Get a BoundArguments object, that maps the passed `args`
+ and `kwargs` to the function's signature. Raises `TypeError`
+ if the passed arguments can not be bound.
+ '''
+ return self._bind(args, kwargs)
+
+ def bind_partial(self, *args, **kwargs):
+ '''Get a BoundArguments object, that partially maps the
+ passed `args` and `kwargs` to the function's signature.
+ Raises `TypeError` if the passed arguments can not be bound.
+ '''
+ return self._bind(args, kwargs, partial=True)
+
+ def __str__(self):
+ result = []
+ render_kw_only_separator = True
+ for idx, param in enumerate(self.parameters.values()):
+ formatted = str(param)
+
+ kind = param.kind
+ if kind == _VAR_POSITIONAL:
+ # OK, we have an '*args'-like parameter, so we won't need
+ # a '*' to separate keyword-only arguments
+ render_kw_only_separator = False
+ elif kind == _KEYWORD_ONLY and render_kw_only_separator:
+ # We have a keyword-only parameter to render and we haven't
+ # rendered an '*args'-like parameter before, so add a '*'
+ # separator to the parameters list ("foo(arg1, *, arg2)" case)
+ result.append('*')
+ # This condition should be only triggered once, so
+ # reset the flag
+ render_kw_only_separator = False
+
+ result.append(formatted)
+
+ rendered = '({0})'.format(', '.join(result))
+
+ if self.return_annotation is not _empty:
+ anno = formatannotation(self.return_annotation)
+ rendered += ' -> {0}'.format(anno)
+
+ return rendered
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/odict.py b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/odict.py
new file mode 100644
index 00000000000..6221e971b37
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/odict.py
@@ -0,0 +1,261 @@
+# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy.
+# Passes Python2.7's test suite and incorporates all the latest updates.
+# Copyright 2009 Raymond Hettinger
+# http://code.activestate.com/recipes/576693/
+"Ordered dictionary"
+
+try:
+ from thread import get_ident as _get_ident
+except ImportError:
+ from dummy_thread import get_ident as _get_ident
+
+try:
+ from _abcoll import KeysView, ValuesView, ItemsView
+except ImportError:
+ pass
+
+
+class OrderedDict(dict):
+ 'Dictionary that remembers insertion order'
+ # An inherited dict maps keys to values.
+ # The inherited dict provides __getitem__, __len__, __contains__, and get.
+ # The remaining methods are order-aware.
+ # Big-O running times for all methods are the same as for regular dictionaries.
+
+ # The internal self.__map dictionary maps keys to links in a doubly linked list.
+ # The circular doubly linked list starts and ends with a sentinel element.
+ # The sentinel element never gets deleted (this simplifies the algorithm).
+ # Each link is stored as a list of length three: [PREV, NEXT, KEY].
+
+ def __init__(self, *args, **kwds):
+ '''Initialize an ordered dictionary. Signature is the same as for
+ regular dictionaries, but keyword arguments are not recommended
+ because their insertion order is arbitrary.
+
+ '''
+ if len(args) > 1:
+ raise TypeError('expected at most 1 arguments, got %d' % len(args))
+ try:
+ self.__root
+ except AttributeError:
+ self.__root = root = [] # sentinel node
+ root[:] = [root, root, None]
+ self.__map = {}
+ self.__update(*args, **kwds)
+
+ def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
+ 'od.__setitem__(i, y) <==> od[i]=y'
+ # Setting a new item creates a new link which goes at the end of the linked
+ # list, and the inherited dictionary is updated with the new key/value pair.
+ if key not in self:
+ root = self.__root
+ last = root[0]
+ last[1] = root[0] = self.__map[key] = [last, root, key]
+ dict_setitem(self, key, value)
+
+ def __delitem__(self, key, dict_delitem=dict.__delitem__):
+ 'od.__delitem__(y) <==> del od[y]'
+ # Deleting an existing item uses self.__map to find the link which is
+ # then removed by updating the links in the predecessor and successor nodes.
+ dict_delitem(self, key)
+ link_prev, link_next, key = self.__map.pop(key)
+ link_prev[1] = link_next
+ link_next[0] = link_prev
+
+ def __iter__(self):
+ 'od.__iter__() <==> iter(od)'
+ root = self.__root
+ curr = root[1]
+ while curr is not root:
+ yield curr[2]
+ curr = curr[1]
+
+ def __reversed__(self):
+ 'od.__reversed__() <==> reversed(od)'
+ root = self.__root
+ curr = root[0]
+ while curr is not root:
+ yield curr[2]
+ curr = curr[0]
+
+ def clear(self):
+ 'od.clear() -> None. Remove all items from od.'
+ try:
+ for node in self.__map.itervalues():
+ del node[:]
+ root = self.__root
+ root[:] = [root, root, None]
+ self.__map.clear()
+ except AttributeError:
+ pass
+ dict.clear(self)
+
+ def popitem(self, last=True):
+ '''od.popitem() -> (k, v), return and remove a (key, value) pair.
+ Pairs are returned in LIFO order if last is true or FIFO order if false.
+
+ '''
+ if not self:
+ raise KeyError('dictionary is empty')
+ root = self.__root
+ if last:
+ link = root[0]
+ link_prev = link[0]
+ link_prev[1] = root
+ root[0] = link_prev
+ else:
+ link = root[1]
+ link_next = link[1]
+ root[1] = link_next
+ link_next[0] = root
+ key = link[2]
+ del self.__map[key]
+ value = dict.pop(self, key)
+ return key, value
+
+ # -- the following methods do not depend on the internal structure --
+
+ def keys(self):
+ 'od.keys() -> list of keys in od'
+ return list(self)
+
+ def values(self):
+ 'od.values() -> list of values in od'
+ return [self[key] for key in self]
+
+ def items(self):
+ 'od.items() -> list of (key, value) pairs in od'
+ return [(key, self[key]) for key in self]
+
+ def iterkeys(self):
+ 'od.iterkeys() -> an iterator over the keys in od'
+ return iter(self)
+
+ def itervalues(self):
+ 'od.itervalues -> an iterator over the values in od'
+ for k in self:
+ yield self[k]
+
+ def iteritems(self):
+ 'od.iteritems -> an iterator over the (key, value) items in od'
+ for k in self:
+ yield (k, self[k])
+
+ def update(*args, **kwds):
+ '''od.update(E, **F) -> None. Update od from dict/iterable E and F.
+
+ If E is a dict instance, does: for k in E: od[k] = E[k]
+ If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]
+ Or if E is an iterable of items, does: for k, v in E: od[k] = v
+ In either case, this is followed by: for k, v in F.items(): od[k] = v
+
+ '''
+ if len(args) > 2:
+ raise TypeError('update() takes at most 2 positional '
+ 'arguments (%d given)' % (len(args),))
+ elif not args:
+ raise TypeError('update() takes at least 1 argument (0 given)')
+ self = args[0]
+ # Make progressively weaker assumptions about "other"
+ other = ()
+ if len(args) == 2:
+ other = args[1]
+ if isinstance(other, dict):
+ for key in other:
+ self[key] = other[key]
+ elif hasattr(other, 'keys'):
+ for key in other.keys():
+ self[key] = other[key]
+ else:
+ for key, value in other:
+ self[key] = value
+ for key, value in kwds.items():
+ self[key] = value
+
+ __update = update # let subclasses override update without breaking __init__
+
+ __marker = object()
+
+ def pop(self, key, default=__marker):
+ '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
+ If key is not found, d is returned if given, otherwise KeyError is raised.
+
+ '''
+ if key in self:
+ result = self[key]
+ del self[key]
+ return result
+ if default is self.__marker:
+ raise KeyError(key)
+ return default
+
+ def setdefault(self, key, default=None):
+ 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
+ if key in self:
+ return self[key]
+ self[key] = default
+ return default
+
+ def __repr__(self, _repr_running={}):
+ 'od.__repr__() <==> repr(od)'
+ call_key = id(self), _get_ident()
+ if call_key in _repr_running:
+ return '...'
+ _repr_running[call_key] = 1
+ try:
+ if not self:
+ return '%s()' % (self.__class__.__name__,)
+ return '%s(%r)' % (self.__class__.__name__, self.items())
+ finally:
+ del _repr_running[call_key]
+
+ def __reduce__(self):
+ 'Return state information for pickling'
+ items = [[k, self[k]] for k in self]
+ inst_dict = vars(self).copy()
+ for k in vars(OrderedDict()):
+ inst_dict.pop(k, None)
+ if inst_dict:
+ return (self.__class__, (items,), inst_dict)
+ return self.__class__, (items,)
+
+ def copy(self):
+ 'od.copy() -> a shallow copy of od'
+ return self.__class__(self)
+
+ @classmethod
+ def fromkeys(cls, iterable, value=None):
+ '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
+ and values equal to v (which defaults to None).
+
+ '''
+ d = cls()
+ for key in iterable:
+ d[key] = value
+ return d
+
+ def __eq__(self, other):
+ '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
+ while comparison to a regular mapping is order-insensitive.
+
+ '''
+ if isinstance(other, OrderedDict):
+ return len(self)==len(other) and self.items() == other.items()
+ return dict.__eq__(self, other)
+
+ def __ne__(self, other):
+ return not self == other
+
+ # -- the following methods are only used in Python 2.7 --
+
+ def viewkeys(self):
+ "od.viewkeys() -> a set-like object providing a view on od's keys"
+ return KeysView(self)
+
+ def viewvalues(self):
+ "od.viewvalues() -> an object providing a view on od's values"
+ return ValuesView(self)
+
+ def viewitems(self):
+ "od.viewitems() -> a set-like object providing a view on od's items"
+ return ItemsView(self)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/version.py b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/version.py
new file mode 100644
index 00000000000..896a370cad1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/funcsigs/version.py
@@ -0,0 +1 @@
+__version__ = "0.4"
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/requirements/development.txt b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/requirements/development.txt
new file mode 100644
index 00000000000..ecafb0a527d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/requirements/development.txt
@@ -0,0 +1,6 @@
+coverage
+coveralls
+pip
+flake8
+sphinx
+wheel
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/requirements/production.txt
index e69de29bb2d..e69de29bb2d 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/requirements/production.txt
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/setup.cfg b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/setup.cfg
new file mode 100644
index 00000000000..5e4090017a9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/setup.cfg
@@ -0,0 +1,2 @@
+[wheel]
+universal = 1
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/setup.py b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/setup.py
new file mode 100644
index 00000000000..98b09126045
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/setup.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+from setuptools import setup
+import re
+import sys
+
+def load_version(filename='funcsigs/version.py'):
+ "Parse a __version__ number from a source file"
+ with open(filename) as source:
+ text = source.read()
+ match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", text)
+ if not match:
+ msg = "Unable to find version number in {}".format(filename)
+ raise RuntimeError(msg)
+ version = match.group(1)
+ return version
+
+def load_rst(filename='docs/source/guide_content.rst'):
+ "Purge refs directives from restructured text"
+ with open(filename) as source:
+ text = source.read()
+ doc = re.sub(r':\w+:`~?([a-zA-Z._()]+)`', r'*\1*', text)
+ return doc
+
+setup(
+ name="funcsigs",
+ version=load_version(),
+ packages=['funcsigs'],
+ zip_safe=False,
+ author="Aaron Iles",
+ author_email="aaron.iles@gmail.com",
+ url="http://funcsigs.readthedocs.org",
+ description="Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+",
+ long_description=open('README.rst').read(),
+ # long_description=load_rst(),
+ license="ASL",
+ install_requires = [],
+ classifiers = [
+ 'Development Status :: 4 - Beta',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Apache Software License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.2',
+ 'Programming Language :: Python :: 3.3',
+ 'Programming Language :: Python :: Implementation :: CPython',
+ 'Programming Language :: Python :: Implementation :: PyPy',
+ 'Topic :: Software Development :: Libraries :: Python Modules'
+ ],
+ tests_require = [] if sys.version_info[0] > 2 else ['unittest2'],
+ test_suite = "tests" if sys.version_info[0] > 2 else 'unittest2.collector'
+)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_formatannotation.py b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_formatannotation.py
new file mode 100644
index 00000000000..fd7a8873f85
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_formatannotation.py
@@ -0,0 +1,27 @@
+try:
+ # python 2.x
+ import unittest2 as unittest
+except ImportError:
+ # python 3.x
+ import unittest
+
+import funcsigs
+
+
+class TestFormatAnnotation(unittest.TestCase):
+ def test_string (self):
+ self.assertEqual(funcsigs.formatannotation("annotation"),
+ "'annotation'")
+
+ def test_builtin_type (self):
+ self.assertEqual(funcsigs.formatannotation(int),
+ "int")
+
+ def test_user_type (self):
+ class dummy (object): pass
+ self.assertEqual(funcsigs.formatannotation(dummy),
+ "tests.test_formatannotation.dummy")
+
+
+if __name__ == "__main__":
+ unittest.begin()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_funcsigs.py b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_funcsigs.py
new file mode 100644
index 00000000000..eecc0a8a709
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_funcsigs.py
@@ -0,0 +1,93 @@
+try:
+ # python 2.x
+ import unittest2 as unittest
+except ImportError:
+ # python 3.x
+ import unittest
+
+import doctest
+import sys
+
+import funcsigs as inspect
+
+
+class TestFunctionSignatures(unittest.TestCase):
+
+ @staticmethod
+ def signature(func):
+ sig = inspect.signature(func)
+ return (tuple((param.name,
+ (Ellipsis if param.default is param.empty else param.default),
+ (Ellipsis if param.annotation is param.empty
+ else param.annotation),
+ str(param.kind).lower())
+ for param in sig.parameters.values()),
+ (Ellipsis if sig.return_annotation is sig.empty
+ else sig.return_annotation))
+
+ def test_zero_arguments(self):
+ def test():
+ pass
+ self.assertEqual(self.signature(test),
+ ((), Ellipsis))
+
+ def test_single_positional_argument(self):
+ def test(a):
+ pass
+ self.assertEqual(self.signature(test),
+ (((('a', Ellipsis, Ellipsis, "positional_or_keyword")),), Ellipsis))
+
+ def test_single_keyword_argument(self):
+ def test(a=None):
+ pass
+ self.assertEqual(self.signature(test),
+ (((('a', None, Ellipsis, "positional_or_keyword")),), Ellipsis))
+
+ def test_var_args(self):
+ def test(*args):
+ pass
+ self.assertEqual(self.signature(test),
+ (((('args', Ellipsis, Ellipsis, "var_positional")),), Ellipsis))
+
+ def test_keywords_args(self):
+ def test(**kwargs):
+ pass
+ self.assertEqual(self.signature(test),
+ (((('kwargs', Ellipsis, Ellipsis, "var_keyword")),), Ellipsis))
+
+ def test_multiple_arguments(self):
+ def test(a, b=None, *args, **kwargs):
+ pass
+ self.assertEqual(self.signature(test), ((
+ ('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('b', None, Ellipsis, "positional_or_keyword"),
+ ('args', Ellipsis, Ellipsis, "var_positional"),
+ ('kwargs', Ellipsis, Ellipsis, "var_keyword"),
+ ), Ellipsis))
+
+ def test_has_version(self):
+ self.assertTrue(inspect.__version__)
+
+ def test_readme(self):
+ doctest.testfile('../README.rst')
+
+ def test_unbound_method(self):
+ if sys.version_info < (3,):
+ self_kind = "positional_only"
+ else:
+ self_kind = "positional_or_keyword"
+ class Test(object):
+ def method(self):
+ pass
+ def method_with_args(self, a):
+ pass
+ self.assertEqual(self.signature(Test.method),
+ (((('self', Ellipsis, Ellipsis, self_kind)),), Ellipsis))
+ self.assertEqual(self.signature(Test.method_with_args), ((
+ ('self', Ellipsis, Ellipsis, self_kind),
+ ('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ), Ellipsis))
+
+
+if __name__ == "__main__":
+ unittest.begin()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_inspect.py b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_inspect.py
new file mode 100644
index 00000000000..323c323eafd
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/funcsigs/tests/test_inspect.py
@@ -0,0 +1,1019 @@
+# Copyright 2001-2013 Python Software Foundation; All Rights Reserved
+from __future__ import absolute_import, division, print_function
+import collections
+import sys
+
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+
+import funcsigs as inspect
+
+
+class TestSignatureObject(unittest.TestCase):
+ @staticmethod
+ def signature(func):
+ sig = inspect.signature(func)
+ return (tuple((param.name,
+ (Ellipsis if param.default is param.empty else param.default),
+ (Ellipsis if param.annotation is param.empty
+ else param.annotation),
+ str(param.kind).lower())
+ for param in sig.parameters.values()),
+ (Ellipsis if sig.return_annotation is sig.empty
+ else sig.return_annotation))
+
+ def __init__(self, *args, **kwargs):
+ unittest.TestCase.__init__(self, *args, **kwargs)
+ if not hasattr(self, 'assertRaisesRegex'):
+ self.assertRaisesRegex = self.assertRaisesRegexp
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_object(self):
+ S = inspect.Signature
+ P = inspect.Parameter
+
+ self.assertEqual(str(S()), '()')
+
+ def test(po, pk, *args, ko, **kwargs):
+ pass
+ sig = inspect.signature(test)
+ po = sig.parameters['po'].replace(kind=P.POSITIONAL_ONLY)
+ pk = sig.parameters['pk']
+ args = sig.parameters['args']
+ ko = sig.parameters['ko']
+ kwargs = sig.parameters['kwargs']
+
+ S((po, pk, args, ko, kwargs))
+
+ with self.assertRaisesRegex(ValueError, 'wrong parameter order'):
+ S((pk, po, args, ko, kwargs))
+
+ with self.assertRaisesRegex(ValueError, 'wrong parameter order'):
+ S((po, args, pk, ko, kwargs))
+
+ with self.assertRaisesRegex(ValueError, 'wrong parameter order'):
+ S((args, po, pk, ko, kwargs))
+
+ with self.assertRaisesRegex(ValueError, 'wrong parameter order'):
+ S((po, pk, args, kwargs, ko))
+
+ kwargs2 = kwargs.replace(name='args')
+ with self.assertRaisesRegex(ValueError, 'duplicate parameter name'):
+ S((po, pk, args, kwargs2, ko))
+""")
+
+ def test_signature_immutability(self):
+ def test(a):
+ pass
+ sig = inspect.signature(test)
+
+ with self.assertRaises(AttributeError):
+ sig.foo = 'bar'
+
+ # Python2 does not have MappingProxyType class
+ if sys.version_info[:2] < (3, 3):
+ return
+
+ with self.assertRaises(TypeError):
+ sig.parameters['a'] = None
+
+ def test_signature_on_noarg(self):
+ def test():
+ pass
+ self.assertEqual(self.signature(test), ((), Ellipsis))
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_on_wargs(self):
+ def test(a, b:'foo') -> 123:
+ pass
+ self.assertEqual(self.signature(test),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('b', Ellipsis, 'foo', "positional_or_keyword")),
+ 123))
+""")
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_on_wkwonly(self):
+ def test(*, a:float, b:str) -> int:
+ pass
+ self.assertEqual(self.signature(test),
+ ((('a', Ellipsis, float, "keyword_only"),
+ ('b', Ellipsis, str, "keyword_only")),
+ int))
+""")
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_on_complex_args(self):
+ def test(a, b:'foo'=10, *args:'bar', spam:'baz', ham=123, **kwargs:int):
+ pass
+ self.assertEqual(self.signature(test),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('b', 10, 'foo', "positional_or_keyword"),
+ ('args', Ellipsis, 'bar', "var_positional"),
+ ('spam', Ellipsis, 'baz', "keyword_only"),
+ ('ham', 123, Ellipsis, "keyword_only"),
+ ('kwargs', Ellipsis, int, "var_keyword")),
+ Ellipsis))
+""")
+
+ def test_signature_on_builtin_function(self):
+ with self.assertRaisesRegex(ValueError, 'not supported by signature'):
+ inspect.signature(type)
+ with self.assertRaisesRegex(ValueError, 'not supported by signature'):
+ # support for 'wrapper_descriptor'
+ inspect.signature(type.__call__)
+ if hasattr(sys, 'pypy_version_info'):
+ raise ValueError('not supported by signature')
+ with self.assertRaisesRegex(ValueError, 'not supported by signature'):
+ # support for 'method-wrapper'
+ inspect.signature(min.__call__)
+ if hasattr(sys, 'pypy_version_info'):
+ raise ValueError('not supported by signature')
+ with self.assertRaisesRegex(ValueError,
+ 'no signature found for builtin function'):
+ # support for 'method-wrapper'
+ inspect.signature(min)
+
+ def test_signature_on_non_function(self):
+ with self.assertRaisesRegex(TypeError, 'is not a callable object'):
+ inspect.signature(42)
+
+ with self.assertRaisesRegex(TypeError, 'is not a Python function'):
+ inspect.Signature.from_function(42)
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_on_method(self):
+ class Test:
+ def foo(self, arg1, arg2=1) -> int:
+ pass
+
+ meth = Test().foo
+
+ self.assertEqual(self.signature(meth),
+ ((('arg1', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('arg2', 1, Ellipsis, "positional_or_keyword")),
+ int))
+""")
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_on_classmethod(self):
+ class Test:
+ @classmethod
+ def foo(cls, arg1, *, arg2=1):
+ pass
+
+ meth = Test().foo
+ self.assertEqual(self.signature(meth),
+ ((('arg1', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('arg2', 1, Ellipsis, "keyword_only")),
+ Ellipsis))
+
+ meth = Test.foo
+ self.assertEqual(self.signature(meth),
+ ((('arg1', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('arg2', 1, Ellipsis, "keyword_only")),
+ Ellipsis))
+""")
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_on_staticmethod(self):
+ class Test:
+ @staticmethod
+ def foo(cls, *, arg):
+ pass
+
+ meth = Test().foo
+ self.assertEqual(self.signature(meth),
+ ((('cls', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('arg', Ellipsis, Ellipsis, "keyword_only")),
+ Ellipsis))
+
+ meth = Test.foo
+ self.assertEqual(self.signature(meth),
+ ((('cls', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('arg', Ellipsis, Ellipsis, "keyword_only")),
+ Ellipsis))
+""")
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_on_partial(self):
+ from functools import partial
+
+ def test():
+ pass
+
+ self.assertEqual(self.signature(partial(test)), ((), Ellipsis))
+
+ with self.assertRaisesRegex(ValueError, "has incorrect arguments"):
+ inspect.signature(partial(test, 1))
+
+ with self.assertRaisesRegex(ValueError, "has incorrect arguments"):
+ inspect.signature(partial(test, a=1))
+
+ def test(a, b, *, c, d):
+ pass
+
+ self.assertEqual(self.signature(partial(test)),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('b', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('c', Ellipsis, Ellipsis, "keyword_only"),
+ ('d', Ellipsis, Ellipsis, "keyword_only")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(partial(test, 1)),
+ ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('c', Ellipsis, Ellipsis, "keyword_only"),
+ ('d', Ellipsis, Ellipsis, "keyword_only")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(partial(test, 1, c=2)),
+ ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('c', 2, Ellipsis, "keyword_only"),
+ ('d', Ellipsis, Ellipsis, "keyword_only")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(partial(test, b=1, c=2)),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('b', 1, Ellipsis, "positional_or_keyword"),
+ ('c', 2, Ellipsis, "keyword_only"),
+ ('d', Ellipsis, Ellipsis, "keyword_only")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(partial(test, 0, b=1, c=2)),
+ ((('b', 1, Ellipsis, "positional_or_keyword"),
+ ('c', 2, Ellipsis, "keyword_only"),
+ ('d', Ellipsis, Ellipsis, "keyword_only"),),
+ Ellipsis))
+
+ def test(a, *args, b, **kwargs):
+ pass
+
+ self.assertEqual(self.signature(partial(test, 1)),
+ ((('args', Ellipsis, Ellipsis, "var_positional"),
+ ('b', Ellipsis, Ellipsis, "keyword_only"),
+ ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(partial(test, 1, 2, 3)),
+ ((('args', Ellipsis, Ellipsis, "var_positional"),
+ ('b', Ellipsis, Ellipsis, "keyword_only"),
+ ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
+ Ellipsis))
+
+
+ self.assertEqual(self.signature(partial(test, 1, 2, 3, test=True)),
+ ((('args', Ellipsis, Ellipsis, "var_positional"),
+ ('b', Ellipsis, Ellipsis, "keyword_only"),
+ ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(partial(test, 1, 2, 3, test=1, b=0)),
+ ((('args', Ellipsis, Ellipsis, "var_positional"),
+ ('b', 0, Ellipsis, "keyword_only"),
+ ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(partial(test, b=0)),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('args', Ellipsis, Ellipsis, "var_positional"),
+ ('b', 0, Ellipsis, "keyword_only"),
+ ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(partial(test, b=0, test=1)),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('args', Ellipsis, Ellipsis, "var_positional"),
+ ('b', 0, Ellipsis, "keyword_only"),
+ ('kwargs', Ellipsis, Ellipsis, "var_keyword")),
+ Ellipsis))
+
+ def test(a, b, c:int) -> 42:
+ pass
+
+ sig = test.__signature__ = inspect.signature(test)
+
+ self.assertEqual(self.signature(partial(partial(test, 1))),
+ ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('c', Ellipsis, int, "positional_or_keyword")),
+ 42))
+
+ self.assertEqual(self.signature(partial(partial(test, 1), 2)),
+ ((('c', Ellipsis, int, "positional_or_keyword"),),
+ 42))
+
+ psig = inspect.signature(partial(partial(test, 1), 2))
+
+ def foo(a):
+ return a
+ _foo = partial(partial(foo, a=10), a=20)
+ self.assertEqual(self.signature(_foo),
+ ((('a', 20, Ellipsis, "positional_or_keyword"),),
+ Ellipsis))
+ # check that we don't have any side-effects in signature(),
+ # and the partial object is still functioning
+ self.assertEqual(_foo(), 20)
+
+ def foo(a, b, c):
+ return a, b, c
+ _foo = partial(partial(foo, 1, b=20), b=30)
+ self.assertEqual(self.signature(_foo),
+ ((('b', 30, Ellipsis, "positional_or_keyword"),
+ ('c', Ellipsis, Ellipsis, "positional_or_keyword")),
+ Ellipsis))
+ self.assertEqual(_foo(c=10), (1, 30, 10))
+ _foo = partial(_foo, 2) # now 'b' has two values -
+ # positional and keyword
+ with self.assertRaisesRegex(ValueError, "has incorrect arguments"):
+ inspect.signature(_foo)
+
+ def foo(a, b, c, *, d):
+ return a, b, c, d
+ _foo = partial(partial(foo, d=20, c=20), b=10, d=30)
+ self.assertEqual(self.signature(_foo),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('b', 10, Ellipsis, "positional_or_keyword"),
+ ('c', 20, Ellipsis, "positional_or_keyword"),
+ ('d', 30, Ellipsis, "keyword_only")),
+ Ellipsis))
+ ba = inspect.signature(_foo).bind(a=200, b=11)
+ self.assertEqual(_foo(*ba.args, **ba.kwargs), (200, 11, 20, 30))
+
+ def foo(a=1, b=2, c=3):
+ return a, b, c
+ _foo = partial(foo, a=10, c=13)
+ ba = inspect.signature(_foo).bind(11)
+ self.assertEqual(_foo(*ba.args, **ba.kwargs), (11, 2, 13))
+ ba = inspect.signature(_foo).bind(11, 12)
+ self.assertEqual(_foo(*ba.args, **ba.kwargs), (11, 12, 13))
+ ba = inspect.signature(_foo).bind(11, b=12)
+ self.assertEqual(_foo(*ba.args, **ba.kwargs), (11, 12, 13))
+ ba = inspect.signature(_foo).bind(b=12)
+ self.assertEqual(_foo(*ba.args, **ba.kwargs), (10, 12, 13))
+ _foo = partial(_foo, b=10)
+ ba = inspect.signature(_foo).bind(12, 14)
+ self.assertEqual(_foo(*ba.args, **ba.kwargs), (12, 14, 13))
+""")
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_on_decorated(self):
+ import functools
+
+ def decorator(func):
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs) -> int:
+ return func(*args, **kwargs)
+ return wrapper
+
+ class Foo:
+ @decorator
+ def bar(self, a, b):
+ pass
+
+ self.assertEqual(self.signature(Foo.bar),
+ ((('self', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('b', Ellipsis, Ellipsis, "positional_or_keyword")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(Foo().bar),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('b', Ellipsis, Ellipsis, "positional_or_keyword")),
+ Ellipsis))
+
+ # Test that we handle method wrappers correctly
+ def decorator(func):
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs) -> int:
+ return func(42, *args, **kwargs)
+ sig = inspect.signature(func)
+ new_params = tuple(sig.parameters.values())[1:]
+ wrapper.__signature__ = sig.replace(parameters=new_params)
+ return wrapper
+
+ class Foo:
+ @decorator
+ def __call__(self, a, b):
+ pass
+
+ self.assertEqual(self.signature(Foo.__call__),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('b', Ellipsis, Ellipsis, "positional_or_keyword")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(Foo().__call__),
+ ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),),
+ Ellipsis))
+""")
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_on_class(self):
+ class C:
+ def __init__(self, a):
+ pass
+
+ self.assertEqual(self.signature(C),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),),
+ Ellipsis))
+
+ class CM(type):
+ def __call__(cls, a):
+ pass
+ class C(metaclass=CM):
+ def __init__(self, b):
+ pass
+
+ self.assertEqual(self.signature(C),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),),
+ Ellipsis))
+
+ class CM(type):
+ def __new__(mcls, name, bases, dct, *, foo=1):
+ return super().__new__(mcls, name, bases, dct)
+ class C(metaclass=CM):
+ def __init__(self, b):
+ pass
+
+ self.assertEqual(self.signature(C),
+ ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),),
+ Ellipsis))
+
+ self.assertEqual(self.signature(CM),
+ ((('name', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('bases', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('dct', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('foo', 1, Ellipsis, "keyword_only")),
+ Ellipsis))
+
+ class CMM(type):
+ def __new__(mcls, name, bases, dct, *, foo=1):
+ return super().__new__(mcls, name, bases, dct)
+ def __call__(cls, nm, bs, dt):
+ return type(nm, bs, dt)
+ class CM(type, metaclass=CMM):
+ def __new__(mcls, name, bases, dct, *, bar=2):
+ return super().__new__(mcls, name, bases, dct)
+ class C(metaclass=CM):
+ def __init__(self, b):
+ pass
+
+ self.assertEqual(self.signature(CMM),
+ ((('name', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('bases', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('dct', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('foo', 1, Ellipsis, "keyword_only")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(CM),
+ ((('nm', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('bs', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('dt', Ellipsis, Ellipsis, "positional_or_keyword")),
+ Ellipsis))
+
+ self.assertEqual(self.signature(C),
+ ((('b', Ellipsis, Ellipsis, "positional_or_keyword"),),
+ Ellipsis))
+
+ class CM(type):
+ def __init__(cls, name, bases, dct, *, bar=2):
+ return super().__init__(name, bases, dct)
+ class C(metaclass=CM):
+ def __init__(self, b):
+ pass
+
+ self.assertEqual(self.signature(CM),
+ ((('name', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('bases', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('dct', Ellipsis, Ellipsis, "positional_or_keyword"),
+ ('bar', 2, Ellipsis, "keyword_only")),
+ Ellipsis))
+""")
+
+ def test_signature_on_callable_objects(self):
+ class Foo(object):
+ def __call__(self, a):
+ pass
+
+ self.assertEqual(self.signature(Foo()),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),),
+ Ellipsis))
+
+ class Spam(object):
+ pass
+ with self.assertRaisesRegex(TypeError, "is not a callable object"):
+ inspect.signature(Spam())
+
+ class Bar(Spam, Foo):
+ pass
+
+ self.assertEqual(self.signature(Bar()),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),),
+ Ellipsis))
+
+ class ToFail(object):
+ __call__ = type
+ with self.assertRaisesRegex(ValueError, "not supported by signature"):
+ inspect.signature(ToFail())
+
+ if sys.version_info[0] < 3:
+ return
+
+ class Wrapped(object):
+ pass
+ Wrapped.__wrapped__ = lambda a: None
+ self.assertEqual(self.signature(Wrapped),
+ ((('a', Ellipsis, Ellipsis, "positional_or_keyword"),),
+ Ellipsis))
+
+ def test_signature_on_lambdas(self):
+ self.assertEqual(self.signature((lambda a=10: a)),
+ ((('a', 10, Ellipsis, "positional_or_keyword"),),
+ Ellipsis))
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_equality(self):
+ def foo(a, *, b:int) -> float: pass
+ self.assertNotEqual(inspect.signature(foo), 42)
+
+ def bar(a, *, b:int) -> float: pass
+ self.assertEqual(inspect.signature(foo), inspect.signature(bar))
+
+ def bar(a, *, b:int) -> int: pass
+ self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
+
+ def bar(a, *, b:int): pass
+ self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
+
+ def bar(a, *, b:int=42) -> float: pass
+ self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
+
+ def bar(a, *, c) -> float: pass
+ self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
+
+ def bar(a, b:int) -> float: pass
+ self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
+ def spam(b:int, a) -> float: pass
+ self.assertNotEqual(inspect.signature(spam), inspect.signature(bar))
+
+ def foo(*, a, b, c): pass
+ def bar(*, c, b, a): pass
+ self.assertEqual(inspect.signature(foo), inspect.signature(bar))
+
+ def foo(*, a=1, b, c): pass
+ def bar(*, c, b, a=1): pass
+ self.assertEqual(inspect.signature(foo), inspect.signature(bar))
+
+ def foo(pos, *, a=1, b, c): pass
+ def bar(pos, *, c, b, a=1): pass
+ self.assertEqual(inspect.signature(foo), inspect.signature(bar))
+
+ def foo(pos, *, a, b, c): pass
+ def bar(pos, *, c, b, a=1): pass
+ self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
+
+ def foo(pos, *args, a=42, b, c, **kwargs:int): pass
+ def bar(pos, *args, c, b, a=42, **kwargs:int): pass
+ self.assertEqual(inspect.signature(foo), inspect.signature(bar))
+""")
+
+ def test_signature_unhashable(self):
+ def foo(a): pass
+ sig = inspect.signature(foo)
+ with self.assertRaisesRegex(TypeError, 'unhashable type'):
+ hash(sig)
+
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_str(self):
+ def foo(a:int=1, *, b, c=None, **kwargs) -> 42:
+ pass
+ self.assertEqual(str(inspect.signature(foo)),
+ '(a:int=1, *, b, c=None, **kwargs) -> 42')
+
+ def foo(a:int=1, *args, b, c=None, **kwargs) -> 42:
+ pass
+ self.assertEqual(str(inspect.signature(foo)),
+ '(a:int=1, *args, b, c=None, **kwargs) -> 42')
+
+ def foo():
+ pass
+ self.assertEqual(str(inspect.signature(foo)), '()')
+""")
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_str_positional_only(self):
+ P = inspect.Parameter
+
+ def test(a_po, *, b, **kwargs):
+ return a_po, kwargs
+
+ sig = inspect.signature(test)
+ new_params = list(sig.parameters.values())
+ new_params[0] = new_params[0].replace(kind=P.POSITIONAL_ONLY)
+ test.__signature__ = sig.replace(parameters=new_params)
+
+ self.assertEqual(str(inspect.signature(test)),
+ '(<a_po>, *, b, **kwargs)')
+
+ sig = inspect.signature(test)
+ new_params = list(sig.parameters.values())
+ new_params[0] = new_params[0].replace(name=None)
+ test.__signature__ = sig.replace(parameters=new_params)
+ self.assertEqual(str(inspect.signature(test)),
+ '(<0>, *, b, **kwargs)')
+""")
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_replace_anno(self):
+ def test() -> 42:
+ pass
+
+ sig = inspect.signature(test)
+ sig = sig.replace(return_annotation=None)
+ self.assertIs(sig.return_annotation, None)
+ sig = sig.replace(return_annotation=sig.empty)
+ self.assertIs(sig.return_annotation, sig.empty)
+ sig = sig.replace(return_annotation=42)
+ self.assertEqual(sig.return_annotation, 42)
+ self.assertEqual(sig, inspect.signature(test))
+""")
+
+
+class TestParameterObject(unittest.TestCase):
+
+ def __init__(self, *args, **kwargs):
+ unittest.TestCase.__init__(self, *args, **kwargs)
+ if not hasattr(self, 'assertRaisesRegex'):
+ self.assertRaisesRegex = self.assertRaisesRegexp
+
+ def test_signature_parameter_kinds(self):
+ P = inspect.Parameter
+ self.assertTrue(P.POSITIONAL_ONLY < P.POSITIONAL_OR_KEYWORD < \
+ P.VAR_POSITIONAL < P.KEYWORD_ONLY < P.VAR_KEYWORD)
+
+ self.assertEqual(str(P.POSITIONAL_ONLY), 'POSITIONAL_ONLY')
+ self.assertTrue('POSITIONAL_ONLY' in repr(P.POSITIONAL_ONLY))
+
+ def test_signature_parameter_object(self):
+ p = inspect.Parameter('foo', default=10,
+ kind=inspect.Parameter.POSITIONAL_ONLY)
+ self.assertEqual(p.name, 'foo')
+ self.assertEqual(p.default, 10)
+ self.assertIs(p.annotation, p.empty)
+ self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
+
+ with self.assertRaisesRegex(ValueError, 'invalid value'):
+ inspect.Parameter('foo', default=10, kind='123')
+
+ with self.assertRaisesRegex(ValueError, 'not a valid parameter name'):
+ inspect.Parameter('1', kind=inspect.Parameter.VAR_KEYWORD)
+
+ with self.assertRaisesRegex(ValueError,
+ 'non-positional-only parameter'):
+ inspect.Parameter(None, kind=inspect.Parameter.VAR_KEYWORD)
+
+ with self.assertRaisesRegex(ValueError, 'cannot have default values'):
+ inspect.Parameter('a', default=42,
+ kind=inspect.Parameter.VAR_KEYWORD)
+
+ with self.assertRaisesRegex(ValueError, 'cannot have default values'):
+ inspect.Parameter('a', default=42,
+ kind=inspect.Parameter.VAR_POSITIONAL)
+
+ p = inspect.Parameter('a', default=42,
+ kind=inspect.Parameter.POSITIONAL_OR_KEYWORD)
+ with self.assertRaisesRegex(ValueError, 'cannot have default values'):
+ p.replace(kind=inspect.Parameter.VAR_POSITIONAL)
+
+ self.assertTrue(repr(p).startswith('<Parameter'))
+
+ def test_signature_parameter_equality(self):
+ P = inspect.Parameter
+ p = P('foo', default=42, kind=inspect.Parameter.KEYWORD_ONLY)
+
+ self.assertEqual(p, p)
+ self.assertNotEqual(p, 42)
+
+ self.assertEqual(p, P('foo', default=42,
+ kind=inspect.Parameter.KEYWORD_ONLY))
+
+ def test_signature_parameter_unhashable(self):
+ p = inspect.Parameter('foo', default=42,
+ kind=inspect.Parameter.KEYWORD_ONLY)
+
+ with self.assertRaisesRegex(TypeError, 'unhashable type'):
+ hash(p)
+
+ def test_signature_parameter_replace(self):
+ p = inspect.Parameter('foo', default=42,
+ kind=inspect.Parameter.KEYWORD_ONLY)
+
+ self.assertIsNot(p, p.replace())
+ self.assertEqual(p, p.replace())
+
+ p2 = p.replace(annotation=1)
+ self.assertEqual(p2.annotation, 1)
+ p2 = p2.replace(annotation=p2.empty)
+ self.assertEqual(p, p2)
+
+ p2 = p2.replace(name='bar')
+ self.assertEqual(p2.name, 'bar')
+ self.assertNotEqual(p2, p)
+
+ with self.assertRaisesRegex(ValueError, 'not a valid parameter name'):
+ p2 = p2.replace(name=p2.empty)
+
+ p2 = p2.replace(name='foo', default=None)
+ self.assertIs(p2.default, None)
+ self.assertNotEqual(p2, p)
+
+ p2 = p2.replace(name='foo', default=p2.empty)
+ self.assertIs(p2.default, p2.empty)
+
+
+ p2 = p2.replace(default=42, kind=p2.POSITIONAL_OR_KEYWORD)
+ self.assertEqual(p2.kind, p2.POSITIONAL_OR_KEYWORD)
+ self.assertNotEqual(p2, p)
+
+ with self.assertRaisesRegex(ValueError, 'invalid value for'):
+ p2 = p2.replace(kind=p2.empty)
+
+ p2 = p2.replace(kind=p2.KEYWORD_ONLY)
+ self.assertEqual(p2, p)
+
+ def test_signature_parameter_positional_only(self):
+ p = inspect.Parameter(None, kind=inspect.Parameter.POSITIONAL_ONLY)
+ self.assertEqual(str(p), '<>')
+
+ p = p.replace(name='1')
+ self.assertEqual(str(p), '<1>')
+
+ def test_signature_parameter_immutability(self):
+ p = inspect.Parameter(None, kind=inspect.Parameter.POSITIONAL_ONLY)
+
+ with self.assertRaises(AttributeError):
+ p.foo = 'bar'
+
+ with self.assertRaises(AttributeError):
+ p.kind = 123
+
+
+class TestSignatureBind(unittest.TestCase):
+ @staticmethod
+ def call(func, *args, **kwargs):
+ sig = inspect.signature(func)
+ ba = sig.bind(*args, **kwargs)
+ return func(*ba.args, **ba.kwargs)
+
+ def __init__(self, *args, **kwargs):
+ unittest.TestCase.__init__(self, *args, **kwargs)
+ if not hasattr(self, 'assertRaisesRegex'):
+ self.assertRaisesRegex = self.assertRaisesRegexp
+
+ def test_signature_bind_empty(self):
+ def test():
+ return 42
+
+ self.assertEqual(self.call(test), 42)
+ with self.assertRaisesRegex(TypeError, 'too many positional arguments'):
+ self.call(test, 1)
+ with self.assertRaisesRegex(TypeError, 'too many positional arguments'):
+ self.call(test, 1, spam=10)
+ with self.assertRaisesRegex(TypeError, 'too many keyword arguments'):
+ self.call(test, spam=1)
+
+ def test_signature_bind_var(self):
+ def test(*args, **kwargs):
+ return args, kwargs
+
+ self.assertEqual(self.call(test), ((), {}))
+ self.assertEqual(self.call(test, 1), ((1,), {}))
+ self.assertEqual(self.call(test, 1, 2), ((1, 2), {}))
+ self.assertEqual(self.call(test, foo='bar'), ((), {'foo': 'bar'}))
+ self.assertEqual(self.call(test, 1, foo='bar'), ((1,), {'foo': 'bar'}))
+ self.assertEqual(self.call(test, args=10), ((), {'args': 10}))
+ self.assertEqual(self.call(test, 1, 2, foo='bar'),
+ ((1, 2), {'foo': 'bar'}))
+
+ def test_signature_bind_just_args(self):
+ def test(a, b, c):
+ return a, b, c
+
+ self.assertEqual(self.call(test, 1, 2, 3), (1, 2, 3))
+
+ with self.assertRaisesRegex(TypeError, 'too many positional arguments'):
+ self.call(test, 1, 2, 3, 4)
+
+ with self.assertRaisesRegex(TypeError, "'b' parameter lacking default"):
+ self.call(test, 1)
+
+ with self.assertRaisesRegex(TypeError, "'a' parameter lacking default"):
+ self.call(test)
+
+ def test(a, b, c=10):
+ return a, b, c
+ self.assertEqual(self.call(test, 1, 2, 3), (1, 2, 3))
+ self.assertEqual(self.call(test, 1, 2), (1, 2, 10))
+
+ def test(a=1, b=2, c=3):
+ return a, b, c
+ self.assertEqual(self.call(test, a=10, c=13), (10, 2, 13))
+ self.assertEqual(self.call(test, a=10), (10, 2, 3))
+ self.assertEqual(self.call(test, b=10), (1, 10, 3))
+
+ def test_signature_bind_varargs_order(self):
+ def test(*args):
+ return args
+
+ self.assertEqual(self.call(test), ())
+ self.assertEqual(self.call(test, 1, 2, 3), (1, 2, 3))
+
+ def test_signature_bind_args_and_varargs(self):
+ def test(a, b, c=3, *args):
+ return a, b, c, args
+
+ self.assertEqual(self.call(test, 1, 2, 3, 4, 5), (1, 2, 3, (4, 5)))
+ self.assertEqual(self.call(test, 1, 2), (1, 2, 3, ()))
+ self.assertEqual(self.call(test, b=1, a=2), (2, 1, 3, ()))
+ self.assertEqual(self.call(test, 1, b=2), (1, 2, 3, ()))
+
+ with self.assertRaisesRegex(TypeError,
+ "multiple values for argument 'c'"):
+ self.call(test, 1, 2, 3, c=4)
+
+ def test_signature_bind_just_kwargs(self):
+ def test(**kwargs):
+ return kwargs
+
+ self.assertEqual(self.call(test), {})
+ self.assertEqual(self.call(test, foo='bar', spam='ham'),
+ {'foo': 'bar', 'spam': 'ham'})
+
+ def test_signature_bind_args_and_kwargs(self):
+ def test(a, b, c=3, **kwargs):
+ return a, b, c, kwargs
+
+ self.assertEqual(self.call(test, 1, 2), (1, 2, 3, {}))
+ self.assertEqual(self.call(test, 1, 2, foo='bar', spam='ham'),
+ (1, 2, 3, {'foo': 'bar', 'spam': 'ham'}))
+ self.assertEqual(self.call(test, b=2, a=1, foo='bar', spam='ham'),
+ (1, 2, 3, {'foo': 'bar', 'spam': 'ham'}))
+ self.assertEqual(self.call(test, a=1, b=2, foo='bar', spam='ham'),
+ (1, 2, 3, {'foo': 'bar', 'spam': 'ham'}))
+ self.assertEqual(self.call(test, 1, b=2, foo='bar', spam='ham'),
+ (1, 2, 3, {'foo': 'bar', 'spam': 'ham'}))
+ self.assertEqual(self.call(test, 1, b=2, c=4, foo='bar', spam='ham'),
+ (1, 2, 4, {'foo': 'bar', 'spam': 'ham'}))
+ self.assertEqual(self.call(test, 1, 2, 4, foo='bar'),
+ (1, 2, 4, {'foo': 'bar'}))
+ self.assertEqual(self.call(test, c=5, a=4, b=3),
+ (4, 3, 5, {}))
+
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_bind_kwonly(self):
+ def test(*, foo):
+ return foo
+ with self.assertRaisesRegex(TypeError,
+ 'too many positional arguments'):
+ self.call(test, 1)
+ self.assertEqual(self.call(test, foo=1), 1)
+
+ def test(a, *, foo=1, bar):
+ return foo
+ with self.assertRaisesRegex(TypeError,
+ "'bar' parameter lacking default value"):
+ self.call(test, 1)
+
+ def test(foo, *, bar):
+ return foo, bar
+ self.assertEqual(self.call(test, 1, bar=2), (1, 2))
+ self.assertEqual(self.call(test, bar=2, foo=1), (1, 2))
+
+ with self.assertRaisesRegex(TypeError,
+ 'too many keyword arguments'):
+ self.call(test, bar=2, foo=1, spam=10)
+
+ with self.assertRaisesRegex(TypeError,
+ 'too many positional arguments'):
+ self.call(test, 1, 2)
+
+ with self.assertRaisesRegex(TypeError,
+ 'too many positional arguments'):
+ self.call(test, 1, 2, bar=2)
+
+ with self.assertRaisesRegex(TypeError,
+ 'too many keyword arguments'):
+ self.call(test, 1, bar=2, spam='ham')
+
+ with self.assertRaisesRegex(TypeError,
+ "'bar' parameter lacking default value"):
+ self.call(test, 1)
+
+ def test(foo, *, bar, **bin):
+ return foo, bar, bin
+ self.assertEqual(self.call(test, 1, bar=2), (1, 2, {}))
+ self.assertEqual(self.call(test, foo=1, bar=2), (1, 2, {}))
+ self.assertEqual(self.call(test, 1, bar=2, spam='ham'),
+ (1, 2, {'spam': 'ham'}))
+ self.assertEqual(self.call(test, spam='ham', foo=1, bar=2),
+ (1, 2, {'spam': 'ham'}))
+ with self.assertRaisesRegex(TypeError,
+ "'foo' parameter lacking default value"):
+ self.call(test, spam='ham', bar=2)
+ self.assertEqual(self.call(test, 1, bar=2, bin=1, spam=10),
+ (1, 2, {'bin': 1, 'spam': 10}))
+""")
+#
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_bind_arguments(self):
+ def test(a, *args, b, z=100, **kwargs):
+ pass
+ sig = inspect.signature(test)
+ ba = sig.bind(10, 20, b=30, c=40, args=50, kwargs=60)
+ # we won't have 'z' argument in the bound arguments object, as we didn't
+ # pass it to the 'bind'
+ self.assertEqual(tuple(ba.arguments.items()),
+ (('a', 10), ('args', (20,)), ('b', 30),
+ ('kwargs', {'c': 40, 'args': 50, 'kwargs': 60})))
+ self.assertEqual(ba.kwargs,
+ {'b': 30, 'c': 40, 'args': 50, 'kwargs': 60})
+ self.assertEqual(ba.args, (10, 20))
+""")
+#
+ if sys.version_info[0] > 2:
+ exec("""
+def test_signature_bind_positional_only(self):
+ P = inspect.Parameter
+
+ def test(a_po, b_po, c_po=3, foo=42, *, bar=50, **kwargs):
+ return a_po, b_po, c_po, foo, bar, kwargs
+
+ sig = inspect.signature(test)
+ new_params = collections.OrderedDict(tuple(sig.parameters.items()))
+ for name in ('a_po', 'b_po', 'c_po'):
+ new_params[name] = new_params[name].replace(kind=P.POSITIONAL_ONLY)
+ new_sig = sig.replace(parameters=new_params.values())
+ test.__signature__ = new_sig
+
+ self.assertEqual(self.call(test, 1, 2, 4, 5, bar=6),
+ (1, 2, 4, 5, 6, {}))
+
+ with self.assertRaisesRegex(TypeError, "parameter is positional only"):
+ self.call(test, 1, 2, c_po=4)
+
+ with self.assertRaisesRegex(TypeError, "parameter is positional only"):
+ self.call(test, a_po=1, b_po=2)
+""")
+
+
+class TestBoundArguments(unittest.TestCase):
+
+ def __init__(self, *args, **kwargs):
+ unittest.TestCase.__init__(self, *args, **kwargs)
+ if not hasattr(self, 'assertRaisesRegex'):
+ self.assertRaisesRegex = self.assertRaisesRegexp
+
+ def test_signature_bound_arguments_unhashable(self):
+ def foo(a): pass
+ ba = inspect.signature(foo).bind(1)
+
+ with self.assertRaisesRegex(TypeError, 'unhashable type'):
+ hash(ba)
+
+ def test_signature_bound_arguments_equality(self):
+ def foo(a): pass
+ ba = inspect.signature(foo).bind(1)
+ self.assertEqual(ba, ba)
+
+ ba2 = inspect.signature(foo).bind(1)
+ self.assertEqual(ba, ba2)
+
+ ba3 = inspect.signature(foo).bind(2)
+ self.assertNotEqual(ba, ba3)
+ ba3.arguments['a'] = 1
+ self.assertEqual(ba, ba3)
+
+ def bar(b): pass
+ ba4 = inspect.signature(bar).bind(1)
+ self.assertNotEqual(ba, ba4)
+
+
+if __name__ == "__main__":
+ unittest.begin()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/.gitignore b/tests/wpt/web-platform-tests/tools/third_party/pluggy/.gitignore
new file mode 100644
index 00000000000..e5c7e98041c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/.gitignore
@@ -0,0 +1,58 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+*.swp
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/.travis.yml b/tests/wpt/web-platform-tests/tools/third_party/pluggy/.travis.yml
new file mode 100644
index 00000000000..6fd136baf37
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/.travis.yml
@@ -0,0 +1,48 @@
+sudo: false
+language: python
+
+matrix:
+ include:
+ - python: '3.6'
+ env: TOXENV=check
+ - python: '3.6'
+ env: TOXENV=docs
+ - python: '2.7'
+ env: TOXENV=py27-pytestrelease
+ - python: '3.4'
+ env: TOXENV=py34-pytestrelease
+ - python: '3.5'
+ env: TOXENV=py35-pytestrelease
+ - python: '3.6'
+ env: TOXENV=py36-pytestrelease
+ - python: 'pypy'
+ env: TOXENV=pypy-pytestrelease
+ - python: 'nightly'
+ env: TOXENV=py37-pytestrelease
+ - python: '2.7'
+ env: TOXENV=py27-pytestmaster
+ - python: '2.7'
+ env: TOXENV=py27-pytestfeatures
+ - python: '3.6'
+ env: TOXENV=py36-pytestmaster
+ - python: '3.6'
+ env: TOXENV=py36-pytestfeatures
+ - python: '3.6'
+ env: TOXENV=benchmark
+
+install:
+ - pip install -U setuptools pip
+ - pip install -U tox
+
+script:
+ - tox
+
+notifications:
+ irc:
+ channels:
+ - "chat.freenode.net#pytest"
+ on_success: change
+ on_failure: change
+ skip_join: true
+# email:
+# - pytest-commit@python.org
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/CHANGELOG.rst b/tests/wpt/web-platform-tests/tools/third_party/pluggy/CHANGELOG.rst
new file mode 100644
index 00000000000..91bf53fdccb
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/CHANGELOG.rst
@@ -0,0 +1,152 @@
+0.6.0
+-----
+- Add CI testing for the features, release, and master
+ branches of ``pytest`` (PR `#79`_).
+- Document public API for ``_Result`` objects passed to wrappers
+ (PR `#85`_).
+- Document and test hook LIFO ordering (PR `#85`_).
+- Turn warnings into errors in test suite (PR `#89`_).
+- Deprecate ``_Result.result`` (PR `#88`_).
+- Convert ``_Multicall`` to a simple function distinguishing it from
+ the legacy version (PR `#90`_).
+- Resolve E741 errors (PR `#96`_).
+- Test and bug fix for unmarked hook collection (PRs `#97`_ and
+ `#102`_).
+- Drop support for EOL Python 2.6 and 3.3 (PR `#103`_).
+- Fix ``inspect`` based arg introspection on py3.6 (PR `#94`_).
+
+.. _#79: https://github.com/pytest-dev/pluggy/pull/79
+.. _#85: https://github.com/pytest-dev/pluggy/pull/85
+.. _#88: https://github.com/pytest-dev/pluggy/pull/88
+.. _#89: https://github.com/pytest-dev/pluggy/pull/89
+.. _#90: https://github.com/pytest-dev/pluggy/pull/90
+.. _#94: https://github.com/pytest-dev/pluggy/pull/94
+.. _#96: https://github.com/pytest-dev/pluggy/pull/96
+.. _#97: https://github.com/pytest-dev/pluggy/pull/97
+.. _#102: https://github.com/pytest-dev/pluggy/pull/102
+.. _#103: https://github.com/pytest-dev/pluggy/pull/103
+
+
+0.5.2
+-----
+- fix bug where ``firstresult`` wrappers were being sent an incorrectly configured
+ ``_Result`` (a list was set instead of a single value). Add tests to check for
+ this as well as ``_Result.force_result()`` behaviour. Thanks to `@tgoodlet`_
+ for the PR `#72`_.
+
+- fix incorrect ``getattr`` of ``DeprecationWarning`` from the ``warnings``
+ module. Thanks to `@nicoddemus`_ for the PR `#77`_.
+
+- hide ``pytest`` tracebacks in certain core routines. Thanks to
+ `@nicoddemus`_ for the PR `#80`_.
+
+.. _#72: https://github.com/pytest-dev/pluggy/pull/72
+.. _#77: https://github.com/pytest-dev/pluggy/pull/77
+.. _#80: https://github.com/pytest-dev/pluggy/pull/80
+
+
+0.5.1
+-----
+- fix a bug and add tests for case where ``firstresult`` hooks return
+ ``None`` results. Thanks to `@RonnyPfannschmidt`_ and `@tgoodlet`_
+ for the issue (`#68`_) and PR (`#69`_) respectively.
+
+.. _#69: https://github.com/pytest-dev/pluggy/pull/69
+.. _#68: https://github.com/pytest-dev/pluggy/issuses/68
+
+
+0.5.0
+-----
+- fix bug where callbacks for historic hooks would not be called for
+ already registered plugins. Thanks `@vodik`_ for the PR
+ and `@hpk42`_ for further fixes.
+
+- fix `#17`_ by considering only actual functions for hooks
+ this removes the ability to register arbitrary callable objects
+ which at first glance is a reasonable simplification,
+ thanks `@RonnyPfannschmidt`_ for report and pr.
+
+- fix `#19`_: allow registering hookspecs from instances. The PR from
+ `@tgoodlet`_ also modernized the varnames implementation.
+
+- resolve `#32`_: split up the test set into multiple modules.
+ Thanks to `@RonnyPfannschmidt`_ for the PR and `@tgoodlet`_ for
+ the initial request.
+
+- resolve `#14`_: add full sphinx docs. Thanks to `@tgoodlet`_ for
+ PR `#39`_.
+
+- add hook call mismatch warnings. Thanks to `@tgoodlet`_ for the
+ PR `#42`_.
+
+- resolve `#44`_: move to new-style classes. Thanks to `@MichalTHEDUDE`_
+ for PR `#46`_.
+
+- add baseline benchmarking/speed tests using ``pytest-benchmark``
+ in PR `#54`_. Thanks to `@tgoodlet`_.
+
+- update the README to showcase the API. Thanks to `@tgoodlet`_ for the
+ issue and PR `#55`_.
+
+- deprecate ``__multicall__`` and add a faster call loop implementation.
+ Thanks to `@tgoodlet`_ for PR `#58`_.
+
+- raise a comprehensible error when a ``hookimpl`` is called with positional
+ args. Thanks to `@RonnyPfannschmidt`_ for the issue and `@tgoodlet`_ for
+ PR `#60`_.
+
+- fix the ``firstresult`` test making it more complete
+ and remove a duplicate of that test. Thanks to `@tgoodlet`_
+ for PR `#62`_.
+
+.. _#62: https://github.com/pytest-dev/pluggy/pull/62
+.. _#60: https://github.com/pytest-dev/pluggy/pull/60
+.. _#58: https://github.com/pytest-dev/pluggy/pull/58
+.. _#55: https://github.com/pytest-dev/pluggy/pull/55
+.. _#54: https://github.com/pytest-dev/pluggy/pull/54
+.. _#46: https://github.com/pytest-dev/pluggy/pull/46
+.. _#44: https://github.com/pytest-dev/pluggy/issues/44
+.. _#42: https://github.com/pytest-dev/pluggy/pull/42
+.. _#39: https://github.com/pytest-dev/pluggy/pull/39
+.. _#32: https://github.com/pytest-dev/pluggy/pull/32
+.. _#19: https://github.com/pytest-dev/pluggy/issues/19
+.. _#17: https://github.com/pytest-dev/pluggy/issues/17
+.. _#14: https://github.com/pytest-dev/pluggy/issues/14
+
+
+0.4.0
+-----
+- add ``has_plugin(name)`` method to pluginmanager. thanks `@nicoddemus`_.
+
+- fix `#11`_: make plugin parsing more resilient against exceptions
+ from ``__getattr__`` functions. Thanks `@nicoddemus`_.
+
+- fix issue `#4`_: specific ``HookCallError`` exception for when a hook call
+ provides not enough arguments.
+
+- better error message when loading setuptools entrypoints fails
+ due to a ``VersionConflict``. Thanks `@blueyed`_.
+
+.. _#11: https://github.com/pytest-dev/pluggy/issues/11
+.. _#4: https://github.com/pytest-dev/pluggy/issues/4
+
+
+0.3.1
+-----
+- avoid using deprecated-in-python3.5 getargspec method. Thanks
+ `@mdboom`_.
+
+
+0.3.0
+-----
+initial release
+
+.. contributors
+.. _@hpk42: https://github.com/hpk42
+.. _@tgoodlet: https://github.com/tgoodlet
+.. _@MichalTHEDUDE: https://github.com/MichalTHEDUDE
+.. _@vodik: https://github.com/vodik
+.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
+.. _@blueyed: https://github.com/blueyed
+.. _@nicoddemus: https://github.com/nicoddemus
+.. _@mdboom: https://github.com/mdboom
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/LICENSE b/tests/wpt/web-platform-tests/tools/third_party/pluggy/LICENSE
new file mode 100644
index 00000000000..121017d0866
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 holger krekel (rather uses bitbucket/hpk42)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/MANIFEST.in b/tests/wpt/web-platform-tests/tools/third_party/pluggy/MANIFEST.in
new file mode 100644
index 00000000000..0cf8f3e0887
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/MANIFEST.in
@@ -0,0 +1,7 @@
+include CHANGELOG
+include README.rst
+include setup.py
+include tox.ini
+include LICENSE
+graft testing
+recursive-exclude * *.pyc *.pyo
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/README.rst b/tests/wpt/web-platform-tests/tools/third_party/pluggy/README.rst
new file mode 100644
index 00000000000..3636b6ec642
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/README.rst
@@ -0,0 +1,80 @@
+pluggy - A minimalist production ready plugin system
+====================================================
+|pypi| |anaconda| |versions| |travis| |appveyor|
+
+
+This is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.
+
+Please `read the docs`_ to learn more!
+
+A definitive example
+********************
+.. code-block:: python
+
+ import pluggy
+
+ hookspec = pluggy.HookspecMarker("myproject")
+ hookimpl = pluggy.HookimplMarker("myproject")
+
+
+ class MySpec(object):
+ """A hook specification namespace.
+ """
+ @hookspec
+ def myhook(self, arg1, arg2):
+ """My special little hook that you can customize.
+ """
+
+
+ class Plugin_1(object):
+ """A hook implementation namespace.
+ """
+ @hookimpl
+ def myhook(self, arg1, arg2):
+ print("inside Plugin_1.myhook()")
+ return arg1 + arg2
+
+
+ class Plugin_2(object):
+ """A 2nd hook implementation namespace.
+ """
+ @hookimpl
+ def myhook(self, arg1, arg2):
+ print("inside Plugin_2.myhook()")
+ return arg1 - arg2
+
+
+ # create a manager and add the spec
+ pm = pluggy.PluginManager("myproject")
+ pm.add_hookspecs(MySpec)
+
+ # register plugins
+ pm.register(Plugin_1())
+ pm.register(Plugin_2())
+
+ # call our `myhook` hook
+ results = pm.hook.myhook(arg1=1, arg2=2)
+ print(results)
+
+
+.. badges
+.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg
+ :target: https://pypi.python.org/pypi/pluggy
+.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg
+ :target: https://pypi.python.org/pypi/pluggy
+.. |travis| image:: https://img.shields.io/travis/pytest-dev/pluggy/master.svg
+ :target: https://travis-ci.org/pytest-dev/pluggy
+.. |appveyor| image:: https://img.shields.io/appveyor/ci/pytestbot/pluggy/master.svg
+ :target: https://ci.appveyor.com/project/pytestbot/pluggy
+.. |anaconda| image:: https://anaconda.org/conda-forge/pluggy/badges/version.svg
+ :target: https://anaconda.org/conda-forge/pluggy
+
+.. links
+.. _pytest:
+ http://pytest.org
+.. _tox:
+ https://tox.readthedocs.org
+.. _devpi:
+ http://doc.devpi.net
+.. _read the docs:
+ https://pluggy.readthedocs.io/en/latest/
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/appveyor.yml b/tests/wpt/web-platform-tests/tools/third_party/pluggy/appveyor.yml
new file mode 100644
index 00000000000..c3903f8bfcc
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/appveyor.yml
@@ -0,0 +1,34 @@
+environment:
+ matrix:
+ # note: please use "tox --listenvs" to populate the build matrix below
+ - TOXENV: "check"
+ - TOXENV: "docs"
+ - TOXENV: "py27-pytestrelease"
+ - TOXENV: "py34-pytestrelease"
+ - TOXENV: "py35-pytestrelease"
+ - TOXENV: "py36-pytestrelease"
+ - TOXENV: "pypy-pytestrelease"
+ - TOXENV: "py27-pytestmaster"
+ - TOXENV: "py27-pytestfeatures"
+ - TOXENV: "py36-pytestmaster"
+ - TOXENV: "py36-pytestfeatures"
+
+install:
+ - echo Installed Pythons
+ - dir c:\Python*
+
+ # install pypy using choco (redirect to a file and write to console in case
+ # choco install returns non-zero, because choco install python.pypy is too
+ # noisy)
+ # pypy is disabled until #1963 gets fixed
+ - choco install python.pypy > pypy-inst.log 2>&1 || (type pypy-inst.log & exit /b 1)
+ - set PATH=C:\tools\pypy\pypy;%PATH% # so tox can find pypy
+ - echo PyPy installed
+ - pypy --version
+
+ - C:\Python35\python -m pip install tox
+
+build: false # Not a C# project, build stuff at the test step instead.
+
+test_script:
+ - C:\Python35\python -m tox
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/_static/img/plug.png b/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/_static/img/plug.png
new file mode 100644
index 00000000000..3339f8a608d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/_static/img/plug.png
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/api_reference.rst b/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/api_reference.rst
new file mode 100644
index 00000000000..aa15135ad3d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/api_reference.rst
@@ -0,0 +1,14 @@
+Api Reference
+=============
+
+.. automodule:: pluggy
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
+.. automethod:: pluggy._Result.get_result
+
+.. automethod:: pluggy._Result.force_result
+
+.. automethod:: pluggy._HookCaller.call_extra
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/conf.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/conf.py
new file mode 100644
index 00000000000..04065218fda
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/conf.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+import pkg_resources
+
+
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.intersphinx',
+ 'sphinx.ext.coverage',
+ 'sphinx.ext.viewcode',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+
+dist = pkg_resources.get_distribution('pluggy')
+project = dist.project_name
+copyright = u'2016, Holger Krekel'
+author = 'Holger Krekel'
+
+release = dist.version
+# The short X.Y version.
+version = u'.'.join(dist.version.split('.')[:2])
+
+
+language = None
+
+pygments_style = 'sphinx'
+html_logo = '_static/img/plug.png'
+html_theme = 'alabaster'
+html_theme_options = {
+ # 'logo': 'img/plug.png',
+ # 'logo_name': 'true',
+ 'description': 'The `pytest` plugin system',
+ 'github_user': 'pytest-dev',
+ 'github_repo': 'pluggy',
+ 'github_button': 'true',
+ 'github_banner': 'true',
+ 'page_width': '1080px',
+ 'fixed_sidebar': 'false',
+}
+html_static_path = ['_static']
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'pluggy', u'pluggy Documentation',
+ [author], 1)
+]
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'pluggy', u'pluggy Documentation',
+ author, 'pluggy', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'https://docs.python.org/': None}
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/examples/firstexample.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/examples/firstexample.py
new file mode 100644
index 00000000000..3cec7cd2144
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/examples/firstexample.py
@@ -0,0 +1,44 @@
+import pluggy
+
+hookspec = pluggy.HookspecMarker("myproject")
+hookimpl = pluggy.HookimplMarker("myproject")
+
+
+class MySpec(object):
+ """A hook specification namespace.
+ """
+ @hookspec
+ def myhook(self, arg1, arg2):
+ """My special little hook that you can customize.
+ """
+
+
+class Plugin_1(object):
+ """A hook implementation namespace.
+ """
+ @hookimpl
+ def myhook(self, arg1, arg2):
+ print("inside Plugin_1.myhook()")
+ return arg1 + arg2
+
+
+class Plugin_2(object):
+ """A 2nd hook implementation namespace.
+ """
+ @hookimpl
+ def myhook(self, arg1, arg2):
+ print("inside Plugin_2.myhook()")
+ return arg1 - arg2
+
+
+# create a manager and add the spec
+pm = pluggy.PluginManager("myproject")
+pm.add_hookspecs(MySpec)
+
+# register plugins
+pm.register(Plugin_1())
+pm.register(Plugin_2())
+
+# call our `myhook` hook
+results = pm.hook.myhook(arg1=1, arg2=2)
+print(results)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/index.rst b/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/index.rst
new file mode 100644
index 00000000000..db65a1023fd
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/docs/index.rst
@@ -0,0 +1,705 @@
+``pluggy``
+==========
+
+The ``pytest`` plugin system
+****************************
+``pluggy`` is the crystallized core of `plugin management and hook
+calling`_ for `pytest`_.
+
+In fact, ``pytest`` is itself composed as a set of ``pluggy`` plugins
+which are invoked in sequence according to a well defined set of protocols.
+Some `200+ plugins`_ use ``pluggy`` to extend and customize ``pytest``'s default behaviour.
+
+In essence, ``pluggy`` enables function `hooking`_ so you can build "pluggable" systems.
+
+How's it work?
+--------------
+A `plugin` is a `namespace`_ which defines hook functions.
+
+``pluggy`` manages *plugins* by relying on:
+
+- a hook *specification* - defines a call signature
+- a set of hook *implementations* - aka `callbacks`_
+- the hook *caller* - a call loop which collects results
+
+where for each registered hook *specification*, a hook *call* will invoke up to ``N``
+registered hook *implementations*.
+
+``pluggy`` accomplishes all this by implementing a `request-response pattern`_ using *function*
+subscriptions and can be thought of and used as a rudimentary busless `publish-subscribe`_
+event system.
+
+``pluggy``'s approach is meant to let a designer think carefuly about which objects are
+explicitly needed by an extension writer. This is in contrast to subclass-based extension
+systems which may expose unecessary state and behaviour or encourage `tight coupling`_
+in overlying frameworks.
+
+
+A first example
+---------------
+
+.. literalinclude:: examples/firstexample.py
+
+Running this directly gets us::
+
+ $ python docs/examples/firstexample.py
+
+ inside Plugin_2.myhook()
+ inside Plugin_1.myhook()
+ [-1, 3]
+
+For more details and advanced usage please read on.
+
+.. _define:
+
+Defining and Collecting Hooks
+*****************************
+A *plugin* is a namespace type (currently one of a ``class`` or module)
+which defines a set of *hook* functions.
+
+As mentioned in :ref:`manage`, all *plugins* which define *hooks*
+are managed by an instance of a :py:class:`pluggy.PluginManager` which
+defines the primary ``pluggy`` API.
+
+In order for a ``PluginManager`` to detect functions in a namespace
+intended to be *hooks*, they must be decorated using special ``pluggy`` *marks*.
+
+.. _marking_hooks:
+
+Marking hooks
+-------------
+The :py:class:`~pluggy.HookspecMarker` and :py:class:`~pluggy.HookimplMarker`
+decorators are used to *mark* functions for detection by a ``PluginManager``:
+
+.. code-block:: python
+
+ from pluggy import HookspecMarker, HookimplMarker
+
+ hookspec = HookspecMarker('project_name')
+ hookimpl = HookimplMarker('project_name')
+
+
+Each decorator type takes a single ``project_name`` string as its
+lone argument the value of which is used to mark hooks for detection by
+by a similarly configured ``PluginManager`` instance.
+
+That is, a *mark* type called with ``project_name`` returns an object which
+can be used to decorate functions which will then be detected by a
+``PluginManager`` which was instantiated with the the same ``project_name``
+value.
+
+Furthermore, each *hookimpl* or *hookspec* decorator can configure the
+underlying call-time behavior of each *hook* object by providing special
+*options* passed as keyword arguments.
+
+
+.. note::
+ The following sections correspond to similar documentation in
+ ``pytest`` for `Writing hook functions`_ and can be used
+ as a supplementary resource.
+
+.. _impls:
+
+Implementations
+---------------
+A hook *implementation* (*hookimpl*) is just a (callback) function
+which has been appropriately marked.
+
+*hookimpls* are loaded from a plugin using the
+:py:meth:`~pluggy.PluginManager.register()` method:
+
+.. code-block:: python
+
+ import sys
+ from pluggy import PluginManager, HookimplMarker
+
+ hookimpl = HookimplMarker('myproject')
+
+ @hookimpl
+ def setup_project(config, args):
+ """This hook is used to process the initial config
+ and possibly input arguments.
+ """
+ if args:
+ config.process_args(args)
+
+ return config
+
+ pm = PluginManager('myproject')
+
+ # load all hookimpls from the local module's namespace
+ plugin_name = pm.register(sys.modules[__name__])
+
+.. _optionalhook:
+
+Optional validation
+^^^^^^^^^^^^^^^^^^^
+Normally each *hookimpl* should be validated a against a corresponding
+hook :ref:`specification <specs>`. If you want to make an exception
+then the *hookimpl* should be marked with the ``"optionalhook"`` option:
+
+.. code-block:: python
+
+ @hookimpl(optionalhook=True)
+ def setup_project(config, args):
+ """This hook is used to process the initial config
+ and possibly input arguments.
+ """
+ if args:
+ config.process_args(args)
+
+ return config
+
+Call time order
+^^^^^^^^^^^^^^^
+By default hooks are :ref:`called <calling>` in LIFO registered order, however,
+a *hookimpl* can influence its call-time invocation position using special
+attributes. If marked with a ``"tryfirst"`` or ``"trylast"`` option it
+will be executed *first* or *last* respectively in the hook call loop:
+
+.. code-block:: python
+
+ import sys
+ from pluggy import PluginManager, HookimplMarker
+
+ hookimpl = HookimplMarker('myproject')
+
+ @hookimpl(trylast=True)
+ def setup_project(config, args):
+ """Default implementation.
+ """
+ if args:
+ config.process_args(args)
+
+ return config
+
+
+ class SomeOtherPlugin(object):
+ """Some other plugin defining the same hook.
+ """
+ @hookimpl(tryfirst=True)
+ def setup_project(config, args):
+ """Report what args were passed before calling
+ downstream hooks.
+ """
+ if args:
+ print("Got args: {}".format(args))
+
+ return config
+
+ pm = PluginManager('myproject')
+
+ # load from the local module's namespace
+ pm.register(sys.modules[__name__])
+ # load a plugin defined on a class
+ pm.register(SomePlugin())
+
+For another example see the `hook function ordering`_ section of the
+``pytest`` docs.
+
+.. note::
+ ``tryfirst`` and ``trylast`` hooks are still invoked in LIFO order within
+ each category.
+
+Wrappers
+^^^^^^^^
+A *hookimpl* can be marked with a ``"hookwrapper"`` option which indicates that
+the function will be called to *wrap* (or surround) all other normal *hookimpl*
+calls. A *hookwrapper* can thus execute some code ahead and after the execution
+of all corresponding non-wrappper *hookimpls*.
+
+Much in the same way as a `@contextlib.contextmanager`_, *hookwrappers* must
+be implemented as generator function with a single ``yield`` in its body:
+
+
+.. code-block:: python
+
+ @hookimpl(hookwrapper=True)
+ def setup_project(config, args):
+ """Wrap calls to ``setup_project()`` implementations which
+ should return json encoded config options.
+ """
+ if config.debug:
+ print("Pre-hook config is {}".format(
+ config.tojson()))
+
+ # get initial default config
+ defaults = config.tojson()
+
+ # all corresponding hookimpls are invoked here
+ outcome = yield
+
+ for item in outcome.get_result():
+ print("JSON config override is {}".format(item))
+
+ if config.debug:
+ print("Post-hook config is {}".format(
+ config.tojson()))
+
+ if config.use_defaults:
+ outcome.force_result(defaults)
+
+The generator is `sent`_ a :py:class:`pluggy._Result` object which can
+be assigned in the ``yield`` expression and used to override or inspect
+the final result(s) returned back to the caller using the
+:py:meth:`~pluggy._Result.force_result` or
+:py:meth:`~pluggy._Result.get_result` methods.
+
+.. note::
+ Hook wrappers can **not** return results (as per generator function
+ semantics); they can only modify them using the ``_Result`` API.
+
+Also see the `hookwrapper`_ section in the ``pytest`` docs.
+
+.. _specs:
+
+Specifications
+--------------
+A hook *specification* (*hookspec*) is a definition used to validate each
+*hookimpl* ensuring that an extension writer has correctly defined their
+callback function *implementation* .
+
+*hookspecs* are defined using similarly marked functions however only the
+function *signature* (its name and names of all its arguments) is analyzed
+and stored. As such, often you will see a *hookspec* defined with only
+a docstring in its body.
+
+*hookspecs* are loaded using the
+:py:meth:`~pluggy.PluginManager.add_hookspecs()` method and normally
+should be added before registering corresponding *hookimpls*:
+
+.. code-block:: python
+
+ import sys
+ from pluggy import PluginManager, HookspecMarker
+
+ hookspec = HookspecMarker('myproject')
+
+ @hookspec
+ def setup_project(config, args):
+ """This hook is used to process the inital config and input
+ arguments.
+ """
+
+ pm = PluginManager('myproject')
+
+ # load from the local module's namespace
+ pm.add_hookspecs(sys.modules[__name__])
+
+
+Registering a *hookimpl* which does not meet the constraints of its
+corresponding *hookspec* will result in an error.
+
+A *hookspec* can also be added **after** some *hookimpls* have been
+registered however this is not normally recommended as it results in
+delayed hook validation.
+
+.. note::
+ The term *hookspec* can sometimes refer to the plugin-namespace
+ which defines ``hookspec`` decorated functions as in the case of
+ ``pytest``'s `hookspec module`_
+
+Enforcing spec validation
+^^^^^^^^^^^^^^^^^^^^^^^^^
+By default there is no strict requirement that each *hookimpl* has
+a corresponding *hookspec*. However, if you'd like you enforce this
+behavior you can run a check with the
+:py:meth:`~pluggy.PluginManager.check_pending()` method. If you'd like
+to enforce requisite *hookspecs* but with certain exceptions for some hooks
+then make sure to mark those hooks as :ref:`optional <optionalhook>`.
+
+Opt-in arguments
+^^^^^^^^^^^^^^^^
+To allow for *hookspecs* to evolve over the lifetime of a project,
+*hookimpls* can accept **less** arguments then defined in the spec.
+This allows for extending hook arguments (and thus semantics) without
+breaking existing *hookimpls*.
+
+In other words this is ok:
+
+.. code-block:: python
+
+ @hookspec
+ def myhook(config, args):
+ pass
+
+ @hookimpl
+ def myhook(args):
+ print(args)
+
+
+whereas this is not:
+
+.. code-block:: python
+
+ @hookspec
+ def myhook(config, args):
+ pass
+
+ @hookimpl
+ def myhook(config, args, extra_arg):
+ print(args)
+
+.. _firstresult:
+
+First result only
+^^^^^^^^^^^^^^^^^
+A *hookspec* can be marked such that when the *hook* is called the call loop
+will only invoke up to the first *hookimpl* which returns a result other
+then ``None``.
+
+.. code-block:: python
+
+ @hookspec(firstresult=True)
+ def myhook(config, args):
+ pass
+
+This can be useful for optimizing a call loop for which you are only
+interested in a single core *hookimpl*. An example is the
+`pytest_cmdline_main`_ central routine of ``pytest``.
+
+Also see the `first result`_ section in the ``pytest`` docs.
+
+.. _historic:
+
+Historic hooks
+^^^^^^^^^^^^^^
+You can mark a *hookspec* as being *historic* meaning that the hook
+can be called with :py:meth:`~pluggy.PluginManager.call_historic()` **before**
+having been registered:
+
+.. code-block:: python
+
+ @hookspec(historic=True)
+ def myhook(config, args):
+ pass
+
+The implication is that late registered *hookimpls* will be called back
+immediately at register time and **can not** return a result to the caller.**
+
+This turns out to be particularly useful when dealing with lazy or
+dynamically loaded plugins.
+
+For more info see :ref:`call_historic`.
+
+
+.. links
+.. _@contextlib.contextmanager:
+ https://docs.python.org/3.6/library/contextlib.html#contextlib.contextmanager
+.. _pytest_cmdline_main:
+ https://github.com/pytest-dev/pytest/blob/master/_pytest/hookspec.py#L80
+.. _hookspec module:
+ https://github.com/pytest-dev/pytest/blob/master/_pytest/hookspec.py
+.. _Writing hook functions:
+ http://doc.pytest.org/en/latest/writing_plugins.html#writing-hook-functions
+.. _hookwrapper:
+ http://doc.pytest.org/en/latest/writing_plugins.html#hookwrapper-executing-around-other-hooks
+.. _hook function ordering:
+ http://doc.pytest.org/en/latest/writing_plugins.html#hook-function-ordering-call-example
+.. _first result:
+ http://doc.pytest.org/en/latest/writing_plugins.html#firstresult-stop-at-first-non-none-result
+.. _sent:
+ https://docs.python.org/3/reference/expressions.html#generator.send
+
+.. _manage:
+
+The Plugin Registry
+*******************
+``pluggy`` manages plugins using instances of the
+:py:class:`pluggy.PluginManager`.
+
+A ``PluginManager`` is instantiated with a single
+``str`` argument, the ``project_name``:
+
+.. code-block:: python
+
+ import pluggy
+ pm = pluggy.PluginManager('my_project_name')
+
+
+The ``project_name`` value is used when a ``PluginManager`` scans for *hook*
+functions :ref:`defined on a plugin <define>`.
+This allows for multiple
+plugin managers from multiple projects to define hooks alongside each other.
+
+
+Registration
+------------
+Each ``PluginManager`` maintains a *plugin* registry where each *plugin*
+contains a set of *hookimpl* definitions. Loading *hookimpl* and *hookspec*
+definitions to populate the registry is described in detail in the section on
+:ref:`define`.
+
+In summary, you pass a plugin namespace object to the
+:py:meth:`~pluggy.PluginManager.register()` and
+:py:meth:`~pluggy.PluginManager.add_hookspec()` methods to collect
+hook *implementations* and *specfications* from *plugin* namespaces respectively.
+
+You can unregister any *plugin*'s hooks using
+:py:meth:`~pluggy.PluginManager.unregister()` and check if a plugin is
+registered by passing its name to the
+:py:meth:`~pluggy.PluginManager.is_registered()` method.
+
+Loading ``setuptools`` entry points
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+You can automatically load plugins registered through `setuptools entry points`_
+with the :py:meth:`~pluggy.PluginManager.load_setuptools_entrypoints()`
+method.
+
+An example use of this is the `pytest entry point`_.
+
+
+Blocking
+--------
+You can block any plugin from being registered using
+:py:meth:`~pluggy.PluginManager.set_blocked()` and check if a given
+*plugin* is blocked by name using :py:meth:`~pluggy.PluginManager.is_blocked()`.
+
+
+Inspection
+----------
+You can use a variety of methods to inspect the both the registry
+and particular plugins in it:
+
+- :py:meth:`~pluggy.PluginManager.list_name_plugin()` -
+ return a list of name-plugin pairs
+- :py:meth:`~pluggy.PluginManager.get_plugins()` - retrieve all plugins
+- :py:meth:`~pluggy.PluginManager.get_canonical_name()`- get a *plugin*'s
+ canonical name (the name it was registered with)
+- :py:meth:`~pluggy.PluginManager.get_plugin()` - retrieve a plugin by its
+ canonical name
+
+
+Parsing mark options
+^^^^^^^^^^^^^^^^^^^^
+You can retrieve the *options* applied to a particular
+*hookspec* or *hookimpl* as per :ref:`marking_hooks` using the
+:py:meth:`~pluggy.PluginManager.parse_hookspec_opts()` and
+:py:meth:`~pluggy.PluginManager.parse_hookimpl_opts()` respectively.
+
+.. links
+.. _setuptools entry points:
+ http://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins
+.. _pytest entry point:
+ http://doc.pytest.org/en/latest/writing_plugins.html#setuptools-entry-points
+
+
+.. _calling:
+
+Calling Hooks
+*************
+The core functionality of ``pluggy`` enables an extension provider
+to override function calls made at certain points throughout a program.
+
+A particular *hook* is invoked by calling an instance of
+a :py:class:`pluggy._HookCaller` which in turn *loops* through the
+``1:N`` registered *hookimpls* and calls them in sequence.
+
+Every :py:class:`pluggy.PluginManager` has a ``hook`` attribute
+which is an instance of this :py:class:`pluggy._HookRelay`.
+The ``_HookRelay`` itself contains references (by hook name) to each
+registered *hookimpl*'s ``_HookCaller`` instance.
+
+More practically you call a *hook* like so:
+
+.. code-block:: python
+
+ import sys
+ import pluggy
+ import mypluginspec
+ import myplugin
+ from configuration import config
+
+ pm = pluggy.PluginManager("myproject")
+ pm.add_hookspecs(mypluginspec)
+ pm.register(myplugin)
+
+ # we invoke the _HookCaller and thus all underlying hookimpls
+ result_list = pm.hook.myhook(config=config, args=sys.argv)
+
+Note that you **must** call hooks using keyword `arguments`_ syntax!
+
+Hook implementations are called in LIFO registered order: *the last
+registered plugin's hooks are called first*. As an example, the below
+assertion should not error:
+
+.. code-block:: python
+
+ from pluggy import PluginManager, HookimplMarker
+
+ hookimpl = HookimplMarker('myproject')
+
+ class Plugin1(object):
+ def myhook(self, args):
+ """Default implementation.
+ """
+ return 1
+
+ class Plugin2(object):
+ def myhook(self, args):
+ """Default implementation.
+ """
+ return 2
+
+ class Plugin3(object):
+ def myhook(self, args):
+ """Default implementation.
+ """
+ return 3
+
+ pm = PluginManager('myproject')
+ pm.register(Plugin1())
+ pm.register(Plugin2())
+ pm.register(Plugin3())
+
+ assert pm.hook.myhook(args=()) == [3, 2, 1]
+
+Collecting results
+------------------
+By default calling a hook results in all underlying :ref:`hookimpls
+<impls>` functions to be invoked in sequence via a loop. Any function
+which returns a value other then a ``None`` result will have that result
+appended to a :py:class:`list` which is returned by the call.
+
+The only exception to this behaviour is if the hook has been marked to return
+its :ref:`firstresult` in which case only the first single value (which is not
+``None``) will be returned.
+
+.. _call_historic:
+
+Historic calls
+--------------
+A *historic call* allows for all newly registered functions to receive all hook
+calls that happened before their registration. The implication is that this is
+only useful if you expect that some *hookimpls* may be registered **after** the
+hook is initially invoked.
+
+Historic hooks must be :ref:`specially marked <historic>` and called
+using the :py:meth:`pluggy._HookCaller.call_historic()` method:
+
+.. code-block:: python
+
+ # call with history; no results returned
+ pm.hook.myhook.call_historic(config=config, args=sys.argv)
+
+ # ... more of our program ...
+
+ # late loading of some plugin
+ import mylateplugin
+
+ # historic call back is done here
+ pm.register(mylateplugin)
+
+Note that if you ``call_historic()`` the ``_HookCaller`` (and thus your
+calling code) can not receive results back from the underlying *hookimpl*
+functions.
+
+Calling with extras
+-------------------
+You can call a hook with temporarily participating *implementation* functions
+(that aren't in the registry) using the
+:py:meth:`pluggy._HookCaller.call_extra()` method.
+
+
+Calling with a subset of registered plugins
+-------------------------------------------
+You can make a call using a subset of plugins by asking the
+``PluginManager`` first for a ``_HookCaller`` with those plugins removed
+using the :py:meth:`pluggy.PluginManager.subset_hook_caller()` method.
+
+You then can use that ``_HookCaller`` to make normal, ``call_historic()``,
+or ``call_extra()`` calls as necessary.
+
+
+.. links
+.. _arguments:
+ https://docs.python.org/3/glossary.html#term-argument
+
+
+Built-in tracing
+****************
+``pluggy`` comes with some batteries included hook tracing for your
+debugging needs.
+
+
+Call tracing
+------------
+To enable tracing use the
+:py:meth:`pluggy.PluginManager.enable_tracing()` method which returns an
+undo function to disable the behaviour.
+
+.. code-block:: python
+
+ pm = PluginManager('myproject')
+ # magic line to set a writer function
+ pm.trace.root.setwriter(print)
+ undo = pm.enable_tracing()
+
+
+Call monitoring
+---------------
+Instead of using the built-in tracing mechanism you can also add your
+own ``before`` and ``after`` monitoring functions using
+:py:class:`pluggy.PluginManager.add_hookcall_monitoring()`.
+
+The expected signature and default implementations for these functions is:
+
+.. code-block:: python
+
+ def before(hook_name, methods, kwargs):
+ pass
+
+ def after(outcome, hook_name, methods, kwargs):
+ pass
+
+Public API
+**********
+Please see the :doc:`api_reference`.
+
+Development
+***********
+Great care must taken when hacking on ``pluggy`` since multiple mature
+projects rely on it. Our Github integrated CI process runs the full
+`tox test suite`_ on each commit so be sure your changes can run on
+all required `Python interpreters`_ and ``pytest`` versions.
+
+Release Policy
+**************
+Pluggy uses `Semantic Versioning`_. Breaking changes are only foreseen for
+Major releases (incremented X in "X.Y.Z"). If you want to use ``pluggy``
+in your project you should thus use a dependency restriction like
+``"pluggy>=0.1.0,<1.0"`` to avoid surprises.
+
+
+.. hyperlinks
+.. _pytest:
+ http://pytest.org
+.. _request-response pattern:
+ https://en.wikipedia.org/wiki/Request%E2%80%93response
+.. _publish-subscribe:
+ https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern
+.. _hooking:
+ https://en.wikipedia.org/wiki/Hooking
+.. _plugin management and hook calling:
+ http://doc.pytest.org/en/latest/writing_plugins.html
+.. _namespace:
+ https://docs.python.org/3.6/tutorial/classes.html#python-scopes-and-namespaces
+.. _callbacks:
+ https://en.wikipedia.org/wiki/Callback_(computer_programming)
+.. _tox test suite:
+ https://github.com/pytest-dev/pluggy/blob/master/tox.ini
+.. _Semantic Versioning:
+ http://semver.org/
+.. _tight coupling:
+ https://en.wikipedia.org/wiki/Coupling_%28computer_programming%29#Types_of_coupling
+.. _Python interpreters:
+ https://github.com/pytest-dev/pluggy/blob/master/tox.ini#L2
+.. _200+ plugins:
+ http://plugincompat.herokuapp.com/
+
+
+.. Indices and tables
+.. ==================
+.. * :ref:`genindex`
+.. * :ref:`modindex`
+.. * :ref:`search`
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/pluggy/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/pluggy/__init__.py
new file mode 100644
index 00000000000..ab5175d9566
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/pluggy/__init__.py
@@ -0,0 +1,684 @@
+import inspect
+import warnings
+from .callers import _multicall, HookCallError, _Result, _legacymulticall
+
+__version__ = '0.5.3.dev'
+
+__all__ = ["PluginManager", "PluginValidationError", "HookCallError",
+ "HookspecMarker", "HookimplMarker"]
+
+
+class PluginValidationError(Exception):
+ """ plugin failed validation. """
+
+
+class HookspecMarker(object):
+ """ Decorator helper class for marking functions as hook specifications.
+
+ You can instantiate it with a project_name to get a decorator.
+ Calling PluginManager.add_hookspecs later will discover all marked functions
+ if the PluginManager uses the same project_name.
+ """
+
+ def __init__(self, project_name):
+ self.project_name = project_name
+
+ def __call__(self, function=None, firstresult=False, historic=False):
+ """ if passed a function, directly sets attributes on the function
+ which will make it discoverable to add_hookspecs(). If passed no
+ function, returns a decorator which can be applied to a function
+ later using the attributes supplied.
+
+ If firstresult is True the 1:N hook call (N being the number of registered
+ hook implementation functions) will stop at I<=N when the I'th function
+ returns a non-None result.
+
+ If historic is True calls to a hook will be memorized and replayed
+ on later registered plugins.
+
+ """
+ def setattr_hookspec_opts(func):
+ if historic and firstresult:
+ raise ValueError("cannot have a historic firstresult hook")
+ setattr(func, self.project_name + "_spec",
+ dict(firstresult=firstresult, historic=historic))
+ return func
+
+ if function is not None:
+ return setattr_hookspec_opts(function)
+ else:
+ return setattr_hookspec_opts
+
+
+class HookimplMarker(object):
+ """ Decorator helper class for marking functions as hook implementations.
+
+ You can instantiate with a project_name to get a decorator.
+ Calling PluginManager.register later will discover all marked functions
+ if the PluginManager uses the same project_name.
+ """
+ def __init__(self, project_name):
+ self.project_name = project_name
+
+ def __call__(self, function=None, hookwrapper=False, optionalhook=False,
+ tryfirst=False, trylast=False):
+
+ """ if passed a function, directly sets attributes on the function
+ which will make it discoverable to register(). If passed no function,
+ returns a decorator which can be applied to a function later using
+ the attributes supplied.
+
+ If optionalhook is True a missing matching hook specification will not result
+ in an error (by default it is an error if no matching spec is found).
+
+ If tryfirst is True this hook implementation will run as early as possible
+ in the chain of N hook implementations for a specfication.
+
+ If trylast is True this hook implementation will run as late as possible
+ in the chain of N hook implementations.
+
+ If hookwrapper is True the hook implementations needs to execute exactly
+ one "yield". The code before the yield is run early before any non-hookwrapper
+ function is run. The code after the yield is run after all non-hookwrapper
+ function have run. The yield receives a ``_Result`` object representing
+ the exception or result outcome of the inner calls (including other hookwrapper
+ calls).
+
+ """
+ def setattr_hookimpl_opts(func):
+ setattr(func, self.project_name + "_impl",
+ dict(hookwrapper=hookwrapper, optionalhook=optionalhook,
+ tryfirst=tryfirst, trylast=trylast))
+ return func
+
+ if function is None:
+ return setattr_hookimpl_opts
+ else:
+ return setattr_hookimpl_opts(function)
+
+
+def normalize_hookimpl_opts(opts):
+ opts.setdefault("tryfirst", False)
+ opts.setdefault("trylast", False)
+ opts.setdefault("hookwrapper", False)
+ opts.setdefault("optionalhook", False)
+
+
+class _TagTracer(object):
+ def __init__(self):
+ self._tag2proc = {}
+ self.writer = None
+ self.indent = 0
+
+ def get(self, name):
+ return _TagTracerSub(self, (name,))
+
+ def format_message(self, tags, args):
+ if isinstance(args[-1], dict):
+ extra = args[-1]
+ args = args[:-1]
+ else:
+ extra = {}
+
+ content = " ".join(map(str, args))
+ indent = " " * self.indent
+
+ lines = [
+ "%s%s [%s]\n" % (indent, content, ":".join(tags))
+ ]
+
+ for name, value in extra.items():
+ lines.append("%s %s: %s\n" % (indent, name, value))
+ return lines
+
+ def processmessage(self, tags, args):
+ if self.writer is not None and args:
+ lines = self.format_message(tags, args)
+ self.writer(''.join(lines))
+ try:
+ self._tag2proc[tags](tags, args)
+ except KeyError:
+ pass
+
+ def setwriter(self, writer):
+ self.writer = writer
+
+ def setprocessor(self, tags, processor):
+ if isinstance(tags, str):
+ tags = tuple(tags.split(":"))
+ else:
+ assert isinstance(tags, tuple)
+ self._tag2proc[tags] = processor
+
+
+class _TagTracerSub(object):
+ def __init__(self, root, tags):
+ self.root = root
+ self.tags = tags
+
+ def __call__(self, *args):
+ self.root.processmessage(self.tags, args)
+
+ def setmyprocessor(self, processor):
+ self.root.setprocessor(self.tags, processor)
+
+ def get(self, name):
+ return self.__class__(self.root, self.tags + (name,))
+
+
+class _TracedHookExecution(object):
+ def __init__(self, pluginmanager, before, after):
+ self.pluginmanager = pluginmanager
+ self.before = before
+ self.after = after
+ self.oldcall = pluginmanager._inner_hookexec
+ assert not isinstance(self.oldcall, _TracedHookExecution)
+ self.pluginmanager._inner_hookexec = self
+
+ def __call__(self, hook, hook_impls, kwargs):
+ self.before(hook.name, hook_impls, kwargs)
+ outcome = _Result.from_call(lambda: self.oldcall(hook, hook_impls, kwargs))
+ self.after(outcome, hook.name, hook_impls, kwargs)
+ return outcome.get_result()
+
+ def undo(self):
+ self.pluginmanager._inner_hookexec = self.oldcall
+
+
+class PluginManager(object):
+ """ Core Pluginmanager class which manages registration
+ of plugin objects and 1:N hook calling.
+
+ You can register new hooks by calling ``add_hookspec(module_or_class)``.
+ You can register plugin objects (which contain hooks) by calling
+ ``register(plugin)``. The Pluginmanager is initialized with a
+ prefix that is searched for in the names of the dict of registered
+ plugin objects. An optional excludefunc allows to blacklist names which
+ are not considered as hooks despite a matching prefix.
+
+ For debugging purposes you can call ``enable_tracing()``
+ which will subsequently send debug information to the trace helper.
+ """
+
+ def __init__(self, project_name, implprefix=None):
+ """ if implprefix is given implementation functions
+ will be recognized if their name matches the implprefix. """
+ self.project_name = project_name
+ self._name2plugin = {}
+ self._plugin2hookcallers = {}
+ self._plugin_distinfo = []
+ self.trace = _TagTracer().get("pluginmanage")
+ self.hook = _HookRelay(self.trace.root.get("hook"))
+ self._implprefix = implprefix
+ self._inner_hookexec = lambda hook, methods, kwargs: \
+ hook.multicall(
+ methods, kwargs,
+ firstresult=hook.spec_opts.get('firstresult'),
+ )
+
+ def _hookexec(self, hook, methods, kwargs):
+ # called from all hookcaller instances.
+ # enable_tracing will set its own wrapping function at self._inner_hookexec
+ return self._inner_hookexec(hook, methods, kwargs)
+
+ def register(self, plugin, name=None):
+ """ Register a plugin and return its canonical name or None if the name
+ is blocked from registering. Raise a ValueError if the plugin is already
+ registered. """
+ plugin_name = name or self.get_canonical_name(plugin)
+
+ if plugin_name in self._name2plugin or plugin in self._plugin2hookcallers:
+ if self._name2plugin.get(plugin_name, -1) is None:
+ return # blocked plugin, return None to indicate no registration
+ raise ValueError("Plugin already registered: %s=%s\n%s" %
+ (plugin_name, plugin, self._name2plugin))
+
+ # XXX if an error happens we should make sure no state has been
+ # changed at point of return
+ self._name2plugin[plugin_name] = plugin
+
+ # register matching hook implementations of the plugin
+ self._plugin2hookcallers[plugin] = hookcallers = []
+ for name in dir(plugin):
+ hookimpl_opts = self.parse_hookimpl_opts(plugin, name)
+ if hookimpl_opts is not None:
+ normalize_hookimpl_opts(hookimpl_opts)
+ method = getattr(plugin, name)
+ hookimpl = HookImpl(plugin, plugin_name, method, hookimpl_opts)
+ hook = getattr(self.hook, name, None)
+ if hook is None:
+ hook = _HookCaller(name, self._hookexec)
+ setattr(self.hook, name, hook)
+ elif hook.has_spec():
+ self._verify_hook(hook, hookimpl)
+ hook._maybe_apply_history(hookimpl)
+ hook._add_hookimpl(hookimpl)
+ hookcallers.append(hook)
+ return plugin_name
+
+ def parse_hookimpl_opts(self, plugin, name):
+ method = getattr(plugin, name)
+ if not inspect.isroutine(method):
+ return
+ try:
+ res = getattr(method, self.project_name + "_impl", None)
+ except Exception:
+ res = {}
+ if res is not None and not isinstance(res, dict):
+ # false positive
+ res = None
+ elif res is None and self._implprefix and name.startswith(self._implprefix):
+ res = {}
+ return res
+
+ def unregister(self, plugin=None, name=None):
+ """ unregister a plugin object and all its contained hook implementations
+ from internal data structures. """
+ if name is None:
+ assert plugin is not None, "one of name or plugin needs to be specified"
+ name = self.get_name(plugin)
+
+ if plugin is None:
+ plugin = self.get_plugin(name)
+
+ # if self._name2plugin[name] == None registration was blocked: ignore
+ if self._name2plugin.get(name):
+ del self._name2plugin[name]
+
+ for hookcaller in self._plugin2hookcallers.pop(plugin, []):
+ hookcaller._remove_plugin(plugin)
+
+ return plugin
+
+ def set_blocked(self, name):
+ """ block registrations of the given name, unregister if already registered. """
+ self.unregister(name=name)
+ self._name2plugin[name] = None
+
+ def is_blocked(self, name):
+ """ return True if the name blogs registering plugins of that name. """
+ return name in self._name2plugin and self._name2plugin[name] is None
+
+ def add_hookspecs(self, module_or_class):
+ """ add new hook specifications defined in the given module_or_class.
+ Functions are recognized if they have been decorated accordingly. """
+ names = []
+ for name in dir(module_or_class):
+ spec_opts = self.parse_hookspec_opts(module_or_class, name)
+ if spec_opts is not None:
+ hc = getattr(self.hook, name, None)
+ if hc is None:
+ hc = _HookCaller(name, self._hookexec, module_or_class, spec_opts)
+ setattr(self.hook, name, hc)
+ else:
+ # plugins registered this hook without knowing the spec
+ hc.set_specification(module_or_class, spec_opts)
+ for hookfunction in (hc._wrappers + hc._nonwrappers):
+ self._verify_hook(hc, hookfunction)
+ names.append(name)
+
+ if not names:
+ raise ValueError("did not find any %r hooks in %r" %
+ (self.project_name, module_or_class))
+
+ def parse_hookspec_opts(self, module_or_class, name):
+ method = getattr(module_or_class, name)
+ return getattr(method, self.project_name + "_spec", None)
+
+ def get_plugins(self):
+ """ return the set of registered plugins. """
+ return set(self._plugin2hookcallers)
+
+ def is_registered(self, plugin):
+ """ Return True if the plugin is already registered. """
+ return plugin in self._plugin2hookcallers
+
+ def get_canonical_name(self, plugin):
+ """ Return canonical name for a plugin object. Note that a plugin
+ may be registered under a different name which was specified
+ by the caller of register(plugin, name). To obtain the name
+ of an registered plugin use ``get_name(plugin)`` instead."""
+ return getattr(plugin, "__name__", None) or str(id(plugin))
+
+ def get_plugin(self, name):
+ """ Return a plugin or None for the given name. """
+ return self._name2plugin.get(name)
+
+ def has_plugin(self, name):
+ """ Return True if a plugin with the given name is registered. """
+ return self.get_plugin(name) is not None
+
+ def get_name(self, plugin):
+ """ Return name for registered plugin or None if not registered. """
+ for name, val in self._name2plugin.items():
+ if plugin == val:
+ return name
+
+ def _verify_hook(self, hook, hookimpl):
+ if hook.is_historic() and hookimpl.hookwrapper:
+ raise PluginValidationError(
+ "Plugin %r\nhook %r\nhistoric incompatible to hookwrapper" %
+ (hookimpl.plugin_name, hook.name))
+
+ # positional arg checking
+ notinspec = set(hookimpl.argnames) - set(hook.argnames)
+ if notinspec:
+ raise PluginValidationError(
+ "Plugin %r for hook %r\nhookimpl definition: %s\n"
+ "Argument(s) %s are declared in the hookimpl but "
+ "can not be found in the hookspec" %
+ (hookimpl.plugin_name, hook.name,
+ _formatdef(hookimpl.function), notinspec)
+ )
+
+ def check_pending(self):
+ """ Verify that all hooks which have not been verified against
+ a hook specification are optional, otherwise raise PluginValidationError"""
+ for name in self.hook.__dict__:
+ if name[0] != "_":
+ hook = getattr(self.hook, name)
+ if not hook.has_spec():
+ for hookimpl in (hook._wrappers + hook._nonwrappers):
+ if not hookimpl.optionalhook:
+ raise PluginValidationError(
+ "unknown hook %r in plugin %r" %
+ (name, hookimpl.plugin))
+
+ def load_setuptools_entrypoints(self, entrypoint_name):
+ """ Load modules from querying the specified setuptools entrypoint name.
+ Return the number of loaded plugins. """
+ from pkg_resources import (iter_entry_points, DistributionNotFound,
+ VersionConflict)
+ for ep in iter_entry_points(entrypoint_name):
+ # is the plugin registered or blocked?
+ if self.get_plugin(ep.name) or self.is_blocked(ep.name):
+ continue
+ try:
+ plugin = ep.load()
+ except DistributionNotFound:
+ continue
+ except VersionConflict as e:
+ raise PluginValidationError(
+ "Plugin %r could not be loaded: %s!" % (ep.name, e))
+ self.register(plugin, name=ep.name)
+ self._plugin_distinfo.append((plugin, ep.dist))
+ return len(self._plugin_distinfo)
+
+ def list_plugin_distinfo(self):
+ """ return list of distinfo/plugin tuples for all setuptools registered
+ plugins. """
+ return list(self._plugin_distinfo)
+
+ def list_name_plugin(self):
+ """ return list of name/plugin pairs. """
+ return list(self._name2plugin.items())
+
+ def get_hookcallers(self, plugin):
+ """ get all hook callers for the specified plugin. """
+ return self._plugin2hookcallers.get(plugin)
+
+ def add_hookcall_monitoring(self, before, after):
+ """ add before/after tracing functions for all hooks
+ and return an undo function which, when called,
+ will remove the added tracers.
+
+ ``before(hook_name, hook_impls, kwargs)`` will be called ahead
+ of all hook calls and receive a hookcaller instance, a list
+ of HookImpl instances and the keyword arguments for the hook call.
+
+ ``after(outcome, hook_name, hook_impls, kwargs)`` receives the
+ same arguments as ``before`` but also a :py:class:`_Result`` object
+ which represents the result of the overall hook call.
+ """
+ return _TracedHookExecution(self, before, after).undo
+
+ def enable_tracing(self):
+ """ enable tracing of hook calls and return an undo function. """
+ hooktrace = self.hook._trace
+
+ def before(hook_name, methods, kwargs):
+ hooktrace.root.indent += 1
+ hooktrace(hook_name, kwargs)
+
+ def after(outcome, hook_name, methods, kwargs):
+ if outcome.excinfo is None:
+ hooktrace("finish", hook_name, "-->", outcome.get_result())
+ hooktrace.root.indent -= 1
+
+ return self.add_hookcall_monitoring(before, after)
+
+ def subset_hook_caller(self, name, remove_plugins):
+ """ Return a new _HookCaller instance for the named method
+ which manages calls to all registered plugins except the
+ ones from remove_plugins. """
+ orig = getattr(self.hook, name)
+ plugins_to_remove = [plug for plug in remove_plugins if hasattr(plug, name)]
+ if plugins_to_remove:
+ hc = _HookCaller(orig.name, orig._hookexec, orig._specmodule_or_class,
+ orig.spec_opts)
+ for hookimpl in (orig._wrappers + orig._nonwrappers):
+ plugin = hookimpl.plugin
+ if plugin not in plugins_to_remove:
+ hc._add_hookimpl(hookimpl)
+ # we also keep track of this hook caller so it
+ # gets properly removed on plugin unregistration
+ self._plugin2hookcallers.setdefault(plugin, []).append(hc)
+ return hc
+ return orig
+
+
+def varnames(func):
+ """Return tuple of positional and keywrord argument names for a function,
+ method, class or callable.
+
+ In case of a class, its ``__init__`` method is considered.
+ For methods the ``self`` parameter is not included.
+ """
+ cache = getattr(func, "__dict__", {})
+ try:
+ return cache["_varnames"]
+ except KeyError:
+ pass
+
+ if inspect.isclass(func):
+ try:
+ func = func.__init__
+ except AttributeError:
+ return (), ()
+ elif not inspect.isroutine(func): # callable object?
+ try:
+ func = getattr(func, '__call__', func)
+ except Exception:
+ return ()
+
+ try: # func MUST be a function or method here or we won't parse any args
+ spec = _getargspec(func)
+ except TypeError:
+ return (), ()
+
+ args, defaults = tuple(spec.args), spec.defaults
+ if defaults:
+ index = -len(defaults)
+ args, defaults = args[:index], tuple(args[index:])
+ else:
+ defaults = ()
+
+ # strip any implicit instance arg
+ if args:
+ if inspect.ismethod(func) or (
+ '.' in getattr(func, '__qualname__', ()) and args[0] == 'self'
+ ):
+ args = args[1:]
+
+ assert "self" not in args # best naming practises check?
+ try:
+ cache["_varnames"] = args, defaults
+ except TypeError:
+ pass
+ return args, defaults
+
+
+class _HookRelay(object):
+ """ hook holder object for performing 1:N hook calls where N is the number
+ of registered plugins.
+
+ """
+
+ def __init__(self, trace):
+ self._trace = trace
+
+
+class _HookCaller(object):
+ def __init__(self, name, hook_execute, specmodule_or_class=None,
+ spec_opts=None):
+ self.name = name
+ self._wrappers = []
+ self._nonwrappers = []
+ self._hookexec = hook_execute
+ self._specmodule_or_class = None
+ self.argnames = None
+ self.kwargnames = None
+ self.multicall = _multicall
+ self.spec_opts = spec_opts or {}
+ if specmodule_or_class is not None:
+ self.set_specification(specmodule_or_class, spec_opts)
+
+ def has_spec(self):
+ return self._specmodule_or_class is not None
+
+ def set_specification(self, specmodule_or_class, spec_opts):
+ assert not self.has_spec()
+ self._specmodule_or_class = specmodule_or_class
+ specfunc = getattr(specmodule_or_class, self.name)
+ # get spec arg signature
+ argnames, self.kwargnames = varnames(specfunc)
+ self.argnames = ["__multicall__"] + list(argnames)
+ self.spec_opts.update(spec_opts)
+ if spec_opts.get("historic"):
+ self._call_history = []
+
+ def is_historic(self):
+ return hasattr(self, "_call_history")
+
+ def _remove_plugin(self, plugin):
+ def remove(wrappers):
+ for i, method in enumerate(wrappers):
+ if method.plugin == plugin:
+ del wrappers[i]
+ return True
+ if remove(self._wrappers) is None:
+ if remove(self._nonwrappers) is None:
+ raise ValueError("plugin %r not found" % (plugin,))
+
+ def _add_hookimpl(self, hookimpl):
+ """A an implementation to the callback chain.
+ """
+ if hookimpl.hookwrapper:
+ methods = self._wrappers
+ else:
+ methods = self._nonwrappers
+
+ if hookimpl.trylast:
+ methods.insert(0, hookimpl)
+ elif hookimpl.tryfirst:
+ methods.append(hookimpl)
+ else:
+ # find last non-tryfirst method
+ i = len(methods) - 1
+ while i >= 0 and methods[i].tryfirst:
+ i -= 1
+ methods.insert(i + 1, hookimpl)
+
+ if '__multicall__' in hookimpl.argnames:
+ warnings.warn(
+ "Support for __multicall__ is now deprecated and will be"
+ "removed in an upcoming release.",
+ DeprecationWarning
+ )
+ self.multicall = _legacymulticall
+
+ def __repr__(self):
+ return "<_HookCaller %r>" % (self.name,)
+
+ def __call__(self, *args, **kwargs):
+ if args:
+ raise TypeError("hook calling supports only keyword arguments")
+ assert not self.is_historic()
+ if self.argnames:
+ notincall = set(self.argnames) - set(['__multicall__']) - set(
+ kwargs.keys())
+ if notincall:
+ warnings.warn(
+ "Argument(s) {} which are declared in the hookspec "
+ "can not be found in this hook call"
+ .format(tuple(notincall)),
+ stacklevel=2,
+ )
+ return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
+
+ def call_historic(self, proc=None, kwargs=None):
+ """ call the hook with given ``kwargs`` for all registered plugins and
+ for all plugins which will be registered afterwards.
+
+ If ``proc`` is not None it will be called for for each non-None result
+ obtained from a hook implementation.
+ """
+ self._call_history.append((kwargs or {}, proc))
+ # historizing hooks don't return results
+ res = self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
+ for x in res or []:
+ proc(x)
+
+ def call_extra(self, methods, kwargs):
+ """ Call the hook with some additional temporarily participating
+ methods using the specified kwargs as call parameters. """
+ old = list(self._nonwrappers), list(self._wrappers)
+ for method in methods:
+ opts = dict(hookwrapper=False, trylast=False, tryfirst=False)
+ hookimpl = HookImpl(None, "<temp>", method, opts)
+ self._add_hookimpl(hookimpl)
+ try:
+ return self(**kwargs)
+ finally:
+ self._nonwrappers, self._wrappers = old
+
+ def _maybe_apply_history(self, method):
+ """Apply call history to a new hookimpl if it is marked as historic.
+ """
+ if self.is_historic():
+ for kwargs, proc in self._call_history:
+ res = self._hookexec(self, [method], kwargs)
+ if res and proc is not None:
+ proc(res[0])
+
+
+class HookImpl(object):
+ def __init__(self, plugin, plugin_name, function, hook_impl_opts):
+ self.function = function
+ self.argnames, self.kwargnames = varnames(self.function)
+ self.plugin = plugin
+ self.opts = hook_impl_opts
+ self.plugin_name = plugin_name
+ self.__dict__.update(hook_impl_opts)
+
+
+if hasattr(inspect, 'getfullargspec'):
+ def _getargspec(func):
+ return inspect.getfullargspec(func)
+else:
+ def _getargspec(func):
+ return inspect.getargspec(func)
+
+
+if hasattr(inspect, 'signature'):
+ def _formatdef(func):
+ return "%s%s" % (
+ func.__name__,
+ str(inspect.signature(func))
+ )
+else:
+ def _formatdef(func):
+ return "%s%s" % (
+ func.__name__,
+ inspect.formatargspec(*inspect.getargspec(func))
+ )
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/pluggy/callers.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/pluggy/callers.py
new file mode 100644
index 00000000000..3ff67becffb
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/pluggy/callers.py
@@ -0,0 +1,201 @@
+'''
+Call loop machinery
+'''
+import sys
+import warnings
+
+_py3 = sys.version_info > (3, 0)
+
+
+if not _py3:
+ exec("""
+def _reraise(cls, val, tb):
+ raise cls, val, tb
+""")
+
+
+def _raise_wrapfail(wrap_controller, msg):
+ co = wrap_controller.gi_code
+ raise RuntimeError("wrap_controller at %r %s:%d %s" %
+ (co.co_name, co.co_filename, co.co_firstlineno, msg))
+
+
+class HookCallError(Exception):
+ """ Hook was called wrongly. """
+
+
+class _Result(object):
+ def __init__(self, result, excinfo):
+ self._result = result
+ self._excinfo = excinfo
+
+ @property
+ def excinfo(self):
+ return self._excinfo
+
+ @property
+ def result(self):
+ """Get the result(s) for this hook call (DEPRECATED in favor of ``get_result()``)."""
+ msg = 'Use get_result() which forces correct exception handling'
+ warnings.warn(DeprecationWarning(msg), stacklevel=2)
+ return self._result
+
+ @classmethod
+ def from_call(cls, func):
+ __tracebackhide__ = True
+ result = excinfo = None
+ try:
+ result = func()
+ except BaseException:
+ excinfo = sys.exc_info()
+
+ return cls(result, excinfo)
+
+ def force_result(self, result):
+ """Force the result(s) to ``result``.
+
+ If the hook was marked as a ``firstresult`` a single value should
+ be set otherwise set a (modified) list of results. Any exceptions
+ found during invocation will be deleted.
+ """
+ self._result = result
+ self._excinfo = None
+
+ def get_result(self):
+ """Get the result(s) for this hook call.
+
+ If the hook was marked as a ``firstresult`` only a single value
+ will be returned otherwise a list of results.
+ """
+ __tracebackhide__ = True
+ if self._excinfo is None:
+ return self._result
+ else:
+ ex = self._excinfo
+ if _py3:
+ raise ex[1].with_traceback(ex[2])
+ _reraise(*ex) # noqa
+
+
+def _wrapped_call(wrap_controller, func):
+ """ Wrap calling to a function with a generator which needs to yield
+ exactly once. The yield point will trigger calling the wrapped function
+ and return its ``_Result`` to the yield point. The generator then needs
+ to finish (raise StopIteration) in order for the wrapped call to complete.
+ """
+ try:
+ next(wrap_controller) # first yield
+ except StopIteration:
+ _raise_wrapfail(wrap_controller, "did not yield")
+ call_outcome = _Result.from_call(func)
+ try:
+ wrap_controller.send(call_outcome)
+ _raise_wrapfail(wrap_controller, "has second yield")
+ except StopIteration:
+ pass
+ return call_outcome.get_result()
+
+
+class _LegacyMultiCall(object):
+ """ execute a call into multiple python functions/methods. """
+
+ # XXX note that the __multicall__ argument is supported only
+ # for pytest compatibility reasons. It was never officially
+ # supported there and is explicitely deprecated since 2.8
+ # so we can remove it soon, allowing to avoid the below recursion
+ # in execute() and simplify/speed up the execute loop.
+
+ def __init__(self, hook_impls, kwargs, firstresult=False):
+ self.hook_impls = hook_impls
+ self.caller_kwargs = kwargs # come from _HookCaller.__call__()
+ self.caller_kwargs["__multicall__"] = self
+ self.firstresult = firstresult
+
+ def execute(self):
+ caller_kwargs = self.caller_kwargs
+ self.results = results = []
+ firstresult = self.firstresult
+
+ while self.hook_impls:
+ hook_impl = self.hook_impls.pop()
+ try:
+ args = [caller_kwargs[argname] for argname in hook_impl.argnames]
+ except KeyError:
+ for argname in hook_impl.argnames:
+ if argname not in caller_kwargs:
+ raise HookCallError(
+ "hook call must provide argument %r" % (argname,))
+ if hook_impl.hookwrapper:
+ return _wrapped_call(hook_impl.function(*args), self.execute)
+ res = hook_impl.function(*args)
+ if res is not None:
+ if firstresult:
+ return res
+ results.append(res)
+
+ if not firstresult:
+ return results
+
+ def __repr__(self):
+ status = "%d meths" % (len(self.hook_impls),)
+ if hasattr(self, "results"):
+ status = ("%d results, " % len(self.results)) + status
+ return "<_MultiCall %s, kwargs=%r>" % (status, self.caller_kwargs)
+
+
+def _legacymulticall(hook_impls, caller_kwargs, firstresult=False):
+ return _LegacyMultiCall(
+ hook_impls, caller_kwargs, firstresult=firstresult).execute()
+
+
+def _multicall(hook_impls, caller_kwargs, firstresult=False):
+ """Execute a call into multiple python functions/methods and return the
+ result(s).
+
+ ``caller_kwargs`` comes from _HookCaller.__call__().
+ """
+ __tracebackhide__ = True
+ results = []
+ excinfo = None
+ try: # run impl and wrapper setup functions in a loop
+ teardowns = []
+ try:
+ for hook_impl in reversed(hook_impls):
+ try:
+ args = [caller_kwargs[argname] for argname in hook_impl.argnames]
+ except KeyError:
+ for argname in hook_impl.argnames:
+ if argname not in caller_kwargs:
+ raise HookCallError(
+ "hook call must provide argument %r" % (argname,))
+
+ if hook_impl.hookwrapper:
+ try:
+ gen = hook_impl.function(*args)
+ next(gen) # first yield
+ teardowns.append(gen)
+ except StopIteration:
+ _raise_wrapfail(gen, "did not yield")
+ else:
+ res = hook_impl.function(*args)
+ if res is not None:
+ results.append(res)
+ if firstresult: # halt further impl calls
+ break
+ except BaseException:
+ excinfo = sys.exc_info()
+ finally:
+ if firstresult: # first result hooks return a single value
+ outcome = _Result(results[0] if results else None, excinfo)
+ else:
+ outcome = _Result(results, excinfo)
+
+ # run all wrapper post-yield blocks
+ for gen in reversed(teardowns):
+ try:
+ gen.send(outcome)
+ _raise_wrapfail(gen, "has second yield")
+ except StopIteration:
+ pass
+
+ return outcome.get_result()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/setup.cfg b/tests/wpt/web-platform-tests/tools/third_party/pluggy/setup.cfg
new file mode 100644
index 00000000000..7e729cb4f24
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/setup.cfg
@@ -0,0 +1,8 @@
+[bdist_wheel]
+universal=1
+
+[metadata]
+license_file=LICENSE
+
+[devpi:upload]
+formats=sdist.tgz,bdist_wheel
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/setup.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/setup.py
new file mode 100644
index 00000000000..b7c0f697120
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/setup.py
@@ -0,0 +1,51 @@
+import os
+from setuptools import setup
+
+classifiers = [
+ 'Development Status :: 4 - Beta',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: MIT License',
+ 'Operating System :: POSIX',
+ 'Operating System :: Microsoft :: Windows',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Topic :: Software Development :: Testing',
+ 'Topic :: Software Development :: Libraries',
+ 'Topic :: Utilities',
+ 'Programming Language :: Python :: Implementation :: CPython',
+ 'Programming Language :: Python :: Implementation :: PyPy'] + [
+ ('Programming Language :: Python :: %s' % x) for x in
+ '2 2.7 3 3.4 3.5 3.6'.split()]
+
+with open('README.rst') as fd:
+ long_description = fd.read()
+
+
+def get_version():
+ p = os.path.join(os.path.dirname(
+ os.path.abspath(__file__)), "pluggy/__init__.py")
+ with open(p) as f:
+ for line in f.readlines():
+ if "__version__" in line:
+ return line.strip().split("=")[-1].strip(" '")
+ raise ValueError("could not read version")
+
+
+def main():
+ setup(
+ name='pluggy',
+ description='plugin and hook calling mechanisms for python',
+ long_description=long_description,
+ version=get_version(),
+ license='MIT license',
+ platforms=['unix', 'linux', 'osx', 'win32'],
+ author='Holger Krekel',
+ author_email='holger@merlinux.eu',
+ url='https://github.com/pytest-dev/pluggy',
+ python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
+ classifiers=classifiers,
+ packages=['pluggy'],
+ )
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/benchmark.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/benchmark.py
new file mode 100644
index 00000000000..5a913e9d418
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/benchmark.py
@@ -0,0 +1,59 @@
+"""
+Benchmarking and performance tests.
+"""
+import pytest
+from pluggy import (_multicall, _legacymulticall, HookImpl, HookspecMarker,
+ HookimplMarker)
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def MC(methods, kwargs, callertype, firstresult=False):
+ hookfuncs = []
+ for method in methods:
+ f = HookImpl(None, "<temp>", method, method.example_impl)
+ hookfuncs.append(f)
+ return callertype(hookfuncs, kwargs, {"firstresult": firstresult})
+
+
+@hookimpl
+def hook(arg1, arg2, arg3):
+ return arg1, arg2, arg3
+
+
+@hookimpl(hookwrapper=True)
+def wrapper(arg1, arg2, arg3):
+ yield
+
+
+@pytest.fixture(
+ params=[10, 100],
+ ids="hooks={}".format,
+)
+def hooks(request):
+ return [hook for i in range(request.param)]
+
+
+@pytest.fixture(
+ params=[10, 100],
+ ids="wrappers={}".format,
+)
+def wrappers(request):
+ return [wrapper for i in range(request.param)]
+
+
+@pytest.fixture(
+ params=[_multicall, _legacymulticall],
+ ids=lambda item: item.__name__
+)
+def callertype(request):
+ return request.param
+
+
+def inner_exec(methods, callertype):
+ return MC(methods, {'arg1': 1, 'arg2': 2, 'arg3': 3}, callertype)
+
+
+def test_hook_and_wrappers_speed(benchmark, hooks, wrappers, callertype):
+ benchmark(inner_exec, hooks + wrappers, callertype)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/conftest.py
new file mode 100644
index 00000000000..3d61a349c88
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/conftest.py
@@ -0,0 +1,30 @@
+import pytest
+
+
+@pytest.fixture(
+ params=[
+ lambda spec: spec,
+ lambda spec: spec()
+ ],
+ ids=[
+ "spec-is-class",
+ "spec-is-instance"
+ ],
+)
+def he_pm(request, pm):
+ from pluggy import HookspecMarker
+ hookspec = HookspecMarker("example")
+
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ return arg + 1
+
+ pm.add_hookspecs(request.param(Hooks))
+ return pm
+
+
+@pytest.fixture
+def pm():
+ from pluggy import PluginManager
+ return PluginManager("example")
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_details.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_details.py
new file mode 100644
index 00000000000..2fad198d958
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_details.py
@@ -0,0 +1,103 @@
+import warnings
+
+import pytest
+
+from pluggy import PluginManager, HookimplMarker, HookspecMarker, _Result
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def test_parse_hookimpl_override():
+ class MyPluginManager(PluginManager):
+ def parse_hookimpl_opts(self, module_or_class, name):
+ opts = PluginManager.parse_hookimpl_opts(
+ self, module_or_class, name)
+ if opts is None:
+ if name.startswith("x1"):
+ opts = {}
+ return opts
+
+ class Plugin(object):
+ def x1meth(self):
+ pass
+
+ @hookimpl(hookwrapper=True, tryfirst=True)
+ def x1meth2(self):
+ pass
+
+ class Spec(object):
+ @hookspec
+ def x1meth(self):
+ pass
+
+ @hookspec
+ def x1meth2(self):
+ pass
+
+ pm = MyPluginManager(hookspec.project_name)
+ pm.register(Plugin())
+ pm.add_hookspecs(Spec)
+ assert not pm.hook.x1meth._nonwrappers[0].hookwrapper
+ assert not pm.hook.x1meth._nonwrappers[0].tryfirst
+ assert not pm.hook.x1meth._nonwrappers[0].trylast
+ assert not pm.hook.x1meth._nonwrappers[0].optionalhook
+
+ assert pm.hook.x1meth2._wrappers[0].tryfirst
+ assert pm.hook.x1meth2._wrappers[0].hookwrapper
+
+
+def test_plugin_getattr_raises_errors():
+ """Pluggy must be able to handle plugins which raise weird exceptions
+ when getattr() gets called (#11).
+ """
+ class DontTouchMe(object):
+ def __getattr__(self, x):
+ raise Exception('cant touch me')
+
+ class Module(object):
+ pass
+
+ module = Module()
+ module.x = DontTouchMe()
+
+ pm = PluginManager(hookspec.project_name)
+ # register() would raise an error
+ pm.register(module, 'donttouch')
+ assert pm.get_plugin('donttouch') is module
+
+
+def test_warning_on_call_vs_hookspec_arg_mismatch():
+ """Verify that is a hook is called with less arguments then defined in the
+ spec that a warning is emitted.
+ """
+ class Spec:
+ @hookspec
+ def myhook(self, arg1, arg2):
+ pass
+
+ class Plugin:
+ @hookimpl
+ def myhook(self, arg1):
+ pass
+
+ pm = PluginManager(hookspec.project_name)
+ pm.register(Plugin())
+ pm.add_hookspecs(Spec())
+
+ with warnings.catch_warnings(record=True) as warns:
+ warnings.simplefilter('always')
+
+ # calling should trigger a warning
+ pm.hook.myhook(arg1=1)
+
+ assert len(warns) == 1
+ warning = warns[-1]
+ assert issubclass(warning.category, Warning)
+ assert "Argument(s) ('arg2',)" in str(warning.message)
+
+
+def test_result_deprecated():
+ r = _Result(10, None)
+ with pytest.deprecated_call():
+ assert r.result == 10
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_helpers.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_helpers.py
new file mode 100644
index 00000000000..b1780968446
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_helpers.py
@@ -0,0 +1,68 @@
+from pluggy import _formatdef, varnames
+
+
+def test_varnames():
+ def f(x):
+ i = 3 # noqa
+
+ class A(object):
+ def f(self, y):
+ pass
+
+ class B(object):
+ def __call__(self, z):
+ pass
+
+ assert varnames(f) == (("x",), ())
+ assert varnames(A().f) == (('y',), ())
+ assert varnames(B()) == (('z',), ())
+
+
+def test_varnames_default():
+ def f(x, y=3):
+ pass
+
+ assert varnames(f) == (("x",), ("y",))
+
+
+def test_varnames_class():
+ class C(object):
+ def __init__(self, x):
+ pass
+
+ class D(object):
+ pass
+
+ class E(object):
+ def __init__(self, x):
+ pass
+
+ class F(object):
+ pass
+
+ assert varnames(C) == (("x",), ())
+ assert varnames(D) == ((), ())
+ assert varnames(E) == (("x",), ())
+ assert varnames(F) == ((), ())
+
+
+def test_formatdef():
+ def function1():
+ pass
+
+ assert _formatdef(function1) == 'function1()'
+
+ def function2(arg1):
+ pass
+
+ assert _formatdef(function2) == "function2(arg1)"
+
+ def function3(arg1, arg2="qwe"):
+ pass
+
+ assert _formatdef(function3) == "function3(arg1, arg2='qwe')"
+
+ def function4(arg1, *args, **kwargs):
+ pass
+
+ assert _formatdef(function4) == "function4(arg1, *args, **kwargs)"
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_hookrelay.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_hookrelay.py
new file mode 100644
index 00000000000..5e7821bed8f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_hookrelay.py
@@ -0,0 +1,210 @@
+import pytest
+from pluggy import PluginValidationError, HookimplMarker, HookspecMarker
+
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def test_happypath(pm):
+ class Api(object):
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+ hook = pm.hook
+ assert hasattr(hook, 'hello')
+ assert repr(hook.hello).find("hello") != -1
+
+ class Plugin(object):
+ @hookimpl
+ def hello(self, arg):
+ return arg + 1
+
+ plugin = Plugin()
+ pm.register(plugin)
+ out = hook.hello(arg=3)
+ assert out == [4]
+ assert not hasattr(hook, 'world')
+ pm.unregister(plugin)
+ assert hook.hello(arg=3) == []
+
+
+def test_argmismatch(pm):
+ class Api(object):
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin(object):
+ @hookimpl
+ def hello(self, argwrong):
+ pass
+
+ with pytest.raises(PluginValidationError) as exc:
+ pm.register(Plugin())
+
+ assert "argwrong" in str(exc.value)
+
+
+def test_only_kwargs(pm):
+ class Api(object):
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+ with pytest.raises(TypeError) as exc:
+ pm.hook.hello(3)
+
+ comprehensible = "hook calling supports only keyword arguments"
+ assert comprehensible in str(exc.value)
+
+
+def test_call_order(pm):
+ class Api(object):
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1(object):
+ @hookimpl
+ def hello(self, arg):
+ return 1
+
+ class Plugin2(object):
+ @hookimpl
+ def hello(self, arg):
+ return 2
+
+ class Plugin3(object):
+ @hookimpl
+ def hello(self, arg):
+ return 3
+
+ class Plugin4(object):
+ @hookimpl(hookwrapper=True)
+ def hello(self, arg):
+ assert arg == 0
+ outcome = yield
+ assert outcome.get_result() == [3, 2, 1]
+
+ pm.register(Plugin1())
+ pm.register(Plugin2())
+ pm.register(Plugin3())
+ pm.register(Plugin4()) # hookwrapper should get same list result
+ res = pm.hook.hello(arg=0)
+ assert res == [3, 2, 1]
+
+
+def test_firstresult_definition(pm):
+ class Api(object):
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1(object):
+ @hookimpl
+ def hello(self, arg):
+ return arg + 1
+
+ class Plugin2(object):
+ @hookimpl
+ def hello(self, arg):
+ return arg - 1
+
+ class Plugin3(object):
+ @hookimpl
+ def hello(self, arg):
+ return None
+
+ class Plugin4(object):
+ @hookimpl(hookwrapper=True)
+ def hello(self, arg):
+ assert arg == 3
+ outcome = yield
+ assert outcome.get_result() == 2
+
+ pm.register(Plugin1()) # discarded - not the last registered plugin
+ pm.register(Plugin2()) # used as result
+ pm.register(Plugin3()) # None result is ignored
+ pm.register(Plugin4()) # hookwrapper should get same non-list result
+ res = pm.hook.hello(arg=3)
+ assert res == 2
+
+
+def test_firstresult_force_result(pm):
+ """Verify forcing a result in a wrapper.
+ """
+ class Api(object):
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1(object):
+ @hookimpl
+ def hello(self, arg):
+ return arg + 1
+
+ class Plugin2(object):
+ @hookimpl(hookwrapper=True)
+ def hello(self, arg):
+ assert arg == 3
+ outcome = yield
+ assert outcome.get_result() == 4
+ outcome.force_result(0)
+
+ class Plugin3(object):
+ @hookimpl
+ def hello(self, arg):
+ return None
+
+ pm.register(Plugin1())
+ pm.register(Plugin2()) # wrapper
+ pm.register(Plugin3()) # ignored since returns None
+ res = pm.hook.hello(arg=3)
+ assert res == 0 # this result is forced and not a list
+
+
+def test_firstresult_returns_none(pm):
+ """If None results are returned by underlying implementations ensure
+ the multi-call loop returns a None value.
+ """
+ class Api(object):
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1(object):
+ @hookimpl
+ def hello(self, arg):
+ return None
+
+ pm.register(Plugin1())
+ res = pm.hook.hello(arg=3)
+ assert res is None
+
+
+def test_firstresult_no_plugin(pm):
+ """If no implementations/plugins have been registered for a firstresult
+ hook the multi-call loop should return a None value.
+ """
+ class Api(object):
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+ res = pm.hook.hello(arg=3)
+ assert res is None
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_method_ordering.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_method_ordering.py
new file mode 100644
index 00000000000..9584a0ae5a8
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_method_ordering.py
@@ -0,0 +1,322 @@
+import pytest
+
+
+import sys
+import types
+
+from pluggy import PluginManager, HookImpl, HookimplMarker, HookspecMarker
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+@pytest.fixture
+def hc(pm):
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ pass
+ pm.add_hookspecs(Hooks)
+ return pm.hook.he_method1
+
+
+@pytest.fixture
+def addmeth(hc):
+ def addmeth(tryfirst=False, trylast=False, hookwrapper=False):
+ def wrap(func):
+ hookimpl(tryfirst=tryfirst, trylast=trylast,
+ hookwrapper=hookwrapper)(func)
+ hc._add_hookimpl(HookImpl(None, "<temp>", func, func.example_impl))
+ return func
+ return wrap
+ return addmeth
+
+
+def funcs(hookmethods):
+ return [hookmethod.function for hookmethod in hookmethods]
+
+
+def test_adding_nonwrappers(hc, addmeth):
+ @addmeth()
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method2():
+ pass
+
+ @addmeth()
+ def he_method3():
+ pass
+ assert funcs(hc._nonwrappers) == [he_method1, he_method2, he_method3]
+
+
+def test_adding_nonwrappers_trylast(hc, addmeth):
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method1_b():
+ pass
+ assert funcs(hc._nonwrappers) == [he_method1, he_method1_middle, he_method1_b]
+
+
+def test_adding_nonwrappers_trylast3(hc, addmeth):
+ @addmeth()
+ def he_method1_a():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1_b():
+ pass
+
+ @addmeth()
+ def he_method1_c():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1_d():
+ pass
+ assert funcs(hc._nonwrappers) == \
+ [he_method1_d, he_method1_b, he_method1_a, he_method1_c]
+
+
+def test_adding_nonwrappers_trylast2(hc, addmeth):
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth()
+ def he_method1_b():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1():
+ pass
+ assert funcs(hc._nonwrappers) == \
+ [he_method1, he_method1_middle, he_method1_b]
+
+
+def test_adding_nonwrappers_tryfirst(hc, addmeth):
+ @addmeth(tryfirst=True)
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth()
+ def he_method1_b():
+ pass
+ assert funcs(hc._nonwrappers) == [
+ he_method1_middle, he_method1_b, he_method1]
+
+
+def test_adding_wrappers_ordering(hc, addmeth):
+ @addmeth(hookwrapper=True)
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth(hookwrapper=True)
+ def he_method3():
+ pass
+
+ assert funcs(hc._nonwrappers) == [he_method1_middle]
+ assert funcs(hc._wrappers) == [he_method1, he_method3]
+
+
+def test_adding_wrappers_ordering_tryfirst(hc, addmeth):
+ @addmeth(hookwrapper=True, tryfirst=True)
+ def he_method1():
+ pass
+
+ @addmeth(hookwrapper=True)
+ def he_method2():
+ pass
+
+ assert hc._nonwrappers == []
+ assert funcs(hc._wrappers) == [he_method2, he_method1]
+
+
+def test_hookspec(pm):
+ class HookSpec(object):
+ @hookspec()
+ def he_myhook1(arg1):
+ pass
+
+ @hookspec(firstresult=True)
+ def he_myhook2(arg1):
+ pass
+
+ @hookspec(firstresult=False)
+ def he_myhook3(arg1):
+ pass
+
+ pm.add_hookspecs(HookSpec)
+ assert not pm.hook.he_myhook1.spec_opts["firstresult"]
+ assert pm.hook.he_myhook2.spec_opts["firstresult"]
+ assert not pm.hook.he_myhook3.spec_opts["firstresult"]
+
+
+@pytest.mark.parametrize('name', ["hookwrapper", "optionalhook", "tryfirst", "trylast"])
+@pytest.mark.parametrize('val', [True, False])
+def test_hookimpl(name, val):
+ @hookimpl(**{name: val})
+ def he_myhook1(arg1):
+ pass
+ if val:
+ assert he_myhook1.example_impl.get(name)
+ else:
+ assert not hasattr(he_myhook1, name)
+
+
+def test_load_setuptools_instantiation(monkeypatch, pm):
+ pkg_resources = pytest.importorskip("pkg_resources")
+
+ def my_iter(name):
+ assert name == "hello"
+
+ class EntryPoint(object):
+ name = "myname"
+ dist = None
+
+ def load(self):
+ class PseudoPlugin(object):
+ x = 42
+ return PseudoPlugin()
+
+ return iter([EntryPoint()])
+
+ monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
+ num = pm.load_setuptools_entrypoints("hello")
+ assert num == 1
+ plugin = pm.get_plugin("myname")
+ assert plugin.x == 42
+ assert pm.list_plugin_distinfo() == [(plugin, None)]
+
+
+def test_load_setuptools_not_installed(monkeypatch, pm):
+ monkeypatch.setitem(
+ sys.modules, 'pkg_resources',
+ types.ModuleType("pkg_resources"))
+
+ with pytest.raises(ImportError):
+ pm.load_setuptools_entrypoints("qwe")
+
+
+def test_add_tracefuncs(he_pm):
+ out = []
+
+ class api1(object):
+ @hookimpl
+ def he_method1(self):
+ out.append("he_method1-api1")
+
+ class api2(object):
+ @hookimpl
+ def he_method1(self):
+ out.append("he_method1-api2")
+
+ he_pm.register(api1())
+ he_pm.register(api2())
+
+ def before(hook_name, hook_impls, kwargs):
+ out.append((hook_name, list(hook_impls), kwargs))
+
+ def after(outcome, hook_name, hook_impls, kwargs):
+ out.append((outcome, hook_name, list(hook_impls), kwargs))
+
+ undo = he_pm.add_hookcall_monitoring(before, after)
+
+ he_pm.hook.he_method1(arg=1)
+ assert len(out) == 4
+ assert out[0][0] == "he_method1"
+ assert len(out[0][1]) == 2
+ assert isinstance(out[0][2], dict)
+ assert out[1] == "he_method1-api2"
+ assert out[2] == "he_method1-api1"
+ assert len(out[3]) == 4
+ assert out[3][1] == out[0][0]
+
+ undo()
+ he_pm.hook.he_method1(arg=1)
+ assert len(out) == 4 + 2
+
+
+def test_hook_tracing(he_pm):
+ saveindent = []
+
+ class api1(object):
+ @hookimpl
+ def he_method1(self):
+ saveindent.append(he_pm.trace.root.indent)
+
+ class api2(object):
+ @hookimpl
+ def he_method1(self):
+ saveindent.append(he_pm.trace.root.indent)
+ raise ValueError()
+
+ he_pm.register(api1())
+ out = []
+ he_pm.trace.root.setwriter(out.append)
+ undo = he_pm.enable_tracing()
+ try:
+ indent = he_pm.trace.root.indent
+ he_pm.hook.he_method1(arg=1)
+ assert indent == he_pm.trace.root.indent
+ assert len(out) == 2
+ assert 'he_method1' in out[0]
+ assert 'finish' in out[1]
+
+ out[:] = []
+ he_pm.register(api2())
+
+ with pytest.raises(ValueError):
+ he_pm.hook.he_method1(arg=1)
+ assert he_pm.trace.root.indent == indent
+ assert saveindent[0] > indent
+ finally:
+ undo()
+
+
+@pytest.mark.parametrize('include_hookspec', [True, False])
+def test_prefix_hookimpl(include_hookspec):
+ pm = PluginManager(hookspec.project_name, "hello_")
+
+ if include_hookspec:
+ class HookSpec(object):
+ @hookspec
+ def hello_myhook(self, arg1):
+ """ add to arg1 """
+
+ pm.add_hookspecs(HookSpec)
+
+ class Plugin(object):
+ def hello_myhook(self, arg1):
+ return arg1 + 1
+
+ pm.register(Plugin())
+ pm.register(Plugin())
+ results = pm.hook.hello_myhook(arg1=17)
+ assert results == [18, 18]
+
+
+def test_prefix_hookimpl_dontmatch_module():
+ pm = PluginManager(hookspec.project_name, "hello_")
+
+ class BadPlugin(object):
+ hello_module = __import__('email')
+
+ pm.register(BadPlugin())
+ pm.check_pending()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_multicall.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_multicall.py
new file mode 100644
index 00000000000..860a209b669
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_multicall.py
@@ -0,0 +1,194 @@
+import pytest
+
+from pluggy import _multicall, _legacymulticall, HookImpl, HookCallError
+from pluggy.callers import _LegacyMultiCall
+from pluggy import HookspecMarker, HookimplMarker
+
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def test_uses_copy_of_methods():
+ out = [lambda: 42]
+ mc = _LegacyMultiCall(out, {})
+ repr(mc)
+ out[:] = []
+ res = mc.execute()
+ return res == 42
+
+
+def MC(methods, kwargs, firstresult=False):
+ caller = _multicall
+ hookfuncs = []
+ for method in methods:
+ f = HookImpl(None, "<temp>", method, method.example_impl)
+ hookfuncs.append(f)
+ if '__multicall__' in f.argnames:
+ caller = _legacymulticall
+ return caller(hookfuncs, kwargs, firstresult=firstresult)
+
+
+def test_call_passing():
+ class P1(object):
+ @hookimpl
+ def m(self, __multicall__, x):
+ assert len(__multicall__.results) == 1
+ assert not __multicall__.hook_impls
+ return 17
+
+ class P2(object):
+ @hookimpl
+ def m(self, __multicall__, x):
+ assert __multicall__.results == []
+ assert __multicall__.hook_impls
+ return 23
+
+ p1 = P1()
+ p2 = P2()
+ reslist = MC([p1.m, p2.m], {"x": 23})
+ assert len(reslist) == 2
+ # ensure reversed order
+ assert reslist == [23, 17]
+
+
+def test_keyword_args():
+ @hookimpl
+ def f(x):
+ return x + 1
+
+ class A(object):
+ @hookimpl
+ def f(self, x, y):
+ return x + y
+
+ reslist = MC([f, A().f], dict(x=23, y=24))
+ assert reslist == [24 + 23, 24]
+
+
+def test_keyword_args_with_defaultargs():
+ @hookimpl
+ def f(x, z=1):
+ return x + z
+ reslist = MC([f], dict(x=23, y=24))
+ assert reslist == [24]
+
+
+def test_tags_call_error():
+ @hookimpl
+ def f(x):
+ return x
+ with pytest.raises(HookCallError):
+ MC([f], {})
+
+
+def test_call_subexecute():
+ @hookimpl
+ def m(__multicall__):
+ subresult = __multicall__.execute()
+ return subresult + 1
+
+ @hookimpl
+ def n():
+ return 1
+
+ res = MC([n, m], {}, firstresult=True)
+ assert res == 2
+
+
+def test_call_none_is_no_result():
+ @hookimpl
+ def m1():
+ return 1
+
+ @hookimpl
+ def m2():
+ return None
+
+ res = MC([m1, m2], {}, firstresult=True)
+ assert res == 1
+ res = MC([m1, m2], {}, {})
+ assert res == [1]
+
+
+def test_hookwrapper():
+ out = []
+
+ @hookimpl(hookwrapper=True)
+ def m1():
+ out.append("m1 init")
+ yield None
+ out.append("m1 finish")
+
+ @hookimpl
+ def m2():
+ out.append("m2")
+ return 2
+
+ res = MC([m2, m1], {})
+ assert res == [2]
+ assert out == ["m1 init", "m2", "m1 finish"]
+ out[:] = []
+ res = MC([m2, m1], {}, firstresult=True)
+ assert res == 2
+ assert out == ["m1 init", "m2", "m1 finish"]
+
+
+def test_hookwrapper_order():
+ out = []
+
+ @hookimpl(hookwrapper=True)
+ def m1():
+ out.append("m1 init")
+ yield 1
+ out.append("m1 finish")
+
+ @hookimpl(hookwrapper=True)
+ def m2():
+ out.append("m2 init")
+ yield 2
+ out.append("m2 finish")
+
+ res = MC([m2, m1], {})
+ assert res == []
+ assert out == ["m1 init", "m2 init", "m2 finish", "m1 finish"]
+
+
+def test_hookwrapper_not_yield():
+ @hookimpl(hookwrapper=True)
+ def m1():
+ pass
+
+ with pytest.raises(TypeError):
+ MC([m1], {})
+
+
+def test_hookwrapper_too_many_yield():
+ @hookimpl(hookwrapper=True)
+ def m1():
+ yield 1
+ yield 2
+
+ with pytest.raises(RuntimeError) as ex:
+ MC([m1], {})
+ assert "m1" in str(ex.value)
+ assert (__file__ + ':') in str(ex.value)
+
+
+@pytest.mark.parametrize("exc", [ValueError, SystemExit])
+def test_hookwrapper_exception(exc):
+ out = []
+
+ @hookimpl(hookwrapper=True)
+ def m1():
+ out.append("m1 init")
+ yield None
+ out.append("m1 finish")
+
+ @hookimpl
+ def m2():
+ raise exc
+
+ with pytest.raises(exc):
+ MC([m2, m1], {})
+ assert out == ["m1 init", "m1 finish"]
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_pluginmanager.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_pluginmanager.py
new file mode 100644
index 00000000000..e2c86cc6442
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_pluginmanager.py
@@ -0,0 +1,374 @@
+import pytest
+import types
+
+from pluggy import (PluginValidationError,
+ HookCallError, HookimplMarker, HookspecMarker)
+
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def test_plugin_double_register(pm):
+ pm.register(42, name="abc")
+ with pytest.raises(ValueError):
+ pm.register(42, name="abc")
+ with pytest.raises(ValueError):
+ pm.register(42, name="def")
+
+
+def test_pm(pm):
+ class A(object):
+ pass
+
+ a1, a2 = A(), A()
+ pm.register(a1)
+ assert pm.is_registered(a1)
+ pm.register(a2, "hello")
+ assert pm.is_registered(a2)
+ out = pm.get_plugins()
+ assert a1 in out
+ assert a2 in out
+ assert pm.get_plugin('hello') == a2
+ assert pm.unregister(a1) == a1
+ assert not pm.is_registered(a1)
+
+ out = pm.list_name_plugin()
+ assert len(out) == 1
+ assert out == [("hello", a2)]
+
+
+def test_has_plugin(pm):
+ class A(object):
+ pass
+
+ a1 = A()
+ pm.register(a1, 'hello')
+ assert pm.is_registered(a1)
+ assert pm.has_plugin('hello')
+
+
+def test_register_dynamic_attr(he_pm):
+ class A(object):
+ def __getattr__(self, name):
+ if name[0] != "_":
+ return 42
+ raise AttributeError()
+
+ a = A()
+ he_pm.register(a)
+ assert not he_pm.get_hookcallers(a)
+
+
+def test_pm_name(pm):
+ class A(object):
+ pass
+
+ a1 = A()
+ name = pm.register(a1, name="hello")
+ assert name == "hello"
+ pm.unregister(a1)
+ assert pm.get_plugin(a1) is None
+ assert not pm.is_registered(a1)
+ assert not pm.get_plugins()
+ name2 = pm.register(a1, name="hello")
+ assert name2 == name
+ pm.unregister(name="hello")
+ assert pm.get_plugin(a1) is None
+ assert not pm.is_registered(a1)
+ assert not pm.get_plugins()
+
+
+def test_set_blocked(pm):
+ class A(object):
+ pass
+
+ a1 = A()
+ name = pm.register(a1)
+ assert pm.is_registered(a1)
+ assert not pm.is_blocked(name)
+ pm.set_blocked(name)
+ assert pm.is_blocked(name)
+ assert not pm.is_registered(a1)
+
+ pm.set_blocked("somename")
+ assert pm.is_blocked("somename")
+ assert not pm.register(A(), "somename")
+ pm.unregister(name="somename")
+ assert pm.is_blocked("somename")
+
+
+def test_register_mismatch_method(he_pm):
+ class hello(object):
+ @hookimpl
+ def he_method_notexists(self):
+ pass
+
+ he_pm.register(hello())
+ with pytest.raises(PluginValidationError):
+ he_pm.check_pending()
+
+
+def test_register_mismatch_arg(he_pm):
+ class hello(object):
+ @hookimpl
+ def he_method1(self, qlwkje):
+ pass
+
+ with pytest.raises(PluginValidationError):
+ he_pm.register(hello())
+
+
+def test_register(pm):
+ class MyPlugin(object):
+ pass
+ my = MyPlugin()
+ pm.register(my)
+ assert my in pm.get_plugins()
+ my2 = MyPlugin()
+ pm.register(my2)
+ assert set([my, my2]).issubset(pm.get_plugins())
+
+ assert pm.is_registered(my)
+ assert pm.is_registered(my2)
+ pm.unregister(my)
+ assert not pm.is_registered(my)
+ assert my not in pm.get_plugins()
+
+
+def test_register_unknown_hooks(pm):
+ class Plugin1(object):
+ @hookimpl
+ def he_method1(self, arg):
+ return arg + 1
+
+ pname = pm.register(Plugin1())
+
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+ # assert not pm._unverified_hooks
+ assert pm.hook.he_method1(arg=1) == [2]
+ assert len(pm.get_hookcallers(pm.get_plugin(pname))) == 1
+
+
+def test_register_historic(pm):
+ class Hooks(object):
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+ pm.add_hookspecs(Hooks)
+
+ pm.hook.he_method1.call_historic(kwargs=dict(arg=1))
+ out = []
+
+ class Plugin(object):
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg)
+
+ pm.register(Plugin())
+ assert out == [1]
+
+ class Plugin2(object):
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg * 10)
+
+ pm.register(Plugin2())
+ assert out == [1, 10]
+ pm.hook.he_method1.call_historic(kwargs=dict(arg=12))
+ assert out == [1, 10, 120, 12]
+
+
+def test_with_result_memorized(pm):
+ class Hooks(object):
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+ pm.add_hookspecs(Hooks)
+
+ he_method1 = pm.hook.he_method1
+ he_method1.call_historic(lambda res: out.append(res), dict(arg=1))
+ out = []
+
+ class Plugin(object):
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 10
+
+ pm.register(Plugin())
+ assert out == [10]
+
+
+def test_with_callbacks_immediately_executed(pm):
+ class Hooks(object):
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+ pm.add_hookspecs(Hooks)
+
+ class Plugin1(object):
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 10
+
+ class Plugin2(object):
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 20
+
+ class Plugin3(object):
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 30
+
+ out = []
+ pm.register(Plugin1())
+ pm.register(Plugin2())
+
+ he_method1 = pm.hook.he_method1
+ he_method1.call_historic(lambda res: out.append(res), dict(arg=1))
+ assert out == [20, 10]
+ pm.register(Plugin3())
+ assert out == [20, 10, 30]
+
+
+def test_register_historic_incompat_hookwrapper(pm):
+ class Hooks(object):
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ out = []
+
+ class Plugin(object):
+ @hookimpl(hookwrapper=True)
+ def he_method1(self, arg):
+ out.append(arg)
+
+ with pytest.raises(PluginValidationError):
+ pm.register(Plugin())
+
+
+def test_call_extra(pm):
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ def he_method1(arg):
+ return arg * 10
+
+ out = pm.hook.he_method1.call_extra([he_method1], dict(arg=1))
+ assert out == [10]
+
+
+def test_call_with_too_few_args(pm):
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ class Plugin1(object):
+ @hookimpl
+ def he_method1(self, arg):
+ 0 / 0
+ pm.register(Plugin1())
+ with pytest.raises(HookCallError):
+ with pytest.warns(UserWarning):
+ pm.hook.he_method1()
+
+
+def test_subset_hook_caller(pm):
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ out = []
+
+ class Plugin1(object):
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg)
+
+ class Plugin2(object):
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg * 10)
+
+ class PluginNo(object):
+ pass
+
+ plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
+ pm.register(plugin1)
+ pm.register(plugin2)
+ pm.register(plugin3)
+ pm.hook.he_method1(arg=1)
+ assert out == [10, 1]
+ out[:] = []
+
+ hc = pm.subset_hook_caller("he_method1", [plugin1])
+ hc(arg=2)
+ assert out == [20]
+ out[:] = []
+
+ hc = pm.subset_hook_caller("he_method1", [plugin2])
+ hc(arg=2)
+ assert out == [2]
+ out[:] = []
+
+ pm.unregister(plugin1)
+ hc(arg=2)
+ assert out == []
+ out[:] = []
+
+ pm.hook.he_method1(arg=1)
+ assert out == [10]
+
+
+def test_multicall_deprecated(pm):
+ class P1(object):
+ @hookimpl
+ def m(self, __multicall__, x):
+ pass
+
+ pytest.deprecated_call(pm.register, P1())
+
+
+def test_add_hookspecs_nohooks(pm):
+ with pytest.raises(ValueError):
+ pm.add_hookspecs(10)
+
+
+def test_reject_prefixed_module(pm):
+ """Verify that a module type attribute that contains the project
+ prefix in its name (in this case `'example_*'` isn't collected
+ when registering a module which imports it.
+ """
+ pm._implprefix = 'example'
+ conftest = types.ModuleType("conftest")
+ src = ("""
+def example_hook():
+ pass
+""")
+ exec(src, conftest.__dict__)
+ conftest.example_blah = types.ModuleType("example_blah")
+ name = pm.register(conftest)
+ assert name == 'conftest'
+ assert getattr(pm.hook, 'example_blah', None) is None
+ assert getattr(pm.hook, 'example_hook', None) # conftest.example_hook should be collected
+ assert pm.parse_hookimpl_opts(conftest, 'example_blah') is None
+ assert pm.parse_hookimpl_opts(conftest, 'example_hook') == {}
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_tracer.py b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_tracer.py
new file mode 100644
index 00000000000..4a3e16cec43
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/testing/test_tracer.py
@@ -0,0 +1,89 @@
+
+from pluggy import _TagTracer
+
+
+def test_simple():
+ rootlogger = _TagTracer()
+ log = rootlogger.get("pytest")
+ log("hello")
+ out = []
+ rootlogger.setwriter(out.append)
+ log("world")
+ assert len(out) == 1
+ assert out[0] == "world [pytest]\n"
+ sublog = log.get("collection")
+ sublog("hello")
+ assert out[1] == "hello [pytest:collection]\n"
+
+
+def test_indent():
+ rootlogger = _TagTracer()
+ log = rootlogger.get("1")
+ out = []
+ log.root.setwriter(lambda arg: out.append(arg))
+ log("hello")
+ log.root.indent += 1
+ log("line1")
+ log("line2")
+ log.root.indent += 1
+ log("line3")
+ log("line4")
+ log.root.indent -= 1
+ log("line5")
+ log.root.indent -= 1
+ log("last")
+ assert len(out) == 7
+ names = [x[:x.rfind(' [')] for x in out]
+ assert names == [
+ 'hello', ' line1', ' line2',
+ ' line3', ' line4', ' line5', 'last']
+
+
+def test_readable_output_dictargs():
+ rootlogger = _TagTracer()
+
+ out = rootlogger.format_message(['test'], [1])
+ assert out == ['1 [test]\n']
+
+ out2 = rootlogger.format_message(['test'], ['test', {'a': 1}])
+ assert out2 == [
+ 'test [test]\n',
+ ' a: 1\n'
+ ]
+
+
+def test_setprocessor():
+ rootlogger = _TagTracer()
+ log = rootlogger.get("1")
+ log2 = log.get("2")
+ assert log2.tags == tuple("12")
+ out = []
+ rootlogger.setprocessor(tuple("12"), lambda *args: out.append(args))
+ log("not seen")
+ log2("seen")
+ assert len(out) == 1
+ tags, args = out[0]
+ assert "1" in tags
+ assert "2" in tags
+ assert args == ("seen",)
+ l2 = []
+ rootlogger.setprocessor("1:2", lambda *args: l2.append(args))
+ log2("seen")
+ tags, args = l2[0]
+ assert args == ("seen",)
+
+
+def test_setmyprocessor():
+ rootlogger = _TagTracer()
+ log = rootlogger.get("1")
+ log2 = log.get("2")
+ out = []
+ log2.setmyprocessor(lambda *args: out.append(args))
+ log("not seen")
+ assert not out
+ log2(42)
+ assert len(out) == 1
+ tags, args = out[0]
+ assert "1" in tags
+ assert "2" in tags
+ assert args == (42,)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pluggy/tox.ini b/tests/wpt/web-platform-tests/tools/third_party/pluggy/tox.ini
new file mode 100644
index 00000000000..89d44e352db
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pluggy/tox.ini
@@ -0,0 +1,44 @@
+[tox]
+envlist=check,docs,py{27,34,35,36,py}-pytestrelease,py{27,36}-pytest{master,features}
+
+[testenv]
+commands=py.test {posargs:testing/}
+setenv=
+ _PYTEST_SETUP_SKIP_PLUGGY_DEP=1
+deps=
+ pytestrelease: pytest
+ pytestmaster: git+https://github.com/pytest-dev/pytest.git@master
+ pytestfeatures: git+https://github.com/pytest-dev/pytest.git@features
+
+[testenv:benchmark]
+commands=py.test {posargs:testing/benchmark.py}
+deps=
+ pytest
+ pytest-benchmark
+
+[testenv:check]
+deps =
+ flake8
+ restructuredtext_lint
+ pygments
+commands =
+ flake8 pluggy.py setup.py testing
+ rst-lint CHANGELOG.rst README.rst
+
+[testenv:docs]
+deps =
+ sphinx
+ pygments
+commands =
+ sphinx-build -b html {toxinidir}/docs {toxinidir}/build/html-docs
+
+[pytest]
+minversion=2.0
+#--pyargs --doctest-modules --ignore=.tox
+addopts=-rxsX
+norecursedirs=.tox ja .hg .env*
+filterwarnings =
+ error
+
+[flake8]
+max-line-length=99
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/.gitattributes b/tests/wpt/web-platform-tests/tools/third_party/py/.gitattributes
new file mode 100644
index 00000000000..1246879c4da
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/.gitattributes
@@ -0,0 +1 @@
+*.dump eol=lf
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/.gitignore b/tests/wpt/web-platform-tests/tools/third_party/py/.gitignore
new file mode 100644
index 00000000000..5bb0d457d49
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/.gitignore
@@ -0,0 +1,12 @@
+
+.cache/
+.tox/
+__pycache__/
+
+*.pyc
+*.pyo
+
+*.egg-info
+.eggs/
+
+dist/*
diff --git a/tests/wpt/web-platform-tests/tools/py/.hgignore b/tests/wpt/web-platform-tests/tools/third_party/py/.hgignore
index 34976da5d15..34976da5d15 100644
--- a/tests/wpt/web-platform-tests/tools/py/.hgignore
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/.hgignore
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/.hgtags b/tests/wpt/web-platform-tests/tools/third_party/py/.hgtags
new file mode 100644
index 00000000000..9d48095bf64
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/.hgtags
@@ -0,0 +1,68 @@
+52c6d9e78777a5a34e813123997dfc614a1a4767 1.0.0b3
+1c7aaa8c61f3b0945921a9acc7beb184201aed4b 1.0.0b4
+1c7aaa8c61f3b0945921a9acc7beb184201aed4b 1.0.0b4
+0000000000000000000000000000000000000000 1.0.0b4
+0000000000000000000000000000000000000000 1.0.0b4
+8cd6eb91eba313b012d6e568f37d844dc0751f2e 1.0.0b4
+8cd6eb91eba313b012d6e568f37d844dc0751f2e 1.0.0b4
+0000000000000000000000000000000000000000 1.0.0b4
+2cc0507f117ffe721dff7ee026648cfce00ec92f 1.0.0b6
+86f1e1b6e49bf5882a809f11edd1dbb08162cdad 1.0.0b8
+86f1e1b6e49bf5882a809f11edd1dbb08162cdad 1.0.0b8
+c63f35c266cbb26dad6b87b5e115d65685adf448 1.0.0b8
+c63f35c266cbb26dad6b87b5e115d65685adf448 1.0.0b8
+0eaa0fdf2ba0163cf534dc2eff4ba2e5fc66c261 1.0.0b8
+e2a60653cb490aeed81bbbd83c070b99401c211c 1.0.0b9
+5ea0cdf7854c3d4278d36eda94a2b68483a0e211 1.0.0
+5ea0cdf7854c3d4278d36eda94a2b68483a0e211 1.0.0
+7acde360d94b6a2690ce3d03ff39301da84c0a2b 1.0.0
+6bd221981ac99103002c1cb94fede400d23a96a1 1.0.1
+4816e8b80602a3fd3a0a120333ad85fbe7d8bab4 1.0.2
+60c44bdbf093285dc69d5462d4dbb4acad325ca6 1.1.0
+319187fcda66714c5eb1353492babeec3d3c826f 1.1.1
+4fc5212f7626a56b9eb6437b5c673f56dd7eb942 1.2.0
+c143a8c8840a1c68570890c8ac6165bbf92fd3c6 1.2.1
+eafd3c256e8732dfb0a4d49d051b5b4339858926 1.3.0
+d5eacf390af74553227122b85e20345d47b2f9e6 1.3.1
+d5eacf390af74553227122b85e20345d47b2f9e6 1.3.1
+8b8e7c25a13cf863f01b2dd955978285ae9daf6a 1.3.1
+3bff44b188a7ec1af328d977b9d39b6757bb38df 1.3.2
+c59d3fa8681a5b5966b8375b16fccd64a3a8dbeb 1.3.3
+79ef6377705184c55633d456832eea318fedcf61 1.3.4
+79ef6377705184c55633d456832eea318fedcf61 1.3.4
+90fffd35373e9f125af233f78b19416f0938d841 1.3.4
+5346ab41b059c95a48cbe1e8a7bae96ce6e0da27 1.4.0
+1f3125cba7976538952be268f107c1d0c36c5ce8 1.4.1
+04ab22db4ff737cf31e91d75a0f5d7077f324167 1.4.2
+9950bf9d684a984d511795013421c89c5cf88bef 1.4.3
+d9951e3bdbc765e73835ae13012f6a074d13d8bf 1.4.4
+b827dd156a36753e32c7f3f15ce82d6fe9e356c8 1.4.6
+f15726f9e5a67cc6221c499affa4840e9d591763 1.4.7
+abfabd07a1d328f13c730e8a50d80d2e470afd3b 1.4.9
+7f37ee0aff9be4b839d6759cfee336f60e8393a4 1.4.10
+fe4593263efa10ea7ba014db6e3379e0b82368a2 1.4.11
+f07af25a26786e4825b5170e17ad693245cb3426 1.4.12
+d3730d84ba7eda92fd3469a3f63fd6d8cb22c975 1.4.13
+12c1ae8e7c5345721e9ec9f8e27b1e36c07f74dc 1.4.14
+12c1ae8e7c5345721e9ec9f8e27b1e36c07f74dc 1.4.14
+0000000000000000000000000000000000000000 1.4.14
+0000000000000000000000000000000000000000 1.4.14
+1497e2efd0f8c73a0e3d529debf0c489e4cd6cab 1.4.14
+e065014c1ce8ad110a381e9baaaa5d647ba7ac6b 1.4.15
+e9e5b38f53dc35b35aa1f9ee9a9be9bbd2d2c3b1 1.4.16
+c603503945f52b78522d96a423605cbc953236d3 1.4.17
+c59201105a29801cc858eb9160b7a19791b91a35 1.4.18
+284cc172e294d48edc840012e1451c32c3963d92 1.4.19
+a3e0626aa0c5aecf271367dc77e476ab216ea3c8 1.4.20
+5e48016c4a3af8e7358a1267d33d021e71765bed 1.4.21
+01ae2cfcc61c4fcb3aa5031349adb5b467c31018 1.4.23
+5ffd982f4dff60b588f309cd9bdc61036547282a 1.4.24
+dc9ffbcaf1f7d72e96be3f68c11deebb7e7193c5 1.4.25
+6de1a44bf75de7af4fcae947c235e9072bbdbb9a 1.4.26
+7d650ba2657890a2253c8c4a83f170febebd90fa 1.4.27
+7d650ba2657890a2253c8c4a83f170febebd90fa 1.4.27
+1810003dec63dd1b506a23849861fffa5bc3ba13 1.4.27
+ba08706f08ddea1b77a426f00dfe2bdc244345e8 1.4.28
+4e8054ada63f3327bcf759ae7cd36c7c8652bc9b 1.4.29
+366ab346610c6de8aaa7617e24011794b40236c6 1.4.30
+657380e439f9b7e04918cb162cb2e46388244b42 1.4.31
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/.travis.yml b/tests/wpt/web-platform-tests/tools/third_party/py/.travis.yml
new file mode 100644
index 00000000000..917c59d14b7
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/.travis.yml
@@ -0,0 +1,27 @@
+sudo: false
+language: python
+python:
+- '2.7'
+- '3.4'
+- '3.5'
+- '3.6'
+- 'pypy-5.4'
+env:
+- DEPS="pytest~=2.9.0"
+- DEPS="pytest~=3.0.0"
+#- DEPS="pytest~=3.1.0"
+
+matrix:
+
+ include:
+ - python: '2.7'
+ # using a different option due to pytest-addopts pytester issues
+ env: PYTEST_XADDOPTS="-n 3 --runslowtests" DEPS="pytest~=3.0.0 pytest-xdist"
+ allow_failures:
+ - python: 'pypy-5.4'
+install:
+- pip install -U setuptools setuptools_scm
+- pip install $DEPS
+- pip install -U . --force-reinstall
+script:
+- py.test --lsof $PYTEST_XADDOPTS
diff --git a/tests/wpt/web-platform-tests/tools/py/AUTHORS b/tests/wpt/web-platform-tests/tools/third_party/py/AUTHORS
index 8c0cf9b71b1..8c0cf9b71b1 100644
--- a/tests/wpt/web-platform-tests/tools/py/AUTHORS
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/AUTHORS
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/CHANGELOG b/tests/wpt/web-platform-tests/tools/third_party/py/CHANGELOG
new file mode 100644
index 00000000000..7e7b01b0b9e
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/CHANGELOG
@@ -0,0 +1,1149 @@
+1.5.2
+=====
+
+- fix #169, #170: error importing py.log on Windows: no module named ``syslog``.
+
+1.5.1
+=====
+
+- fix #167 - prevent pip from installing py in unsupported Python versions.
+
+1.5.0
+=====
+
+NOTE: **this release has been removed from PyPI** due to missing package
+metadata which caused a number of problems to py26 and py33 users.
+This issue was fixed in the 1.5.1 release.
+
+- python 2.6 and 3.3 are no longer supported
+- deprecate py.std and remove all internal uses
+- fix #73 turn py.error into an actual module
+- path join to / no longer produces leading double slashes
+- fix #82 - remove unsupportable aliases
+- fix python37 compatibility of path.sysfind on windows by correctly replacing vars
+- turn iniconfig and apipkg into vendored packages and ease de-vendoring for distributions
+- fix #68 remove invalid py.test.ensuretemp references
+- fix #25 - deprecate path.listdir(sort=callable)
+- add ``TerminalWriter.chars_on_current_line`` read-only property that tracks how many characters
+ have been written to the current line.
+
+1.4.34
+====================================================================
+
+- fix issue119 / pytest issue708 where tmpdir may fail to make numbered directories
+ when the filesystem is case-insensitive.
+
+1.4.33
+====================================================================
+
+- avoid imports in calls to py.path.local().fnmatch(). Thanks Andreas Pelme for
+ the PR.
+
+- fix issue106: Naive unicode encoding when calling fspath() in python2. Thanks Tiago Nobrega for the PR.
+
+- fix issue110: unittest.TestCase.assertWarns fails with py imported.
+
+1.4.32
+====================================================================
+
+- fix issue70: added ability to copy all stat info in py.path.local.copy.
+
+- make TerminalWriter.fullwidth a property. This results in the correct
+ value when the terminal gets resized.
+
+- update supported html tags to include recent additions.
+ Thanks Denis Afonso for the PR.
+
+- Remove internal code in ``Source.compile`` meant to support earlier Python 3 versions that produced the side effect
+ of leaving ``None`` in ``sys.modules`` when called (see pytest-dev/pytest#2103).
+ Thanks Bruno Oliveira for the PR.
+
+1.4.31
+==================================================
+
+- fix local().copy(dest, mode=True) to also work
+ with unicode.
+
+- pass better error message with svn EEXIST paths
+
+1.4.30
+==================================================
+
+- fix issue68 an assert with a multiline list comprehension
+ was not reported correctly. Thanks Henrik Heibuerger.
+
+
+1.4.29
+==================================================
+
+- fix issue55: revert a change to the statement finding algorithm
+ which is used by pytest for generating tracebacks.
+ Thanks Daniel Hahler for initial analysis.
+
+- fix pytest issue254 for when traceback rendering can't
+ find valid source code. Thanks Ionel Cristian Maries.
+
+
+1.4.28
+==================================================
+
+- fix issue64 -- dirpath regression when "abs=True" is passed.
+ Thanks Gilles Dartiguelongue.
+
+1.4.27
+==================================================
+
+- fix issue59: point to new repo site
+
+- allow a new ensuresyspath="append" mode for py.path.local.pyimport()
+ so that a neccessary import path is appended instead of prepended to
+ sys.path
+
+- strike undocumented, untested argument to py.path.local.pypkgpath
+
+- speed up py.path.local.dirpath by a factor of 10
+
+1.4.26
+==================================================
+
+- avoid calling normpath twice in py.path.local
+
+- py.builtin._reraise properly reraises under Python3 now.
+
+- fix issue53 - remove module index, thanks jenisys.
+
+- allow posix path separators when "fnmatch" is called.
+ Thanks Christian Long for the complete PR.
+
+1.4.25
+==================================================
+
+- fix issue52: vaguely fix py25 compat of py.path.local (it's not
+ officially supported), also fix docs
+
+- fix pytest issue 589: when checking if we have a recursion error
+ check for the specific "maximum recursion depth" text of the exception.
+
+1.4.24
+==================================================
+
+- Fix retrieving source when an else: line has an other statement on
+ the same line.
+
+- add localpath read_text/write_text/read_bytes/write_bytes methods
+ as shortcuts and clearer bytes/text interfaces for read/write.
+ Adapted from a PR from Paul Moore.
+
+
+1.4.23
+==================================================
+
+- use newer apipkg version which makes attribute access on
+ alias modules resolve to None rather than an ImportError.
+ This helps with code that uses inspect.getframeinfo()
+ on py34 which causes a complete walk on sys.modules
+ thus triggering the alias module to resolve and blowing
+ up with ImportError. The negative side is that something
+ like "py.test.X" will now result in None instead of "importerror: pytest"
+ if pytest is not installed. But you shouldn't import "py.test"
+ anyway anymore.
+
+- adapt one svn test to only check for any exception instead
+ of specific ones because different svn versions cause different
+ errors and we don't care.
+
+
+1.4.22
+==================================================
+
+- refactor class-level registry on ForkedFunc child start/finish
+ event to become instance based (i.e. passed into the constructor)
+
+1.4.21
+==================================================
+
+- ForkedFunc now has class-level register_on_start/on_exit()
+ methods to allow adding information in the boxed process.
+ Thanks Marc Schlaich.
+
+- ForkedFunc in the child opens in "auto-flush" mode for
+ stdout/stderr so that when a subprocess dies you can see
+ its output even if it didn't flush itself.
+
+- refactor traceback generation in light of pytest issue 364
+ (shortening tracebacks). you can now set a new traceback style
+ on a per-entry basis such that a caller can force entries to be
+ isplayed as short or long entries.
+
+- win32: py.path.local.sysfind(name) will preferrably return files with
+ extensions so that if "X" and "X.bat" or "X.exe" is on the PATH,
+ one of the latter two will be returned.
+
+1.4.20
+==================================================
+
+- ignore unicode decode errors in xmlescape. Thanks Anatoly Bubenkoff.
+
+- on python2 modify traceback.format_exception_only to match python3
+ behaviour, namely trying to print unicode for Exception instances
+
+- use a safer way for serializing exception reports (helps to fix
+ pytest issue413)
+
+Changes between 1.4.18 and 1.4.19
+==================================================
+
+- merge in apipkg fixes
+
+- some micro-optimizations in py/_code/code.py for speeding
+ up pytest runs. Thanks Alex Gaynor for initiative.
+
+- check PY_COLORS=1 or PY_COLORS=0 to force coloring/not-coloring
+ for py.io.TerminalWriter() independently from capabilities
+ of the output file. Thanks Marc Abramowitz for the PR.
+
+- some fixes to unicode handling in assertion handling.
+ Thanks for the PR to Floris Bruynooghe. (This helps
+ to fix pytest issue 319).
+
+- depend on setuptools presence, remove distribute_setup
+
+Changes between 1.4.17 and 1.4.18
+==================================================
+
+- introduce path.ensure_dir() as a synonym for ensure(..., dir=1)
+
+- some unicode/python3 related fixes wrt to path manipulations
+ (if you start passing unicode particular in py2 you might
+ still get problems, though)
+
+Changes between 1.4.16 and 1.4.17
+==================================================
+
+- make py.io.TerminalWriter() prefer colorama if it is available
+ and avoid empty lines when separator-lines are printed by
+ being defensive and reducing the working terminalwidth by 1
+
+- introduce optional "expanduser" argument to py.path.local
+ to that local("~", expanduser=True) gives the home
+ directory of "user".
+
+Changes between 1.4.15 and 1.4.16
+==================================================
+
+- fix issue35 - define __gt__ ordering between a local path
+ and strings
+
+- fix issue36 - make chdir() work even if os.getcwd() fails.
+
+- add path.exists/isdir/isfile/islink shortcuts
+
+- introduce local path.as_cwd() context manager.
+
+- introduce p.write(ensure=1) and p.open(ensure=1)
+ where ensure triggers creation of neccessary parent
+ dirs.
+
+
+Changes between 1.4.14 and 1.4.15
+==================================================
+
+- majorly speed up some common calling patterns with
+ LocalPath.listdir()/join/check/stat functions considerably.
+
+- fix an edge case with fnmatch where a glob style pattern appeared
+ in an absolute path.
+
+Changes between 1.4.13 and 1.4.14
+==================================================
+
+- fix dupfile to work with files that don't
+ carry a mode. Thanks Jason R. Coombs.
+
+Changes between 1.4.12 and 1.4.13
+==================================================
+
+- fix getting statementrange/compiling a file ending
+ in a comment line without newline (on python2.5)
+- for local paths you can pass "mode=True" to a copy()
+ in order to copy permission bits (underlying mechanism
+ is using shutil.copymode)
+- add paths arguments to py.path.local.sysfind to restrict
+ search to the diretories in the path.
+- add isdir/isfile/islink to path.stat() objects allowing to perform
+ multiple checks without calling out multiple times
+- drop py.path.local.__new__ in favour of a simpler __init__
+- iniconfig: allow "name:value" settings in config files, no space after
+ "name" required
+- fix issue 27 - NameError in unlikely untested case of saferepr
+
+
+Changes between 1.4.11 and 1.4.12
+==================================================
+
+- fix python2.4 support - for pre-AST interpreters re-introduce
+ old way to find statements in exceptions (closes pytest issue 209)
+- add tox.ini to distribution
+- fix issue23 - print *,** args information in tracebacks,
+ thanks Manuel Jacob
+
+
+Changes between 1.4.10 and 1.4.11
+==================================================
+
+- use _ast to determine statement ranges when printing tracebacks -
+ avoiding multi-second delays on some large test modules
+- fix an internal test to not use class-denoted pytest_funcarg__
+- fix a doc link to bug tracker
+- try to make terminal.write() printing more robust against
+ unicodeencode/decode problems, amend according test
+- introduce py.builtin.text and py.builtin.bytes
+ to point to respective str/unicode (py2) and bytes/str (py3) types
+- fix error handling on win32/py33 for ENODIR
+
+Changes between 1.4.9 and 1.4.10
+==================================================
+
+- terminalwriter: default to encode to UTF8 if no encoding is defined
+ on the output stream
+- issue22: improve heuristic for finding the statementrange in exceptions
+
+Changes between 1.4.8 and 1.4.9
+==================================================
+
+- fix bug of path.visit() which would not recognize glob-style patterns
+ for the "rec" recursion argument
+- changed iniconfig parsing to better conform, now the chars ";"
+ and "#" only mark a comment at the stripped start of a line
+- include recent apipkg-1.2
+- change internal terminalwriter.line/reline logic to more nicely
+ support file spinners
+
+Changes between 1.4.7 and 1.4.8
+==================================================
+
+- fix issue 13 - correct handling of the tag name object in xmlgen
+- fix issue 14 - support raw attribute values in xmlgen
+- fix windows terminalwriter printing/re-line problem
+- update distribute_setup.py to 0.6.27
+
+Changes between 1.4.6 and 1.4.7
+==================================================
+
+- fix issue11 - own test failure with python3.3 / Thanks Benjamin Peterson
+- help fix pytest issue 102
+
+Changes between 1.4.5 and 1.4.6
+==================================================
+
+- help to fix pytest issue99: unify output of
+ ExceptionInfo.getrepr(style="native") with ...(style="long")
+- fix issue7: source.getstatementrange() now raises proper error
+ if no valid statement can be found
+- fix issue8: fix code and tests of svnurl/svnwc to work on subversion 1.7 -
+ note that path.status(updates=1) will not properly work svn-17's status
+ --xml output is broken.
+- make source.getstatementrange() more resilent about non-python code frames
+ (as seen from jnja2)
+- make trackeback recursion detection more resilent
+ about the eval magic of a decorator library
+- iniconfig: add support for ; as comment starter
+- properly handle lists in xmlgen on python3
+- normalize py.code.getfslineno(obj) to always return a (string, int) tuple
+ defaulting to ("", -1) respectively if no source code can be found for obj.
+
+Changes between 1.4.4 and 1.4.5
+==================================================
+
+- improve some unicode handling in terminalwriter and capturing
+ (used by pytest)
+
+Changes between 1.4.3 and 1.4.4
+==================================================
+
+- a few fixes and assertion related refinements for pytest-2.1
+- guard py.code.Code and getfslineno against bogus input
+ and make py.code.Code objects for object instance
+ by looking up their __call__ function.
+- make exception presentation robust against invalid current cwd
+
+Changes between 1.4.2 and 1.4.3
+==================================================
+
+- fix terminal coloring issue for skipped tests (thanks Amaury)
+- fix issue4 - large calls to ansi_print (thanks Amaury)
+
+Changes between 1.4.1 and 1.4.2
+==================================================
+
+- fix (pytest) issue23 - tmpdir argument now works on Python3.2 and WindowsXP
+ (which apparently starts to offer os.symlink now)
+
+- better error message for syntax errors from compiled code
+
+- small fix to better deal with (un-)colored terminal output on windows
+
+Changes between 1.4.0 and 1.4.1
+==================================================
+
+- fix issue1 - py.error.* classes to be pickleable
+
+- fix issue2 - on windows32 use PATHEXT as the list of potential
+ extensions to find find binaries with py.path.local.sysfind(commandname)
+
+- fix (pytest-) issue10 and refine assertion reinterpretation
+ to avoid breaking if the __nonzero__ of an object fails
+
+- fix (pytest-) issue17 where python3 does not like "import *"
+ leading to misrepresentation of import-errors in test modules
+
+- fix py.error.* attribute pypy access issue
+
+- allow path.samefile(arg) to succeed when arg is a relative filename
+
+- fix (pytest-) issue20 path.samefile(relpath) works as expected now
+
+- fix (pytest-) issue8 len(long_list) now shows the length of the list
+
+Changes between 1.3.4 and 1.4.0
+==================================================
+
+- py.test was moved to a separate "pytest" package. What remains is
+ a stub hook which will proxy ``import py.test`` to ``pytest``.
+- all command line tools ("py.cleanup/lookup/countloc/..." moved
+ to "pycmd" package)
+- removed the old and deprecated "py.magic" namespace
+- use apipkg-1.1 and make py.apipkg.initpkg|ApiModule available
+- add py.iniconfig module for brain-dead easy ini-config file parsing
+- introduce py.builtin.any()
+- path objects have a .dirname attribute now (equivalent to
+ os.path.dirname(path))
+- path.visit() accepts breadthfirst (bf) and sort options
+- remove deprecated py.compat namespace
+
+Changes between 1.3.3 and 1.3.4
+==================================================
+
+- fix issue111: improve install documentation for windows
+- fix issue119: fix custom collectability of __init__.py as a module
+- fix issue116: --doctestmodules work with __init__.py files as well
+- fix issue115: unify internal exception passthrough/catching/GeneratorExit
+- fix issue118: new --tb=native for presenting cpython-standard exceptions
+
+Changes between 1.3.2 and 1.3.3
+==================================================
+
+- fix issue113: assertion representation problem with triple-quoted strings
+ (and possibly other cases)
+- make conftest loading detect that a conftest file with the same
+ content was already loaded, avoids surprises in nested directory structures
+ which can be produced e.g. by Hudson. It probably removes the need to use
+ --confcutdir in most cases.
+- fix terminal coloring for win32
+ (thanks Michael Foord for reporting)
+- fix weirdness: make terminal width detection work on stdout instead of stdin
+ (thanks Armin Ronacher for reporting)
+- remove trailing whitespace in all py/text distribution files
+
+Changes between 1.3.1 and 1.3.2
+==================================================
+
+New features
+++++++++++++++++++
+
+- fix issue103: introduce py.test.raises as context manager, examples::
+
+ with py.test.raises(ZeroDivisionError):
+ x = 0
+ 1 / x
+
+ with py.test.raises(RuntimeError) as excinfo:
+ call_something()
+
+ # you may do extra checks on excinfo.value|type|traceback here
+
+ (thanks Ronny Pfannschmidt)
+
+- Funcarg factories can now dynamically apply a marker to a
+ test invocation. This is for example useful if a factory
+ provides parameters to a test which are expected-to-fail::
+
+ def pytest_funcarg__arg(request):
+ request.applymarker(py.test.mark.xfail(reason="flaky config"))
+ ...
+
+ def test_function(arg):
+ ...
+
+- improved error reporting on collection and import errors. This makes
+ use of a more general mechanism, namely that for custom test item/collect
+ nodes ``node.repr_failure(excinfo)`` is now uniformly called so that you can
+ override it to return a string error representation of your choice
+ which is going to be reported as a (red) string.
+
+- introduce '--junitprefix=STR' option to prepend a prefix
+ to all reports in the junitxml file.
+
+Bug fixes / Maintenance
+++++++++++++++++++++++++++
+
+- make tests and the ``pytest_recwarn`` plugin in particular fully compatible
+ to Python2.7 (if you use the ``recwarn`` funcarg warnings will be enabled so that
+ you can properly check for their existence in a cross-python manner).
+- refine --pdb: ignore xfailed tests, unify its TB-reporting and
+ don't display failures again at the end.
+- fix assertion interpretation with the ** operator (thanks Benjamin Peterson)
+- fix issue105 assignment on the same line as a failing assertion (thanks Benjamin Peterson)
+- fix issue104 proper escaping for test names in junitxml plugin (thanks anonymous)
+- fix issue57 -f|--looponfail to work with xpassing tests (thanks Ronny)
+- fix issue92 collectonly reporter and --pastebin (thanks Benjamin Peterson)
+- fix py.code.compile(source) to generate unique filenames
+- fix assertion re-interp problems on PyPy, by defering code
+ compilation to the (overridable) Frame.eval class. (thanks Amaury Forgeot)
+- fix py.path.local.pyimport() to work with directories
+- streamline py.path.local.mkdtemp implementation and usage
+- don't print empty lines when showing junitxml-filename
+- add optional boolean ignore_errors parameter to py.path.local.remove
+- fix terminal writing on win32/python2.4
+- py.process.cmdexec() now tries harder to return properly encoded unicode objects
+ on all python versions
+- install plain py.test/py.which scripts also for Jython, this helps to
+ get canonical script paths in virtualenv situations
+- make path.bestrelpath(path) return ".", note that when calling
+ X.bestrelpath the assumption is that X is a directory.
+- make initial conftest discovery ignore "--" prefixed arguments
+- fix resultlog plugin when used in an multicpu/multihost xdist situation
+ (thanks Jakub Gustak)
+- perform distributed testing related reporting in the xdist-plugin
+ rather than having dist-related code in the generic py.test
+ distribution
+- fix homedir detection on Windows
+- ship distribute_setup.py version 0.6.13
+
+Changes between 1.3.0 and 1.3.1
+==================================================
+
+New features
+++++++++++++++++++
+
+- issue91: introduce new py.test.xfail(reason) helper
+ to imperatively mark a test as expected to fail. Can
+ be used from within setup and test functions. This is
+ useful especially for parametrized tests when certain
+ configurations are expected-to-fail. In this case the
+ declarative approach with the @py.test.mark.xfail cannot
+ be used as it would mark all configurations as xfail.
+
+- issue102: introduce new --maxfail=NUM option to stop
+ test runs after NUM failures. This is a generalization
+ of the '-x' or '--exitfirst' option which is now equivalent
+ to '--maxfail=1'. Both '-x' and '--maxfail' will
+ now also print a line near the end indicating the Interruption.
+
+- issue89: allow py.test.mark decorators to be used on classes
+ (class decorators were introduced with python2.6) and
+ also allow to have multiple markers applied at class/module level
+ by specifying a list.
+
+- improve and refine letter reporting in the progress bar:
+ . pass
+ f failed test
+ s skipped tests (reminder: use for dependency/platform mismatch only)
+ x xfailed test (test that was expected to fail)
+ X xpassed test (test that was expected to fail but passed)
+
+ You can use any combination of 'fsxX' with the '-r' extended
+ reporting option. The xfail/xpass results will show up as
+ skipped tests in the junitxml output - which also fixes
+ issue99.
+
+- make py.test.cmdline.main() return the exitstatus instead of raising
+ SystemExit and also allow it to be called multiple times. This of
+ course requires that your application and tests are properly teared
+ down and don't have global state.
+
+Fixes / Maintenance
+++++++++++++++++++++++
+
+- improved traceback presentation:
+ - improved and unified reporting for "--tb=short" option
+ - Errors during test module imports are much shorter, (using --tb=short style)
+ - raises shows shorter more relevant tracebacks
+ - --fulltrace now more systematically makes traces longer / inhibits cutting
+
+- improve support for raises and other dynamically compiled code by
+ manipulating python's linecache.cache instead of the previous
+ rather hacky way of creating custom code objects. This makes
+ it seemlessly work on Jython and PyPy where it previously didn't.
+
+- fix issue96: make capturing more resilient against Control-C
+ interruptions (involved somewhat substantial refactoring
+ to the underlying capturing functionality to avoid race
+ conditions).
+
+- fix chaining of conditional skipif/xfail decorators - so it works now
+ as expected to use multiple @py.test.mark.skipif(condition) decorators,
+ including specific reporting which of the conditions lead to skipping.
+
+- fix issue95: late-import zlib so that it's not required
+ for general py.test startup.
+
+- fix issue94: make reporting more robust against bogus source code
+ (and internally be more careful when presenting unexpected byte sequences)
+
+
+Changes between 1.2.1 and 1.3.0
+==================================================
+
+- deprecate --report option in favour of a new shorter and easier to
+ remember -r option: it takes a string argument consisting of any
+ combination of 'xfsX' characters. They relate to the single chars
+ you see during the dotted progress printing and will print an extra line
+ per test at the end of the test run. This extra line indicates the exact
+ position or test ID that you directly paste to the py.test cmdline in order
+ to re-run a particular test.
+
+- allow external plugins to register new hooks via the new
+ pytest_addhooks(pluginmanager) hook. The new release of
+ the pytest-xdist plugin for distributed and looponfailing
+ testing requires this feature.
+
+- add a new pytest_ignore_collect(path, config) hook to allow projects and
+ plugins to define exclusion behaviour for their directory structure -
+ for example you may define in a conftest.py this method::
+
+ def pytest_ignore_collect(path):
+ return path.check(link=1)
+
+ to prevent even a collection try of any tests in symlinked dirs.
+
+- new pytest_pycollect_makemodule(path, parent) hook for
+ allowing customization of the Module collection object for a
+ matching test module.
+
+- extend and refine xfail mechanism:
+ ``@py.test.mark.xfail(run=False)`` do not run the decorated test
+ ``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries
+ specifiying ``--runxfail`` on command line virtually ignores xfail markers
+
+- expose (previously internal) commonly useful methods:
+ py.io.get_terminal_with() -> return terminal width
+ py.io.ansi_print(...) -> print colored/bold text on linux/win32
+ py.io.saferepr(obj) -> return limited representation string
+
+- expose test outcome related exceptions as py.test.skip.Exception,
+ py.test.raises.Exception etc., useful mostly for plugins
+ doing special outcome interpretation/tweaking
+
+- (issue85) fix junitxml plugin to handle tests with non-ascii output
+
+- fix/refine python3 compatibility (thanks Benjamin Peterson)
+
+- fixes for making the jython/win32 combination work, note however:
+ jython2.5.1/win32 does not provide a command line launcher, see
+ http://bugs.jython.org/issue1491 . See pylib install documentation
+ for how to work around.
+
+- fixes for handling of unicode exception values and unprintable objects
+
+- (issue87) fix unboundlocal error in assertionold code
+
+- (issue86) improve documentation for looponfailing
+
+- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method
+
+- ship distribute_setup.py version 0.6.10
+
+- added links to the new capturelog and coverage plugins
+
+
+Changes between 1.2.1 and 1.2.0
+=====================================
+
+- refined usage and options for "py.cleanup"::
+
+ py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
+ py.cleanup -e .swp -e .cache # also remove files with these extensions
+ py.cleanup -s # remove "build" and "dist" directory next to setup.py files
+ py.cleanup -d # also remove empty directories
+ py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
+ py.cleanup -n # dry run, only show what would be removed
+
+- add a new option "py.test --funcargs" which shows available funcargs
+ and their help strings (docstrings on their respective factory function)
+ for a given test path
+
+- display a short and concise traceback if a funcarg lookup fails
+
+- early-load "conftest.py" files in non-dot first-level sub directories.
+ allows to conveniently keep and access test-related options in a ``test``
+ subdir and still add command line options.
+
+- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
+
+- fix issue78: always call python-level teardown functions even if the
+ according setup failed. This includes refinements for calling setup_module/class functions
+ which will now only be called once instead of the previous behaviour where they'd be called
+ multiple times if they raise an exception (including a Skipped exception). Any exception
+ will be re-corded and associated with all tests in the according module/class scope.
+
+- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
+
+- fix pdb debugging to be in the correct frame on raises-related errors
+
+- update apipkg.py to fix an issue where recursive imports might
+ unnecessarily break importing
+
+- fix plugin links
+
+Changes between 1.2 and 1.1.1
+=====================================
+
+- moved dist/looponfailing from py.test core into a new
+ separately released pytest-xdist plugin.
+
+- new junitxml plugin: --junitxml=path will generate a junit style xml file
+ which is processable e.g. by the Hudson CI system.
+
+- new option: --genscript=path will generate a standalone py.test script
+ which will not need any libraries installed. thanks to Ralf Schmitt.
+
+- new option: --ignore will prevent specified path from collection.
+ Can be specified multiple times.
+
+- new option: --confcutdir=dir will make py.test only consider conftest
+ files that are relative to the specified dir.
+
+- new funcarg: "pytestconfig" is the pytest config object for access
+ to command line args and can now be easily used in a test.
+
+- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to
+ disambiguate between Python3, python2.X, Jython and PyPy installed versions.
+
+- new "pytestconfig" funcarg allows access to test config object
+
+- new "pytest_report_header" hook can return additional lines
+ to be displayed at the header of a test run.
+
+- (experimental) allow "py.test path::name1::name2::..." for pointing
+ to a test within a test collection directly. This might eventually
+ evolve as a full substitute to "-k" specifications.
+
+- streamlined plugin loading: order is now as documented in
+ customize.html: setuptools, ENV, commandline, conftest.
+ also setuptools entry point names are turned to canonical namees ("pytest_*")
+
+- automatically skip tests that need 'capfd' but have no os.dup
+
+- allow pytest_generate_tests to be defined in classes as well
+
+- deprecate usage of 'disabled' attribute in favour of pytestmark
+- deprecate definition of Directory, Module, Class and Function nodes
+ in conftest.py files. Use pytest collect hooks instead.
+
+- collection/item node specific runtest/collect hooks are only called exactly
+ on matching conftest.py files, i.e. ones which are exactly below
+ the filesystem path of an item
+
+- change: the first pytest_collect_directory hook to return something
+ will now prevent further hooks to be called.
+
+- change: figleaf plugin now requires --figleaf to run. Also
+ change its long command line options to be a bit shorter (see py.test -h).
+
+- change: pytest doctest plugin is now enabled by default and has a
+ new option --doctest-glob to set a pattern for file matches.
+
+- change: remove internal py._* helper vars, only keep py._pydir
+
+- robustify capturing to survive if custom pytest_runtest_setup
+ code failed and prevented the capturing setup code from running.
+
+- make py.test.* helpers provided by default plugins visible early -
+ works transparently both for pydoc and for interactive sessions
+ which will regularly see e.g. py.test.mark and py.test.importorskip.
+
+- simplify internal plugin manager machinery
+- simplify internal collection tree by introducing a RootCollector node
+
+- fix assert reinterpreation that sees a call containing "keyword=..."
+
+- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
+ hooks on slaves during dist-testing, report module/session teardown
+ hooks correctly.
+
+- fix issue65: properly handle dist-testing if no
+ execnet/py lib installed remotely.
+
+- skip some install-tests if no execnet is available
+
+- fix docs, fix internal bin/ script generation
+
+
+Changes between 1.1.1 and 1.1.0
+=====================================
+
+- introduce automatic plugin registration via 'pytest11'
+ entrypoints via setuptools' pkg_resources.iter_entry_points
+
+- fix py.test dist-testing to work with execnet >= 1.0.0b4
+
+- re-introduce py.test.cmdline.main() for better backward compatibility
+
+- svn paths: fix a bug with path.check(versioned=True) for svn paths,
+ allow '%' in svn paths, make svnwc.update() default to interactive mode
+ like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
+
+- refine distributed tarball to contain test and no pyc files
+
+- try harder to have deprecation warnings for py.compat.* accesses
+ report a correct location
+
+Changes between 1.1.0 and 1.0.2
+=====================================
+
+* adjust and improve docs
+
+* remove py.rest tool and internal namespace - it was
+ never really advertised and can still be used with
+ the old release if needed. If there is interest
+ it could be revived into its own tool i guess.
+
+* fix issue48 and issue59: raise an Error if the module
+ from an imported test file does not seem to come from
+ the filepath - avoids "same-name" confusion that has
+ been reported repeatedly
+
+* merged Ronny's nose-compatibility hacks: now
+ nose-style setup_module() and setup() functions are
+ supported
+
+* introduce generalized py.test.mark function marking
+
+* reshuffle / refine command line grouping
+
+* deprecate parser.addgroup in favour of getgroup which creates option group
+
+* add --report command line option that allows to control showing of skipped/xfailed sections
+
+* generalized skipping: a new way to mark python functions with skipif or xfail
+ at function, class and modules level based on platform or sys-module attributes.
+
+* extend py.test.mark decorator to allow for positional args
+
+* introduce and test "py.cleanup -d" to remove empty directories
+
+* fix issue #59 - robustify unittest test collection
+
+* make bpython/help interaction work by adding an __all__ attribute
+ to ApiModule, cleanup initpkg
+
+* use MIT license for pylib, add some contributors
+
+* remove py.execnet code and substitute all usages with 'execnet' proper
+
+* fix issue50 - cached_setup now caches more to expectations
+ for test functions with multiple arguments.
+
+* merge Jarko's fixes, issue #45 and #46
+
+* add the ability to specify a path for py.lookup to search in
+
+* fix a funcarg cached_setup bug probably only occuring
+ in distributed testing and "module" scope with teardown.
+
+* many fixes and changes for making the code base python3 compatible,
+ many thanks to Benjamin Peterson for helping with this.
+
+* consolidate builtins implementation to be compatible with >=2.3,
+ add helpers to ease keeping 2 and 3k compatible code
+
+* deprecate py.compat.doctest|subprocess|textwrap|optparse
+
+* deprecate py.magic.autopath, remove py/magic directory
+
+* move pytest assertion handling to py/code and a pytest_assertion
+ plugin, add "--no-assert" option, deprecate py.magic namespaces
+ in favour of (less) py.code ones.
+
+* consolidate and cleanup py/code classes and files
+
+* cleanup py/misc, move tests to bin-for-dist
+
+* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
+
+* consolidate py.log implementation, remove old approach.
+
+* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
+ text/unicode and byte-streams (uses underlying standard lib io.*
+ if available)
+
+* make py.unittest_convert helper script available which converts "unittest.py"
+ style files into the simpler assert/direct-test-classes py.test/nosetests
+ style. The script was written by Laura Creighton.
+
+* simplified internal localpath implementation
+
+Changes between 1.0.1 and 1.0.2
+=====================================
+
+* fixing packaging issues, triggered by fedora redhat packaging,
+ also added doc, examples and contrib dirs to the tarball.
+
+* added a documentation link to the new django plugin.
+
+Changes between 1.0.0 and 1.0.1
+=====================================
+
+* added a 'pytest_nose' plugin which handles nose.SkipTest,
+ nose-style function/method/generator setup/teardown and
+ tries to report functions correctly.
+
+* capturing of unicode writes or encoded strings to sys.stdout/err
+ work better, also terminalwriting was adapted and somewhat
+ unified between windows and linux.
+
+* improved documentation layout and content a lot
+
+* added a "--help-config" option to show conftest.py / ENV-var names for
+ all longopt cmdline options, and some special conftest.py variables.
+ renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
+
+* fix issue #27: better reporting on non-collectable items given on commandline
+ (e.g. pyc files)
+
+* fix issue #33: added --version flag (thanks Benjamin Peterson)
+
+* fix issue #32: adding support for "incomplete" paths to wcpath.status()
+
+* "Test" prefixed classes are *not* collected by default anymore if they
+ have an __init__ method
+
+* monkeypatch setenv() now accepts a "prepend" parameter
+
+* improved reporting of collection error tracebacks
+
+* simplified multicall mechanism and plugin architecture,
+ renamed some internal methods and argnames
+
+Changes between 1.0.0b9 and 1.0.0
+=====================================
+
+* more terse reporting try to show filesystem path relatively to current dir
+* improve xfail output a bit
+
+Changes between 1.0.0b8 and 1.0.0b9
+=====================================
+
+* cleanly handle and report final teardown of test setup
+
+* fix svn-1.6 compat issue with py.path.svnwc().versioned()
+ (thanks Wouter Vanden Hove)
+
+* setup/teardown or collection problems now show as ERRORs
+ or with big "E"'s in the progress lines. they are reported
+ and counted separately.
+
+* dist-testing: properly handle test items that get locally
+ collected but cannot be collected on the remote side - often
+ due to platform/dependency reasons
+
+* simplified py.test.mark API - see keyword plugin documentation
+
+* integrate better with logging: capturing now by default captures
+ test functions and their immediate setup/teardown in a single stream
+
+* capsys and capfd funcargs now have a readouterr() and a close() method
+ (underlyingly py.io.StdCapture/FD objects are used which grew a
+ readouterr() method as well to return snapshots of captured out/err)
+
+* make assert-reinterpretation work better with comparisons not
+ returning bools (reported with numpy from thanks maciej fijalkowski)
+
+* reworked per-test output capturing into the pytest_iocapture.py plugin
+ and thus removed capturing code from config object
+
+* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
+
+
+Changes between 1.0.0b7 and 1.0.0b8
+=====================================
+
+* pytest_unittest-plugin is now enabled by default
+
+* introduced pytest_keyboardinterrupt hook and
+ refined pytest_sessionfinish hooked, added tests.
+
+* workaround a buggy logging module interaction ("closing already closed
+ files"). Thanks to Sridhar Ratnakumar for triggering.
+
+* if plugins use "py.test.importorskip" for importing
+ a dependency only a warning will be issued instead
+ of exiting the testing process.
+
+* many improvements to docs:
+ - refined funcargs doc , use the term "factory" instead of "provider"
+ - added a new talk/tutorial doc page
+ - better download page
+ - better plugin docstrings
+ - added new plugins page and automatic doc generation script
+
+* fixed teardown problem related to partially failing funcarg setups
+ (thanks MrTopf for reporting), "pytest_runtest_teardown" is now
+ always invoked even if the "pytest_runtest_setup" failed.
+
+* tweaked doctest output for docstrings in py modules,
+ thanks Radomir.
+
+Changes between 1.0.0b3 and 1.0.0b7
+=============================================
+
+* renamed py.test.xfail back to py.test.mark.xfail to avoid
+ two ways to decorate for xfail
+
+* re-added py.test.mark decorator for setting keywords on functions
+ (it was actually documented so removing it was not nice)
+
+* remove scope-argument from request.addfinalizer() because
+ request.cached_setup has the scope arg. TOOWTDI.
+
+* perform setup finalization before reporting failures
+
+* apply modified patches from Andreas Kloeckner to allow
+ test functions to have no func_code (#22) and to make
+ "-k" and function keywords work (#20)
+
+* apply patch from Daniel Peolzleithner (issue #23)
+
+* resolve issue #18, multiprocessing.Manager() and
+ redirection clash
+
+* make __name__ == "__channelexec__" for remote_exec code
+
+Changes between 1.0.0b1 and 1.0.0b3
+=============================================
+
+* plugin classes are removed: one now defines
+ hooks directly in conftest.py or global pytest_*.py
+ files.
+
+* added new pytest_namespace(config) hook that allows
+ to inject helpers directly to the py.test.* namespace.
+
+* documented and refined many hooks
+
+* added new style of generative tests via
+ pytest_generate_tests hook that integrates
+ well with function arguments.
+
+
+Changes between 0.9.2 and 1.0.0b1
+=============================================
+
+* introduced new "funcarg" setup method,
+ see doc/test/funcarg.txt
+
+* introduced plugin architecuture and many
+ new py.test plugins, see
+ doc/test/plugins.txt
+
+* teardown_method is now guaranteed to get
+ called after a test method has run.
+
+* new method: py.test.importorskip(mod,minversion)
+ will either import or call py.test.skip()
+
+* completely revised internal py.test architecture
+
+* new py.process.ForkedFunc object allowing to
+ fork execution of a function to a sub process
+ and getting a result back.
+
+XXX lots of things missing here XXX
+
+Changes between 0.9.1 and 0.9.2
+===============================
+
+* refined installation and metadata, created new setup.py,
+ now based on setuptools/ez_setup (thanks to Ralf Schmitt
+ for his support).
+
+* improved the way of making py.* scripts available in
+ windows environments, they are now added to the
+ Scripts directory as ".cmd" files.
+
+* py.path.svnwc.status() now is more complete and
+ uses xml output from the 'svn' command if available
+ (Guido Wesdorp)
+
+* fix for py.path.svn* to work with svn 1.5
+ (Chris Lamb)
+
+* fix path.relto(otherpath) method on windows to
+ use normcase for checking if a path is relative.
+
+* py.test's traceback is better parseable from editors
+ (follows the filenames:LINENO: MSG convention)
+ (thanks to Osmo Salomaa)
+
+* fix to javascript-generation, "py.test --runbrowser"
+ should work more reliably now
+
+* removed previously accidentally added
+ py.test.broken and py.test.notimplemented helpers.
+
+* there now is a py.__version__ attribute
+
+Changes between 0.9.0 and 0.9.1
+===============================
+
+This is a fairly complete list of changes between 0.9 and 0.9.1, which can
+serve as a reference for developers.
+
+* allowing + signs in py.path.svn urls [39106]
+* fixed support for Failed exceptions without excinfo in py.test [39340]
+* added support for killing processes for Windows (as well as platforms that
+ support os.kill) in py.misc.killproc [39655]
+* added setup/teardown for generative tests to py.test [40702]
+* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739]
+* fixed problem with calling .remove() on wcpaths of non-versioned files in
+ py.path [44248]
+* fixed some import and inheritance issues in py.test [41480, 44648, 44655]
+* fail to run greenlet tests when pypy is available, but without stackless
+ [45294]
+* small fixes in rsession tests [45295]
+* fixed issue with 2.5 type representations in py.test [45483, 45484]
+* made that internal reporting issues displaying is done atomically in py.test
+ [45518]
+* made that non-existing files are igored by the py.lookup script [45519]
+* improved exception name creation in py.test [45535]
+* made that less threads are used in execnet [merge in 45539]
+* removed lock required for atomical reporting issue displaying in py.test
+ [45545]
+* removed globals from execnet [45541, 45547]
+* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit
+ get called in 2.5 (py.execnet) [45548]
+* fixed bug in joining threads in py.execnet's servemain [45549]
+* refactored py.test.rsession tests to not rely on exact output format anymore
+ [45646]
+* using repr() on test outcome [45647]
+* added 'Reason' classes for py.test.skip() [45648, 45649]
+* killed some unnecessary sanity check in py.test.collect [45655]
+* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only
+ usable by Administrators [45901]
+* added support for locking and non-recursive commits to py.path.svnwc [45994]
+* locking files in py.execnet to prevent CPython from segfaulting [46010]
+* added export() method to py.path.svnurl
+* fixed -d -x in py.test [47277]
+* fixed argument concatenation problem in py.path.svnwc [49423]
+* restore py.test behaviour that it exits with code 1 when there are failures
+ [49974]
+* don't fail on html files that don't have an accompanying .txt file [50606]
+* fixed 'utestconvert.py < input' [50645]
+* small fix for code indentation in py.code.source [50755]
+* fix _docgen.py documentation building [51285]
+* improved checks for source representation of code blocks in py.test [51292]
+* added support for passing authentication to py.path.svn* objects [52000,
+ 52001]
+* removed sorted() call for py.apigen tests in favour of [].sort() to support
+ Python 2.3 [52481]
diff --git a/tests/wpt/web-platform-tests/tools/py/LICENSE b/tests/wpt/web-platform-tests/tools/third_party/py/LICENSE
index 31ecdfb1dbc..31ecdfb1dbc 100644
--- a/tests/wpt/web-platform-tests/tools/py/LICENSE
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/LICENSE
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/MANIFEST.in b/tests/wpt/web-platform-tests/tools/third_party/py/MANIFEST.in
new file mode 100644
index 00000000000..239ad2283e1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/MANIFEST.in
@@ -0,0 +1,10 @@
+include CHANGELOG
+include AUTHORS
+include README.rst
+include setup.py
+include LICENSE
+include conftest.py
+include tox.ini
+graft doc
+graft testing
+global-exclude *.pyc
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/README.rst b/tests/wpt/web-platform-tests/tools/third_party/py/README.rst
new file mode 100644
index 00000000000..7092ae4c460
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/README.rst
@@ -0,0 +1,34 @@
+.. image:: https://img.shields.io/pypi/v/py.svg
+ :target: https://pypi.org/project/py
+
+.. image:: https://anaconda.org/conda-forge/py/badges/version.svg
+ :target: https://anaconda.org/conda-forge/py
+
+.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
+ :target: https://pypi.org/project/py
+
+.. image:: https://img.shields.io/travis/pytest-dev/py.svg
+ :target: https://travis-ci.org/pytest-dev/py
+
+.. image:: https://ci.appveyor.com/api/projects/status/10keglan6uqwj5al/branch/master?svg=true
+ :target: https://ci.appveyor.com/project/pytestbot/py
+
+
+**NOTE**: this library is in **maintenance mode** and should not be used in new code.
+
+The py lib is a Python development support library featuring
+the following tools and modules:
+
+* ``py.path``: uniform local and svn path objects
+* ``py.apipkg``: explicit API control and lazy-importing
+* ``py.iniconfig``: easy parsing of .ini files
+* ``py.code``: dynamic code generation and introspection (deprecated, moved to ``pytest``).
+
+**NOTE**: prior to the 1.4 release this distribution used to
+contain py.test which is now its own package, see http://pytest.org
+
+For questions and more information please visit http://py.readthedocs.org
+
+Bugs and issues: https://github.com/pytest-dev/py
+
+Authors: Holger Krekel and others, 2004-2017
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/appveyor.yml b/tests/wpt/web-platform-tests/tools/third_party/py/appveyor.yml
new file mode 100644
index 00000000000..5fbeca9ab65
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/appveyor.yml
@@ -0,0 +1,26 @@
+environment:
+ matrix:
+ # note: please use "tox --listenvs" to populate the build matrix below
+ - TOXENV: "py27-pytest29"
+ - TOXENV: "py27-pytest30"
+ - TOXENV: "py27-pytest31"
+ - TOXENV: "py34-pytest29"
+ - TOXENV: "py34-pytest30"
+ - TOXENV: "py34-pytest31"
+ - TOXENV: "py35-pytest29"
+ - TOXENV: "py35-pytest30"
+ - TOXENV: "py35-pytest31"
+ - TOXENV: "py36-pytest29"
+ - TOXENV: "py36-pytest30"
+ - TOXENV: "py36-pytest31"
+
+install:
+ - echo Installed Pythons
+ - dir c:\Python*
+
+ - C:\Python36\python -m pip install --upgrade --pre tox
+
+build: false # Not a C# project, build stuff at the test step instead.
+
+test_script:
+ - C:\Python36\python -m tox
diff --git a/tests/wpt/web-platform-tests/tools/py/bench/localpath.py b/tests/wpt/web-platform-tests/tools/third_party/py/bench/localpath.py
index ad4fbd8e2bf..ad4fbd8e2bf 100644
--- a/tests/wpt/web-platform-tests/tools/py/bench/localpath.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/bench/localpath.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/py/conftest.py
new file mode 100644
index 00000000000..5bff3fe0224
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/conftest.py
@@ -0,0 +1,60 @@
+import py
+import pytest
+import sys
+
+pytest_plugins = 'doctest', 'pytester'
+
+collect_ignore = ['build', 'doc/_build']
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("pylib", "py lib testing options")
+ group.addoption('--runslowtests',
+ action="store_true", dest="runslowtests", default=False,
+ help=("run slow tests"))
+
+@pytest.fixture
+def sshhost(request):
+ val = request.config.getvalue("sshhost")
+ if val:
+ return val
+ py.test.skip("need --sshhost option")
+
+
+# XXX copied from execnet's conftest.py - needs to be merged
+winpymap = {
+ 'python2.7': r'C:\Python27\python.exe',
+}
+
+
+def getexecutable(name, cache={}):
+ try:
+ return cache[name]
+ except KeyError:
+ executable = py.path.local.sysfind(name)
+ if executable:
+ if name == "jython":
+ import subprocess
+ popen = subprocess.Popen(
+ [str(executable), "--version"],
+ universal_newlines=True, stderr=subprocess.PIPE)
+ out, err = popen.communicate()
+ if not err or "2.5" not in err:
+ executable = None
+ cache[name] = executable
+ return executable
+
+
+@pytest.fixture(params=('python2.7', 'pypy-c', 'jython'))
+def anypython(request):
+ name = request.param
+ executable = getexecutable(name)
+ if executable is None:
+ if sys.platform == "win32":
+ executable = winpymap.get(name, None)
+ if executable:
+ executable = py.path.local(executable)
+ if executable.check():
+ return executable
+ py.test.skip("no %s found" % (name,))
+ return executable
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/Makefile b/tests/wpt/web-platform-tests/tools/third_party/py/doc/Makefile
index 0a0e89e01fe..0a0e89e01fe 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/Makefile
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/Makefile
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/_templates/layout.html b/tests/wpt/web-platform-tests/tools/third_party/py/doc/_templates/layout.html
index 683863aa460..683863aa460 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/_templates/layout.html
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/_templates/layout.html
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-0.9.0.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-0.9.0.txt
index 07109313543..07109313543 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-0.9.0.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-0.9.0.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-0.9.2.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-0.9.2.txt
index bc2d2ef2908..bc2d2ef2908 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-0.9.2.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-0.9.2.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.0.0.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.0.0.txt
index 7024255a195..7024255a195 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.0.0.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.0.0.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.0.1.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.0.1.txt
index 0c9f8760bdb..0c9f8760bdb 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.0.1.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.0.1.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.0.2.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.0.2.txt
index 23546195353..23546195353 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.0.2.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.0.2.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.1.0.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.1.0.txt
index 0441c3215eb..0441c3215eb 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.1.0.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.1.0.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.1.1.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.1.1.txt
index 83e6a1fd8d9..83e6a1fd8d9 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.1.1.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.1.1.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.2.0.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.2.0.txt
index 4f6a5614476..4f6a5614476 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.2.0.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.2.0.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.2.1.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.2.1.txt
index 5bf8ba22dc6..5bf8ba22dc6 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.2.1.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.2.1.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.0.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.0.txt
index cf97db0367a..cf97db0367a 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.0.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.0.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.1.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.1.txt
index 471de408a10..471de408a10 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.1.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.1.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.2.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.2.txt
index 599dfbed755..599dfbed755 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.2.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.2.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.3.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.3.txt
index c62cb859053..c62cb859053 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.3.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.3.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.4.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.4.txt
index c156c8bdb33..c156c8bdb33 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.3.4.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.3.4.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.4.0.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.4.0.txt
index 6f9a7714d9f..6f9a7714d9f 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.4.0.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.4.0.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.4.1.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.4.1.txt
index a5aa76b1438..a5aa76b1438 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/release-1.4.1.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/release-1.4.1.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/announce/releases.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/releases.txt
index 309c29bac5d..309c29bac5d 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/announce/releases.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/announce/releases.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/changelog.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/changelog.txt
index 237daca3548..237daca3548 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/changelog.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/changelog.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/code.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/code.txt
index bdd8691da03..bdd8691da03 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/code.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/code.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/conf.py b/tests/wpt/web-platform-tests/tools/third_party/py/doc/conf.py
index de4cbf8a46f..de4cbf8a46f 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/conf.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/conf.py
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/download.html b/tests/wpt/web-platform-tests/tools/third_party/py/doc/download.html
index 5f4c466402d..5f4c466402d 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/download.html
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/download.html
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/example/genhtml.py b/tests/wpt/web-platform-tests/tools/third_party/py/doc/example/genhtml.py
index b5c8f525b62..b5c8f525b62 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/example/genhtml.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/example/genhtml.py
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/example/genhtmlcss.py b/tests/wpt/web-platform-tests/tools/third_party/py/doc/example/genhtmlcss.py
index 3e6d0af5454..3e6d0af5454 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/example/genhtmlcss.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/example/genhtmlcss.py
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/example/genxml.py b/tests/wpt/web-platform-tests/tools/third_party/py/doc/example/genxml.py
index 5f754e8897b..5f754e8897b 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/example/genxml.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/example/genxml.py
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/faq.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/faq.txt
index 52cb4b3fbd3..52cb4b3fbd3 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/faq.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/faq.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/img/pylib.png b/tests/wpt/web-platform-tests/tools/third_party/py/doc/img/pylib.png
index 2e10d438866..2e10d438866 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/img/pylib.png
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/img/pylib.png
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/doc/index.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/index.txt
new file mode 100644
index 00000000000..c700b17e987
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/index.txt
@@ -0,0 +1,39 @@
+.. py documentation master file, created by
+ sphinx-quickstart on Thu Oct 21 08:30:10 2010.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to py's documentation!
+=================================
+
+see :ref:`CHANGELOG <changelog>` for latest changes.
+
+.. _`pytest distribution`: http://pytest.org
+
+Contents:
+
+.. toctree::
+
+ install
+ path
+ code
+ io
+ log
+ xml
+ misc
+
+ :maxdepth: 2
+
+.. toctree::
+ :hidden:
+
+ announce/release-2.0.0
+ changelog
+ announce/*
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`search`
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/doc/install.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/install.txt
new file mode 100644
index 00000000000..fb4056d1fba
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/install.txt
@@ -0,0 +1,88 @@
+
+.. _`py`:
+.. _`index page`: http://pypi.python.org/pypi/py/
+
+installation info in a nutshell
+===================================================
+
+**PyPI name**: py_
+
+**Pythons**: CPython 2.7, 3.4, 3.5, 3.6, PyPy-5.4
+
+**Operating systems**: Linux, Windows, OSX, Unix
+
+**Requirements**: setuptools_ or Distribute_
+
+**Installers**: ``easy_install`` and ``pip``
+
+**hg repository**: https://bitbucket.org/hpk42/py
+
+easy install or pip ``py``
+-----------------------------
+
+Both `Distribute`_ and setuptools_ provide the ``easy_install``
+installation tool with which you can type into a command line window::
+
+ easy_install -U py
+
+to install the latest release of the py lib. The ``-U`` switch
+will trigger an upgrade if you already have an older version installed.
+
+.. note::
+
+ As of version 1.4 py does not contain py.test anymore - you
+ need to install the new `pytest`_ distribution.
+
+.. _pytest: http://pytest.org
+
+Working from version control or a tarball
+-----------------------------------------------
+
+To follow development or start experiments, checkout the
+complete code and documentation source with mercurial_::
+
+ hg clone https://bitbucket.org/hpk42/py
+
+Development takes place on the 'trunk' branch.
+
+You can also go to the python package index and
+download and unpack a TAR file::
+
+ http://pypi.python.org/pypi/py/
+
+activating a checkout with setuptools
+--------------------------------------------
+
+With a working `Distribute`_ or setuptools_ installation you can type::
+
+ python setup.py develop
+
+in order to work inline with the tools and the lib of your checkout.
+
+.. _`no-setuptools`:
+
+.. _`directly use a checkout`:
+
+.. _`setuptools`: http://pypi.python.org/pypi/setuptools
+
+
+Mailing list and issue tracker
+--------------------------------------
+
+- `py-dev developers list`_ and `commit mailing list`_.
+
+- #pylib on irc.freenode.net IRC channel for random questions.
+
+- `bitbucket issue tracker`_ use this bitbucket issue tracker to report
+ bugs or request features.
+
+.. _`bitbucket issue tracker`: http://bitbucket.org/hpk42/py/issues/
+
+.. _codespeak: http://codespeak.net/
+.. _`py-dev`:
+.. _`development mailing list`:
+.. _`py-dev developers list`: http://codespeak.net/mailman/listinfo/py-dev
+.. _`py-svn`:
+.. _`commit mailing list`: http://codespeak.net/mailman/listinfo/py-svn
+
+.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/io.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/io.txt
index c11308a6d28..c11308a6d28 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/io.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/io.txt
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/links.inc b/tests/wpt/web-platform-tests/tools/third_party/py/doc/links.inc
index 9bcfe5cf85c..9bcfe5cf85c 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/links.inc
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/links.inc
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/log.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/log.txt
index ca60fcac250..ca60fcac250 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/log.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/log.txt
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/doc/misc.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/misc.txt
new file mode 100644
index 00000000000..4b453482757
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/misc.txt
@@ -0,0 +1,93 @@
+====================================
+Miscellaneous features of the py lib
+====================================
+
+Mapping the standard python library into py
+===========================================
+
+The ``py.std`` object allows lazy access to
+standard library modules. For example, to get to the print-exception
+functionality of the standard library you can write::
+
+ py.std.traceback.print_exc()
+
+without having to do anything else than the usual ``import py``
+at the beginning. You can access any other top-level standard
+library module this way. This means that you will only trigger
+imports of modules that are actually needed. Note that no attempt
+is made to import submodules.
+
+Support for interaction with system utilities/binaries
+======================================================
+
+Currently, the py lib offers two ways to interact with
+system executables. ``py.process.cmdexec()`` invokes
+the shell in order to execute a string. The other
+one, ``py.path.local``'s 'sysexec()' method lets you
+directly execute a binary.
+
+Both approaches will raise an exception in case of a return-
+code other than 0 and otherwise return the stdout-output
+of the child process.
+
+The shell based approach
+------------------------
+
+You can execute a command via your system shell
+by doing something like::
+
+ out = py.process.cmdexec('ls -v')
+
+However, the ``cmdexec`` approach has a few shortcomings:
+
+- it relies on the underlying system shell
+- it neccessitates shell-escaping for expressing arguments
+- it does not easily allow to "fix" the binary you want to run.
+- it only allows to execute executables from the local
+ filesystem
+
+.. _sysexec:
+
+local paths have ``sysexec``
+----------------------------
+
+In order to synchronously execute an executable file you
+can use ``sysexec``::
+
+ binsvn.sysexec('ls', 'http://codespeak.net/svn')
+
+where ``binsvn`` is a path that points to the ``svn`` commandline
+binary. Note that this function does not offer any shell-escaping
+so you have to pass in already separated arguments.
+
+finding an executable local path
+--------------------------------
+
+Finding an executable is quite different on multiple platforms.
+Currently, the ``PATH`` environment variable based search on
+unix platforms is supported::
+
+ py.path.local.sysfind('svn')
+
+which returns the first path whose ``basename`` matches ``svn``.
+In principle, `sysfind` deploys platform specific algorithms
+to perform the search. On Windows, for example, it may look
+at the registry (XXX).
+
+To make the story complete, we allow to pass in a second ``checker``
+argument that is called for each found executable. For example, if
+you have multiple binaries available you may want to select the
+right version::
+
+ def mysvn(p):
+ """ check that the given svn binary has version 1.1. """
+ line = p.execute('--version'').readlines()[0]
+ if line.find('version 1.1'):
+ return p
+ binsvn = py.path.local.sysfind('svn', checker=mysvn)
+
+
+Cross-Python Version compatibility helpers
+=============================================
+
+The ``py.builtin`` namespace provides a number of helpers that help to write python code compatible across Python interpreters, mainly Python2 and Python3. Type ``help(py.builtin)`` on a Python prompt for the selection of builtins.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/doc/path.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/path.txt
new file mode 100644
index 00000000000..c906179099a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/path.txt
@@ -0,0 +1,258 @@
+=======
+py.path
+=======
+
+The 'py' lib provides a uniform high-level api to deal with filesystems
+and filesystem-like interfaces: ``py.path``. It aims to offer a central
+object to fs-like object trees (reading from and writing to files, adding
+files/directories, examining the types and structure, etc.), and out-of-the-box
+provides a number of implementations of this API.
+
+py.path.local - local file system path
+===============================================
+
+.. _`local`:
+
+basic interactive example
+-------------------------------------
+
+The first and most obvious of the implementations is a wrapper around a local
+filesystem. It's just a bit nicer in usage than the regular Python APIs, and
+of course all the functionality is bundled together rather than spread over a
+number of modules.
+
+
+.. sourcecode:: pycon
+
+ >>> import py
+ >>> temppath = py.path.local('py.path_documentation')
+ >>> foopath = temppath.join('foo') # get child 'foo' (lazily)
+ >>> foopath.check() # check if child 'foo' exists
+ False
+ >>> foopath.write('bar') # write some data to it
+ >>> foopath.check()
+ True
+ >>> foopath.read()
+ 'bar'
+ >>> foofile = foopath.open() # return a 'real' file object
+ >>> foofile.read(1)
+ 'b'
+
+reference documentation
+---------------------------------
+
+.. autoclass:: py._path.local.LocalPath
+ :members:
+ :inherited-members:
+
+``py.path.svnurl`` and ``py.path.svnwc``
+==================================================
+
+Two other ``py.path`` implementations that the py lib provides wrap the
+popular `Subversion`_ revision control system: the first (called 'svnurl')
+by interfacing with a remote server, the second by wrapping a local checkout.
+Both allow you to access relatively advanced features such as metadata and
+versioning, and both in a way more user-friendly manner than existing other
+solutions.
+
+Some example usage of ``py.path.svnurl``:
+
+.. sourcecode:: pycon
+
+ .. >>> import py
+ .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
+ >>> url = py.path.svnurl('http://codespeak.net/svn/py')
+ >>> info = url.info()
+ >>> info.kind
+ 'dir'
+ >>> firstentry = url.log()[-1]
+ >>> import time
+ >>> time.strftime('%Y-%m-%d', time.gmtime(firstentry.date))
+ '2004-10-02'
+
+Example usage of ``py.path.svnwc``:
+
+.. sourcecode:: pycon
+
+ .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
+ >>> temp = py.path.local('py.path_documentation')
+ >>> wc = py.path.svnwc(temp.join('svnwc'))
+ >>> wc.checkout('http://codespeak.net/svn/py/dist/py/path/local')
+ >>> wc.join('local.py').check()
+ True
+
+.. _`Subversion`: http://subversion.tigris.org/
+
+svn path related API reference
+-----------------------------------------
+
+.. autoclass:: py._path.svnwc.SvnWCCommandPath
+ :members:
+ :inherited-members:
+
+.. autoclass:: py._path.svnurl.SvnCommandPath
+ :members:
+ :inherited-members:
+
+.. autoclass:: py._path.svnwc.SvnAuth
+ :members:
+ :inherited-members:
+
+Common vs. specific API, Examples
+========================================
+
+All Path objects support a common set of operations, suitable
+for many use cases and allowing to transparently switch the
+path object within an application (e.g. from "local" to "svnwc").
+The common set includes functions such as `path.read()` to read all data
+from a file, `path.write()` to write data, `path.listdir()` to get a list
+of directory entries, `path.check()` to check if a node exists
+and is of a particular type, `path.join()` to get
+to a (grand)child, `path.visit()` to recursively walk through a node's
+children, etc. Only things that are not common on 'normal' filesystems (yet),
+such as handling metadata (e.g. the Subversion "properties") require
+using specific APIs.
+
+A quick 'cookbook' of small examples that will be useful 'in real life',
+which also presents parts of the 'common' API, and shows some non-common
+methods:
+
+Searching `.txt` files
+--------------------------------
+
+Search for a particular string inside all files with a .txt extension in a
+specific directory.
+
+.. sourcecode:: pycon
+
+ >>> dirpath = temppath.ensure('testdir', dir=True)
+ >>> dirpath.join('textfile1.txt').write('foo bar baz')
+ >>> dirpath.join('textfile2.txt').write('frob bar spam eggs')
+ >>> subdir = dirpath.ensure('subdir', dir=True)
+ >>> subdir.join('textfile1.txt').write('foo baz')
+ >>> subdir.join('textfile2.txt').write('spam eggs spam foo bar spam')
+ >>> results = []
+ >>> for fpath in dirpath.visit('*.txt'):
+ ... if 'bar' in fpath.read():
+ ... results.append(fpath.basename)
+ >>> results.sort()
+ >>> results
+ ['textfile1.txt', 'textfile2.txt', 'textfile2.txt']
+
+Working with Paths
+----------------------------
+
+This example shows the ``py.path`` features to deal with
+filesystem paths Note that the filesystem is never touched,
+all operations are performed on a string level (so the paths
+don't have to exist, either):
+
+.. sourcecode:: pycon
+
+ >>> p1 = py.path.local('/foo/bar')
+ >>> p2 = p1.join('baz/qux')
+ >>> p2 == py.path.local('/foo/bar/baz/qux')
+ True
+ >>> sep = py.path.local.sep
+ >>> p2.relto(p1).replace(sep, '/') # os-specific path sep in the string
+ 'baz/qux'
+ >>> p2.bestrelpath(p1).replace(sep, '/')
+ '../..'
+ >>> p2.join(p2.bestrelpath(p1)) == p1
+ True
+ >>> p3 = p1 / 'baz/qux' # the / operator allows joining, too
+ >>> p2 == p3
+ True
+ >>> p4 = p1 + ".py"
+ >>> p4.basename == "bar.py"
+ True
+ >>> p4.ext == ".py"
+ True
+ >>> p4.purebasename == "bar"
+ True
+
+This should be possible on every implementation of ``py.path``, so
+regardless of whether the implementation wraps a UNIX filesystem, a Windows
+one, or a database or object tree, these functions should be available (each
+with their own notion of path seperators and dealing with conversions, etc.).
+
+Checking path types
+-------------------------------
+
+Now we will show a bit about the powerful 'check()' method on paths, which
+allows you to check whether a file exists, what type it is, etc.:
+
+.. sourcecode:: pycon
+
+ >>> file1 = temppath.join('file1')
+ >>> file1.check() # does it exist?
+ False
+ >>> file1 = file1.ensure(file=True) # 'touch' the file
+ >>> file1.check()
+ True
+ >>> file1.check(dir=True) # is it a dir?
+ False
+ >>> file1.check(file=True) # or a file?
+ True
+ >>> file1.check(ext='.txt') # check the extension
+ False
+ >>> textfile = temppath.ensure('text.txt', file=True)
+ >>> textfile.check(ext='.txt')
+ True
+ >>> file1.check(basename='file1') # we can use all the path's properties here
+ True
+
+Setting svn-properties
+--------------------------------
+
+As an example of 'uncommon' methods, we'll show how to read and write
+properties in an ``py.path.svnwc`` instance:
+
+.. sourcecode:: pycon
+
+ .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
+ >>> wc.propget('foo')
+ ''
+ >>> wc.propset('foo', 'bar')
+ >>> wc.propget('foo')
+ 'bar'
+ >>> len(wc.status().prop_modified) # our own props
+ 1
+ >>> msg = wc.revert() # roll back our changes
+ >>> len(wc.status().prop_modified)
+ 0
+
+SVN authentication
+----------------------------
+
+Some uncommon functionality can also be provided as extensions, such as SVN
+authentication:
+
+.. sourcecode:: pycon
+
+ .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
+ >>> auth = py.path.SvnAuth('anonymous', 'user', cache_auth=False,
+ ... interactive=False)
+ >>> wc.auth = auth
+ >>> wc.update() # this should work
+ >>> path = wc.ensure('thisshouldnotexist.txt')
+ >>> try:
+ ... path.commit('testing')
+ ... except py.process.cmdexec.Error, e:
+ ... pass
+ >>> 'authorization failed' in str(e)
+ True
+
+Known problems / limitations
+===================================
+
+* The SVN path objects require the "svn" command line,
+ there is currently no support for python bindings.
+ Parsing the svn output can lead to problems, particularly
+ regarding if you have a non-english "locales" setting.
+
+* While the path objects basically work on windows,
+ there is no attention yet on making unicode paths
+ work or deal with the famous "8.3" filename issues.
+
+
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/style.css b/tests/wpt/web-platform-tests/tools/third_party/py/doc/style.css
index 1faf762c715..1faf762c715 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/style.css
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/style.css
diff --git a/tests/wpt/web-platform-tests/tools/py/doc/xml.txt b/tests/wpt/web-platform-tests/tools/third_party/py/doc/xml.txt
index 1022de6e912..1022de6e912 100644
--- a/tests/wpt/web-platform-tests/tools/py/doc/xml.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/doc/xml.txt
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/__init__.py
new file mode 100644
index 00000000000..b5e0c1630c7
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/__init__.py
@@ -0,0 +1,154 @@
+"""
+pylib: rapid testing and development utils
+
+this module uses apipkg.py for lazy-loading sub modules
+and classes. The initpkg-dictionary below specifies
+name->value mappings where value can be another namespace
+dictionary or an import path.
+
+(c) Holger Krekel and others, 2004-2014
+"""
+__version__ = '1.5.2'
+
+try:
+ from py._vendored_packages import apipkg
+ lib_not_mangled_by_packagers = True
+ vendor_prefix = '._vendored_packages.'
+except ImportError:
+ import apipkg
+ lib_not_mangled_by_packagers = False
+ vendor_prefix = ''
+
+# so that py.error.* instances are picklable
+import sys
+
+apipkg.initpkg(__name__, attr={'_apipkg': apipkg}, exportdefs={
+ # access to all standard lib modules
+ 'std': '._std:std',
+ # access to all posix errno's as classes
+ 'error': '._error:error',
+
+ '_pydir' : '.__metainfo:pydir',
+ 'version': 'py:__version__', # backward compatibility
+
+ # pytest-2.0 has a flat namespace, we use alias modules
+ # to keep old references compatible
+ 'test' : 'pytest',
+
+ # hook into the top-level standard library
+ 'process' : {
+ '__doc__' : '._process:__doc__',
+ 'cmdexec' : '._process.cmdexec:cmdexec',
+ 'kill' : '._process.killproc:kill',
+ 'ForkedFunc' : '._process.forkedfunc:ForkedFunc',
+ },
+
+ 'apipkg' : {
+ 'initpkg' : vendor_prefix + 'apipkg:initpkg',
+ 'ApiModule' : vendor_prefix + 'apipkg:ApiModule',
+ },
+
+ 'iniconfig' : {
+ 'IniConfig' : vendor_prefix + 'iniconfig:IniConfig',
+ 'ParseError' : vendor_prefix + 'iniconfig:ParseError',
+ },
+
+ 'path' : {
+ '__doc__' : '._path:__doc__',
+ 'svnwc' : '._path.svnwc:SvnWCCommandPath',
+ 'svnurl' : '._path.svnurl:SvnCommandPath',
+ 'local' : '._path.local:LocalPath',
+ 'SvnAuth' : '._path.svnwc:SvnAuth',
+ },
+
+ # python inspection/code-generation API
+ 'code' : {
+ '__doc__' : '._code:__doc__',
+ 'compile' : '._code.source:compile_',
+ 'Source' : '._code.source:Source',
+ 'Code' : '._code.code:Code',
+ 'Frame' : '._code.code:Frame',
+ 'ExceptionInfo' : '._code.code:ExceptionInfo',
+ 'Traceback' : '._code.code:Traceback',
+ 'getfslineno' : '._code.source:getfslineno',
+ 'getrawcode' : '._code.code:getrawcode',
+ 'patch_builtins' : '._code.code:patch_builtins',
+ 'unpatch_builtins' : '._code.code:unpatch_builtins',
+ '_AssertionError' : '._code.assertion:AssertionError',
+ '_reinterpret_old' : '._code.assertion:reinterpret_old',
+ '_reinterpret' : '._code.assertion:reinterpret',
+ '_reprcompare' : '._code.assertion:_reprcompare',
+ '_format_explanation' : '._code.assertion:_format_explanation',
+ },
+
+ # backports and additions of builtins
+ 'builtin' : {
+ '__doc__' : '._builtin:__doc__',
+ 'enumerate' : '._builtin:enumerate',
+ 'reversed' : '._builtin:reversed',
+ 'sorted' : '._builtin:sorted',
+ 'any' : '._builtin:any',
+ 'all' : '._builtin:all',
+ 'set' : '._builtin:set',
+ 'frozenset' : '._builtin:frozenset',
+ 'BaseException' : '._builtin:BaseException',
+ 'GeneratorExit' : '._builtin:GeneratorExit',
+ '_sysex' : '._builtin:_sysex',
+ 'print_' : '._builtin:print_',
+ '_reraise' : '._builtin:_reraise',
+ '_tryimport' : '._builtin:_tryimport',
+ 'exec_' : '._builtin:exec_',
+ '_basestring' : '._builtin:_basestring',
+ '_totext' : '._builtin:_totext',
+ '_isbytes' : '._builtin:_isbytes',
+ '_istext' : '._builtin:_istext',
+ '_getimself' : '._builtin:_getimself',
+ '_getfuncdict' : '._builtin:_getfuncdict',
+ '_getcode' : '._builtin:_getcode',
+ 'builtins' : '._builtin:builtins',
+ 'execfile' : '._builtin:execfile',
+ 'callable' : '._builtin:callable',
+ 'bytes' : '._builtin:bytes',
+ 'text' : '._builtin:text',
+ },
+
+ # input-output helping
+ 'io' : {
+ '__doc__' : '._io:__doc__',
+ 'dupfile' : '._io.capture:dupfile',
+ 'TextIO' : '._io.capture:TextIO',
+ 'BytesIO' : '._io.capture:BytesIO',
+ 'FDCapture' : '._io.capture:FDCapture',
+ 'StdCapture' : '._io.capture:StdCapture',
+ 'StdCaptureFD' : '._io.capture:StdCaptureFD',
+ 'TerminalWriter' : '._io.terminalwriter:TerminalWriter',
+ 'ansi_print' : '._io.terminalwriter:ansi_print',
+ 'get_terminal_width' : '._io.terminalwriter:get_terminal_width',
+ 'saferepr' : '._io.saferepr:saferepr',
+ },
+
+ # small and mean xml/html generation
+ 'xml' : {
+ '__doc__' : '._xmlgen:__doc__',
+ 'html' : '._xmlgen:html',
+ 'Tag' : '._xmlgen:Tag',
+ 'raw' : '._xmlgen:raw',
+ 'Namespace' : '._xmlgen:Namespace',
+ 'escape' : '._xmlgen:escape',
+ },
+
+ 'log' : {
+ # logging API ('producers' and 'consumers' connected via keywords)
+ '__doc__' : '._log:__doc__',
+ '_apiwarn' : '._log.warning:_apiwarn',
+ 'Producer' : '._log.log:Producer',
+ 'setconsumer' : '._log.log:setconsumer',
+ '_setstate' : '._log.log:setstate',
+ '_getstate' : '._log.log:getstate',
+ 'Path' : '._log.log:Path',
+ 'STDOUT' : '._log.log:STDOUT',
+ 'STDERR' : '._log.log:STDERR',
+ 'Syslog' : '._log.log:Syslog',
+ },
+
+})
diff --git a/tests/wpt/web-platform-tests/tools/py/py/__metainfo.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/__metainfo.py
index 12581eb7afb..12581eb7afb 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/__metainfo.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/__metainfo.py
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_builtin.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_builtin.py
index 52ee9d79cad..52ee9d79cad 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_builtin.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_builtin.py
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_code/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/__init__.py
index f15acf85132..f15acf85132 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_code/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/_assertionnew.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/_assertionnew.py
new file mode 100644
index 00000000000..d03f29d8708
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/_assertionnew.py
@@ -0,0 +1,322 @@
+"""
+Find intermediate evalutation results in assert statements through builtin AST.
+This should replace _assertionold.py eventually.
+"""
+
+import sys
+import ast
+
+import py
+from py._code.assertion import _format_explanation, BuiltinAssertionError
+
+
+def _is_ast_expr(node):
+ return isinstance(node, ast.expr)
+def _is_ast_stmt(node):
+ return isinstance(node, ast.stmt)
+
+
+class Failure(Exception):
+ """Error found while interpreting AST."""
+
+ def __init__(self, explanation=""):
+ self.cause = sys.exc_info()
+ self.explanation = explanation
+
+
+def interpret(source, frame, should_fail=False):
+ mod = ast.parse(source)
+ visitor = DebugInterpreter(frame)
+ try:
+ visitor.visit(mod)
+ except Failure:
+ failure = sys.exc_info()[1]
+ return getfailure(failure)
+ if should_fail:
+ return ("(assertion failed, but when it was re-run for "
+ "printing intermediate values, it did not fail. Suggestions: "
+ "compute assert expression before the assert or use --no-assert)")
+
+def run(offending_line, frame=None):
+ if frame is None:
+ frame = py.code.Frame(sys._getframe(1))
+ return interpret(offending_line, frame)
+
+def getfailure(failure):
+ explanation = _format_explanation(failure.explanation)
+ value = failure.cause[1]
+ if str(value):
+ lines = explanation.splitlines()
+ if not lines:
+ lines.append("")
+ lines[0] += " << %s" % (value,)
+ explanation = "\n".join(lines)
+ text = "%s: %s" % (failure.cause[0].__name__, explanation)
+ if text.startswith("AssertionError: assert "):
+ text = text[16:]
+ return text
+
+
+operator_map = {
+ ast.BitOr : "|",
+ ast.BitXor : "^",
+ ast.BitAnd : "&",
+ ast.LShift : "<<",
+ ast.RShift : ">>",
+ ast.Add : "+",
+ ast.Sub : "-",
+ ast.Mult : "*",
+ ast.Div : "/",
+ ast.FloorDiv : "//",
+ ast.Mod : "%",
+ ast.Eq : "==",
+ ast.NotEq : "!=",
+ ast.Lt : "<",
+ ast.LtE : "<=",
+ ast.Gt : ">",
+ ast.GtE : ">=",
+ ast.Pow : "**",
+ ast.Is : "is",
+ ast.IsNot : "is not",
+ ast.In : "in",
+ ast.NotIn : "not in"
+}
+
+unary_map = {
+ ast.Not : "not %s",
+ ast.Invert : "~%s",
+ ast.USub : "-%s",
+ ast.UAdd : "+%s"
+}
+
+
+class DebugInterpreter(ast.NodeVisitor):
+ """Interpret AST nodes to gleam useful debugging information. """
+
+ def __init__(self, frame):
+ self.frame = frame
+
+ def generic_visit(self, node):
+ # Fallback when we don't have a special implementation.
+ if _is_ast_expr(node):
+ mod = ast.Expression(node)
+ co = self._compile(mod)
+ try:
+ result = self.frame.eval(co)
+ except Exception:
+ raise Failure()
+ explanation = self.frame.repr(result)
+ return explanation, result
+ elif _is_ast_stmt(node):
+ mod = ast.Module([node])
+ co = self._compile(mod, "exec")
+ try:
+ self.frame.exec_(co)
+ except Exception:
+ raise Failure()
+ return None, None
+ else:
+ raise AssertionError("can't handle %s" %(node,))
+
+ def _compile(self, source, mode="eval"):
+ return compile(source, "<assertion interpretation>", mode)
+
+ def visit_Expr(self, expr):
+ return self.visit(expr.value)
+
+ def visit_Module(self, mod):
+ for stmt in mod.body:
+ self.visit(stmt)
+
+ def visit_Name(self, name):
+ explanation, result = self.generic_visit(name)
+ # See if the name is local.
+ source = "%r in locals() is not globals()" % (name.id,)
+ co = self._compile(source)
+ try:
+ local = self.frame.eval(co)
+ except Exception:
+ # have to assume it isn't
+ local = False
+ if not local:
+ return name.id, result
+ return explanation, result
+
+ def visit_Compare(self, comp):
+ left = comp.left
+ left_explanation, left_result = self.visit(left)
+ for op, next_op in zip(comp.ops, comp.comparators):
+ next_explanation, next_result = self.visit(next_op)
+ op_symbol = operator_map[op.__class__]
+ explanation = "%s %s %s" % (left_explanation, op_symbol,
+ next_explanation)
+ source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,)
+ co = self._compile(source)
+ try:
+ result = self.frame.eval(co, __exprinfo_left=left_result,
+ __exprinfo_right=next_result)
+ except Exception:
+ raise Failure(explanation)
+ try:
+ if not result:
+ break
+ except KeyboardInterrupt:
+ raise
+ except:
+ break
+ left_explanation, left_result = next_explanation, next_result
+
+ rcomp = py.code._reprcompare
+ if rcomp:
+ res = rcomp(op_symbol, left_result, next_result)
+ if res:
+ explanation = res
+ return explanation, result
+
+ def visit_BoolOp(self, boolop):
+ is_or = isinstance(boolop.op, ast.Or)
+ explanations = []
+ for operand in boolop.values:
+ explanation, result = self.visit(operand)
+ explanations.append(explanation)
+ if result == is_or:
+ break
+ name = is_or and " or " or " and "
+ explanation = "(" + name.join(explanations) + ")"
+ return explanation, result
+
+ def visit_UnaryOp(self, unary):
+ pattern = unary_map[unary.op.__class__]
+ operand_explanation, operand_result = self.visit(unary.operand)
+ explanation = pattern % (operand_explanation,)
+ co = self._compile(pattern % ("__exprinfo_expr",))
+ try:
+ result = self.frame.eval(co, __exprinfo_expr=operand_result)
+ except Exception:
+ raise Failure(explanation)
+ return explanation, result
+
+ def visit_BinOp(self, binop):
+ left_explanation, left_result = self.visit(binop.left)
+ right_explanation, right_result = self.visit(binop.right)
+ symbol = operator_map[binop.op.__class__]
+ explanation = "(%s %s %s)" % (left_explanation, symbol,
+ right_explanation)
+ source = "__exprinfo_left %s __exprinfo_right" % (symbol,)
+ co = self._compile(source)
+ try:
+ result = self.frame.eval(co, __exprinfo_left=left_result,
+ __exprinfo_right=right_result)
+ except Exception:
+ raise Failure(explanation)
+ return explanation, result
+
+ def visit_Call(self, call):
+ func_explanation, func = self.visit(call.func)
+ arg_explanations = []
+ ns = {"__exprinfo_func" : func}
+ arguments = []
+ for arg in call.args:
+ arg_explanation, arg_result = self.visit(arg)
+ arg_name = "__exprinfo_%s" % (len(ns),)
+ ns[arg_name] = arg_result
+ arguments.append(arg_name)
+ arg_explanations.append(arg_explanation)
+ for keyword in call.keywords:
+ arg_explanation, arg_result = self.visit(keyword.value)
+ arg_name = "__exprinfo_%s" % (len(ns),)
+ ns[arg_name] = arg_result
+ keyword_source = "%s=%%s" % (keyword.arg)
+ arguments.append(keyword_source % (arg_name,))
+ arg_explanations.append(keyword_source % (arg_explanation,))
+ if call.starargs:
+ arg_explanation, arg_result = self.visit(call.starargs)
+ arg_name = "__exprinfo_star"
+ ns[arg_name] = arg_result
+ arguments.append("*%s" % (arg_name,))
+ arg_explanations.append("*%s" % (arg_explanation,))
+ if call.kwargs:
+ arg_explanation, arg_result = self.visit(call.kwargs)
+ arg_name = "__exprinfo_kwds"
+ ns[arg_name] = arg_result
+ arguments.append("**%s" % (arg_name,))
+ arg_explanations.append("**%s" % (arg_explanation,))
+ args_explained = ", ".join(arg_explanations)
+ explanation = "%s(%s)" % (func_explanation, args_explained)
+ args = ", ".join(arguments)
+ source = "__exprinfo_func(%s)" % (args,)
+ co = self._compile(source)
+ try:
+ result = self.frame.eval(co, **ns)
+ except Exception:
+ raise Failure(explanation)
+ pattern = "%s\n{%s = %s\n}"
+ rep = self.frame.repr(result)
+ explanation = pattern % (rep, rep, explanation)
+ return explanation, result
+
+ def _is_builtin_name(self, name):
+ pattern = "%r not in globals() and %r not in locals()"
+ source = pattern % (name.id, name.id)
+ co = self._compile(source)
+ try:
+ return self.frame.eval(co)
+ except Exception:
+ return False
+
+ def visit_Attribute(self, attr):
+ if not isinstance(attr.ctx, ast.Load):
+ return self.generic_visit(attr)
+ source_explanation, source_result = self.visit(attr.value)
+ explanation = "%s.%s" % (source_explanation, attr.attr)
+ source = "__exprinfo_expr.%s" % (attr.attr,)
+ co = self._compile(source)
+ try:
+ result = self.frame.eval(co, __exprinfo_expr=source_result)
+ except Exception:
+ raise Failure(explanation)
+ explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result),
+ self.frame.repr(result),
+ source_explanation, attr.attr)
+ # Check if the attr is from an instance.
+ source = "%r in getattr(__exprinfo_expr, '__dict__', {})"
+ source = source % (attr.attr,)
+ co = self._compile(source)
+ try:
+ from_instance = self.frame.eval(co, __exprinfo_expr=source_result)
+ except Exception:
+ from_instance = True
+ if from_instance:
+ rep = self.frame.repr(result)
+ pattern = "%s\n{%s = %s\n}"
+ explanation = pattern % (rep, rep, explanation)
+ return explanation, result
+
+ def visit_Assert(self, assrt):
+ test_explanation, test_result = self.visit(assrt.test)
+ if test_explanation.startswith("False\n{False =") and \
+ test_explanation.endswith("\n"):
+ test_explanation = test_explanation[15:-2]
+ explanation = "assert %s" % (test_explanation,)
+ if not test_result:
+ try:
+ raise BuiltinAssertionError
+ except Exception:
+ raise Failure(explanation)
+ return explanation, test_result
+
+ def visit_Assign(self, assign):
+ value_explanation, value_result = self.visit(assign.value)
+ explanation = "... = %s" % (value_explanation,)
+ name = ast.Name("__exprinfo_expr", ast.Load(),
+ lineno=assign.value.lineno,
+ col_offset=assign.value.col_offset)
+ new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno,
+ col_offset=assign.col_offset)
+ mod = ast.Module([new_assign])
+ co = self._compile(mod, "exec")
+ try:
+ self.frame.exec_(co, __exprinfo_expr=value_result)
+ except Exception:
+ raise Failure(explanation)
+ return explanation, value_result
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/_assertionold.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/_assertionold.py
new file mode 100644
index 00000000000..1bb70a875d0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/_assertionold.py
@@ -0,0 +1,556 @@
+import py
+import sys, inspect
+from compiler import parse, ast, pycodegen
+from py._code.assertion import BuiltinAssertionError, _format_explanation
+import types
+
+passthroughex = py.builtin._sysex
+
+class Failure:
+ def __init__(self, node):
+ self.exc, self.value, self.tb = sys.exc_info()
+ self.node = node
+
+class View(object):
+ """View base class.
+
+ If C is a subclass of View, then C(x) creates a proxy object around
+ the object x. The actual class of the proxy is not C in general,
+ but a *subclass* of C determined by the rules below. To avoid confusion
+ we call view class the class of the proxy (a subclass of C, so of View)
+ and object class the class of x.
+
+ Attributes and methods not found in the proxy are automatically read on x.
+ Other operations like setting attributes are performed on the proxy, as
+ determined by its view class. The object x is available from the proxy
+ as its __obj__ attribute.
+
+ The view class selection is determined by the __view__ tuples and the
+ optional __viewkey__ method. By default, the selected view class is the
+ most specific subclass of C whose __view__ mentions the class of x.
+ If no such subclass is found, the search proceeds with the parent
+ object classes. For example, C(True) will first look for a subclass
+ of C with __view__ = (..., bool, ...) and only if it doesn't find any
+ look for one with __view__ = (..., int, ...), and then ..., object,...
+ If everything fails the class C itself is considered to be the default.
+
+ Alternatively, the view class selection can be driven by another aspect
+ of the object x, instead of the class of x, by overriding __viewkey__.
+ See last example at the end of this module.
+ """
+
+ _viewcache = {}
+ __view__ = ()
+
+ def __new__(rootclass, obj, *args, **kwds):
+ self = object.__new__(rootclass)
+ self.__obj__ = obj
+ self.__rootclass__ = rootclass
+ key = self.__viewkey__()
+ try:
+ self.__class__ = self._viewcache[key]
+ except KeyError:
+ self.__class__ = self._selectsubclass(key)
+ return self
+
+ def __getattr__(self, attr):
+ # attributes not found in the normal hierarchy rooted on View
+ # are looked up in the object's real class
+ return getattr(self.__obj__, attr)
+
+ def __viewkey__(self):
+ return self.__obj__.__class__
+
+ def __matchkey__(self, key, subclasses):
+ if inspect.isclass(key):
+ keys = inspect.getmro(key)
+ else:
+ keys = [key]
+ for key in keys:
+ result = [C for C in subclasses if key in C.__view__]
+ if result:
+ return result
+ return []
+
+ def _selectsubclass(self, key):
+ subclasses = list(enumsubclasses(self.__rootclass__))
+ for C in subclasses:
+ if not isinstance(C.__view__, tuple):
+ C.__view__ = (C.__view__,)
+ choices = self.__matchkey__(key, subclasses)
+ if not choices:
+ return self.__rootclass__
+ elif len(choices) == 1:
+ return choices[0]
+ else:
+ # combine the multiple choices
+ return type('?', tuple(choices), {})
+
+ def __repr__(self):
+ return '%s(%r)' % (self.__rootclass__.__name__, self.__obj__)
+
+
+def enumsubclasses(cls):
+ for subcls in cls.__subclasses__():
+ for subsubclass in enumsubclasses(subcls):
+ yield subsubclass
+ yield cls
+
+
+class Interpretable(View):
+ """A parse tree node with a few extra methods."""
+ explanation = None
+
+ def is_builtin(self, frame):
+ return False
+
+ def eval(self, frame):
+ # fall-back for unknown expression nodes
+ try:
+ expr = ast.Expression(self.__obj__)
+ expr.filename = '<eval>'
+ self.__obj__.filename = '<eval>'
+ co = pycodegen.ExpressionCodeGenerator(expr).getCode()
+ result = frame.eval(co)
+ except passthroughex:
+ raise
+ except:
+ raise Failure(self)
+ self.result = result
+ self.explanation = self.explanation or frame.repr(self.result)
+
+ def run(self, frame):
+ # fall-back for unknown statement nodes
+ try:
+ expr = ast.Module(None, ast.Stmt([self.__obj__]))
+ expr.filename = '<run>'
+ co = pycodegen.ModuleCodeGenerator(expr).getCode()
+ frame.exec_(co)
+ except passthroughex:
+ raise
+ except:
+ raise Failure(self)
+
+ def nice_explanation(self):
+ return _format_explanation(self.explanation)
+
+
+class Name(Interpretable):
+ __view__ = ast.Name
+
+ def is_local(self, frame):
+ source = '%r in locals() is not globals()' % self.name
+ try:
+ return frame.is_true(frame.eval(source))
+ except passthroughex:
+ raise
+ except:
+ return False
+
+ def is_global(self, frame):
+ source = '%r in globals()' % self.name
+ try:
+ return frame.is_true(frame.eval(source))
+ except passthroughex:
+ raise
+ except:
+ return False
+
+ def is_builtin(self, frame):
+ source = '%r not in locals() and %r not in globals()' % (
+ self.name, self.name)
+ try:
+ return frame.is_true(frame.eval(source))
+ except passthroughex:
+ raise
+ except:
+ return False
+
+ def eval(self, frame):
+ super(Name, self).eval(frame)
+ if not self.is_local(frame):
+ self.explanation = self.name
+
+class Compare(Interpretable):
+ __view__ = ast.Compare
+
+ def eval(self, frame):
+ expr = Interpretable(self.expr)
+ expr.eval(frame)
+ for operation, expr2 in self.ops:
+ if hasattr(self, 'result'):
+ # shortcutting in chained expressions
+ if not frame.is_true(self.result):
+ break
+ expr2 = Interpretable(expr2)
+ expr2.eval(frame)
+ self.explanation = "%s %s %s" % (
+ expr.explanation, operation, expr2.explanation)
+ source = "__exprinfo_left %s __exprinfo_right" % operation
+ try:
+ self.result = frame.eval(source,
+ __exprinfo_left=expr.result,
+ __exprinfo_right=expr2.result)
+ except passthroughex:
+ raise
+ except:
+ raise Failure(self)
+ expr = expr2
+
+class And(Interpretable):
+ __view__ = ast.And
+
+ def eval(self, frame):
+ explanations = []
+ for expr in self.nodes:
+ expr = Interpretable(expr)
+ expr.eval(frame)
+ explanations.append(expr.explanation)
+ self.result = expr.result
+ if not frame.is_true(expr.result):
+ break
+ self.explanation = '(' + ' and '.join(explanations) + ')'
+
+class Or(Interpretable):
+ __view__ = ast.Or
+
+ def eval(self, frame):
+ explanations = []
+ for expr in self.nodes:
+ expr = Interpretable(expr)
+ expr.eval(frame)
+ explanations.append(expr.explanation)
+ self.result = expr.result
+ if frame.is_true(expr.result):
+ break
+ self.explanation = '(' + ' or '.join(explanations) + ')'
+
+
+# == Unary operations ==
+keepalive = []
+for astclass, astpattern in {
+ ast.Not : 'not __exprinfo_expr',
+ ast.Invert : '(~__exprinfo_expr)',
+ }.items():
+
+ class UnaryArith(Interpretable):
+ __view__ = astclass
+
+ def eval(self, frame, astpattern=astpattern):
+ expr = Interpretable(self.expr)
+ expr.eval(frame)
+ self.explanation = astpattern.replace('__exprinfo_expr',
+ expr.explanation)
+ try:
+ self.result = frame.eval(astpattern,
+ __exprinfo_expr=expr.result)
+ except passthroughex:
+ raise
+ except:
+ raise Failure(self)
+
+ keepalive.append(UnaryArith)
+
+# == Binary operations ==
+for astclass, astpattern in {
+ ast.Add : '(__exprinfo_left + __exprinfo_right)',
+ ast.Sub : '(__exprinfo_left - __exprinfo_right)',
+ ast.Mul : '(__exprinfo_left * __exprinfo_right)',
+ ast.Div : '(__exprinfo_left / __exprinfo_right)',
+ ast.Mod : '(__exprinfo_left % __exprinfo_right)',
+ ast.Power : '(__exprinfo_left ** __exprinfo_right)',
+ }.items():
+
+ class BinaryArith(Interpretable):
+ __view__ = astclass
+
+ def eval(self, frame, astpattern=astpattern):
+ left = Interpretable(self.left)
+ left.eval(frame)
+ right = Interpretable(self.right)
+ right.eval(frame)
+ self.explanation = (astpattern
+ .replace('__exprinfo_left', left .explanation)
+ .replace('__exprinfo_right', right.explanation))
+ try:
+ self.result = frame.eval(astpattern,
+ __exprinfo_left=left.result,
+ __exprinfo_right=right.result)
+ except passthroughex:
+ raise
+ except:
+ raise Failure(self)
+
+ keepalive.append(BinaryArith)
+
+
+class CallFunc(Interpretable):
+ __view__ = ast.CallFunc
+
+ def is_bool(self, frame):
+ source = 'isinstance(__exprinfo_value, bool)'
+ try:
+ return frame.is_true(frame.eval(source,
+ __exprinfo_value=self.result))
+ except passthroughex:
+ raise
+ except:
+ return False
+
+ def eval(self, frame):
+ node = Interpretable(self.node)
+ node.eval(frame)
+ explanations = []
+ vars = {'__exprinfo_fn': node.result}
+ source = '__exprinfo_fn('
+ for a in self.args:
+ if isinstance(a, ast.Keyword):
+ keyword = a.name
+ a = a.expr
+ else:
+ keyword = None
+ a = Interpretable(a)
+ a.eval(frame)
+ argname = '__exprinfo_%d' % len(vars)
+ vars[argname] = a.result
+ if keyword is None:
+ source += argname + ','
+ explanations.append(a.explanation)
+ else:
+ source += '%s=%s,' % (keyword, argname)
+ explanations.append('%s=%s' % (keyword, a.explanation))
+ if self.star_args:
+ star_args = Interpretable(self.star_args)
+ star_args.eval(frame)
+ argname = '__exprinfo_star'
+ vars[argname] = star_args.result
+ source += '*' + argname + ','
+ explanations.append('*' + star_args.explanation)
+ if self.dstar_args:
+ dstar_args = Interpretable(self.dstar_args)
+ dstar_args.eval(frame)
+ argname = '__exprinfo_kwds'
+ vars[argname] = dstar_args.result
+ source += '**' + argname + ','
+ explanations.append('**' + dstar_args.explanation)
+ self.explanation = "%s(%s)" % (
+ node.explanation, ', '.join(explanations))
+ if source.endswith(','):
+ source = source[:-1]
+ source += ')'
+ try:
+ self.result = frame.eval(source, **vars)
+ except passthroughex:
+ raise
+ except:
+ raise Failure(self)
+ if not node.is_builtin(frame) or not self.is_bool(frame):
+ r = frame.repr(self.result)
+ self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
+
+class Getattr(Interpretable):
+ __view__ = ast.Getattr
+
+ def eval(self, frame):
+ expr = Interpretable(self.expr)
+ expr.eval(frame)
+ source = '__exprinfo_expr.%s' % self.attrname
+ try:
+ self.result = frame.eval(source, __exprinfo_expr=expr.result)
+ except passthroughex:
+ raise
+ except:
+ raise Failure(self)
+ self.explanation = '%s.%s' % (expr.explanation, self.attrname)
+ # if the attribute comes from the instance, its value is interesting
+ source = ('hasattr(__exprinfo_expr, "__dict__") and '
+ '%r in __exprinfo_expr.__dict__' % self.attrname)
+ try:
+ from_instance = frame.is_true(
+ frame.eval(source, __exprinfo_expr=expr.result))
+ except passthroughex:
+ raise
+ except:
+ from_instance = True
+ if from_instance:
+ r = frame.repr(self.result)
+ self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
+
+# == Re-interpretation of full statements ==
+
+class Assert(Interpretable):
+ __view__ = ast.Assert
+
+ def run(self, frame):
+ test = Interpretable(self.test)
+ test.eval(frame)
+ # simplify 'assert False where False = ...'
+ if (test.explanation.startswith('False\n{False = ') and
+ test.explanation.endswith('\n}')):
+ test.explanation = test.explanation[15:-2]
+ # print the result as 'assert <explanation>'
+ self.result = test.result
+ self.explanation = 'assert ' + test.explanation
+ if not frame.is_true(test.result):
+ try:
+ raise BuiltinAssertionError
+ except passthroughex:
+ raise
+ except:
+ raise Failure(self)
+
+class Assign(Interpretable):
+ __view__ = ast.Assign
+
+ def run(self, frame):
+ expr = Interpretable(self.expr)
+ expr.eval(frame)
+ self.result = expr.result
+ self.explanation = '... = ' + expr.explanation
+ # fall-back-run the rest of the assignment
+ ass = ast.Assign(self.nodes, ast.Name('__exprinfo_expr'))
+ mod = ast.Module(None, ast.Stmt([ass]))
+ mod.filename = '<run>'
+ co = pycodegen.ModuleCodeGenerator(mod).getCode()
+ try:
+ frame.exec_(co, __exprinfo_expr=expr.result)
+ except passthroughex:
+ raise
+ except:
+ raise Failure(self)
+
+class Discard(Interpretable):
+ __view__ = ast.Discard
+
+ def run(self, frame):
+ expr = Interpretable(self.expr)
+ expr.eval(frame)
+ self.result = expr.result
+ self.explanation = expr.explanation
+
+class Stmt(Interpretable):
+ __view__ = ast.Stmt
+
+ def run(self, frame):
+ for stmt in self.nodes:
+ stmt = Interpretable(stmt)
+ stmt.run(frame)
+
+
+def report_failure(e):
+ explanation = e.node.nice_explanation()
+ if explanation:
+ explanation = ", in: " + explanation
+ else:
+ explanation = ""
+ sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation))
+
+def check(s, frame=None):
+ if frame is None:
+ frame = sys._getframe(1)
+ frame = py.code.Frame(frame)
+ expr = parse(s, 'eval')
+ assert isinstance(expr, ast.Expression)
+ node = Interpretable(expr.node)
+ try:
+ node.eval(frame)
+ except passthroughex:
+ raise
+ except Failure:
+ e = sys.exc_info()[1]
+ report_failure(e)
+ else:
+ if not frame.is_true(node.result):
+ sys.stderr.write("assertion failed: %s\n" % node.nice_explanation())
+
+
+###########################################################
+# API / Entry points
+# #########################################################
+
+def interpret(source, frame, should_fail=False):
+ module = Interpretable(parse(source, 'exec').node)
+ #print "got module", module
+ if isinstance(frame, types.FrameType):
+ frame = py.code.Frame(frame)
+ try:
+ module.run(frame)
+ except Failure:
+ e = sys.exc_info()[1]
+ return getfailure(e)
+ except passthroughex:
+ raise
+ except:
+ import traceback
+ traceback.print_exc()
+ if should_fail:
+ return ("(assertion failed, but when it was re-run for "
+ "printing intermediate values, it did not fail. Suggestions: "
+ "compute assert expression before the assert or use --nomagic)")
+ else:
+ return None
+
+def getmsg(excinfo):
+ if isinstance(excinfo, tuple):
+ excinfo = py.code.ExceptionInfo(excinfo)
+ #frame, line = gettbline(tb)
+ #frame = py.code.Frame(frame)
+ #return interpret(line, frame)
+
+ tb = excinfo.traceback[-1]
+ source = str(tb.statement).strip()
+ x = interpret(source, tb.frame, should_fail=True)
+ if not isinstance(x, str):
+ raise TypeError("interpret returned non-string %r" % (x,))
+ return x
+
+def getfailure(e):
+ explanation = e.node.nice_explanation()
+ if str(e.value):
+ lines = explanation.split('\n')
+ lines[0] += " << %s" % (e.value,)
+ explanation = '\n'.join(lines)
+ text = "%s: %s" % (e.exc.__name__, explanation)
+ if text.startswith('AssertionError: assert '):
+ text = text[16:]
+ return text
+
+def run(s, frame=None):
+ if frame is None:
+ frame = sys._getframe(1)
+ frame = py.code.Frame(frame)
+ module = Interpretable(parse(s, 'exec').node)
+ try:
+ module.run(frame)
+ except Failure:
+ e = sys.exc_info()[1]
+ report_failure(e)
+
+
+if __name__ == '__main__':
+ # example:
+ def f():
+ return 5
+ def g():
+ return 3
+ def h(x):
+ return 'never'
+ check("f() * g() == 5")
+ check("not f()")
+ check("not (f() and g() or 0)")
+ check("f() == g()")
+ i = 4
+ check("i == f()")
+ check("len(f()) == 0")
+ check("isinstance(2+3+4, float)")
+
+ run("x = i")
+ check("x == 5")
+
+ run("assert not f(), 'oops'")
+ run("a, b, c = 1, 2")
+ run("a, b, c = f()")
+
+ check("max([f(),g()]) == 4")
+ check("'hello'[g()] == 'h'")
+ run("'guk%d' % h(f())")
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_code/_py2traceback.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/_py2traceback.py
index d65e27cb730..d65e27cb730 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_code/_py2traceback.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/_py2traceback.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/assertion.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/assertion.py
new file mode 100644
index 00000000000..ff1643799c9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/assertion.py
@@ -0,0 +1,90 @@
+import sys
+import py
+
+BuiltinAssertionError = py.builtin.builtins.AssertionError
+
+_reprcompare = None # if set, will be called by assert reinterp for comparison ops
+
+def _format_explanation(explanation):
+ """This formats an explanation
+
+ Normally all embedded newlines are escaped, however there are
+ three exceptions: \n{, \n} and \n~. The first two are intended
+ cover nested explanations, see function and attribute explanations
+ for examples (.visit_Call(), visit_Attribute()). The last one is
+ for when one explanation needs to span multiple lines, e.g. when
+ displaying diffs.
+ """
+ raw_lines = (explanation or '').split('\n')
+ # escape newlines not followed by {, } and ~
+ lines = [raw_lines[0]]
+ for l in raw_lines[1:]:
+ if l.startswith('{') or l.startswith('}') or l.startswith('~'):
+ lines.append(l)
+ else:
+ lines[-1] += '\\n' + l
+
+ result = lines[:1]
+ stack = [0]
+ stackcnt = [0]
+ for line in lines[1:]:
+ if line.startswith('{'):
+ if stackcnt[-1]:
+ s = 'and '
+ else:
+ s = 'where '
+ stack.append(len(result))
+ stackcnt[-1] += 1
+ stackcnt.append(0)
+ result.append(' +' + ' '*(len(stack)-1) + s + line[1:])
+ elif line.startswith('}'):
+ assert line.startswith('}')
+ stack.pop()
+ stackcnt.pop()
+ result[stack[-1]] += line[1:]
+ else:
+ assert line.startswith('~')
+ result.append(' '*len(stack) + line[1:])
+ assert len(stack) == 1
+ return '\n'.join(result)
+
+
+class AssertionError(BuiltinAssertionError):
+ def __init__(self, *args):
+ BuiltinAssertionError.__init__(self, *args)
+ if args:
+ try:
+ self.msg = str(args[0])
+ except py.builtin._sysex:
+ raise
+ except:
+ self.msg = "<[broken __repr__] %s at %0xd>" %(
+ args[0].__class__, id(args[0]))
+ else:
+ f = py.code.Frame(sys._getframe(1))
+ try:
+ source = f.code.fullsource
+ if source is not None:
+ try:
+ source = source.getstatement(f.lineno, assertion=True)
+ except IndexError:
+ source = None
+ else:
+ source = str(source.deindent()).strip()
+ except py.error.ENOENT:
+ source = None
+ # this can also occur during reinterpretation, when the
+ # co_filename is set to "<run>".
+ if source:
+ self.msg = reinterpret(source, f, should_fail=True)
+ else:
+ self.msg = "<could not determine information>"
+ if not self.args:
+ self.args = (self.msg,)
+
+if sys.version_info > (3, 0):
+ AssertionError.__module__ = "builtins"
+ reinterpret_old = "old reinterpretation not available for py3"
+else:
+ from py._code._assertionold import interpret as reinterpret_old
+from py._code._assertionnew import interpret as reinterpret
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/code.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/code.py
new file mode 100644
index 00000000000..dad796283fe
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/code.py
@@ -0,0 +1,796 @@
+import py
+import sys
+from inspect import CO_VARARGS, CO_VARKEYWORDS, isclass
+
+builtin_repr = repr
+
+reprlib = py.builtin._tryimport('repr', 'reprlib')
+
+if sys.version_info[0] >= 3:
+ from traceback import format_exception_only
+else:
+ from py._code._py2traceback import format_exception_only
+
+import traceback
+
+
+class Code(object):
+ """ wrapper around Python code objects """
+ def __init__(self, rawcode):
+ if not hasattr(rawcode, "co_filename"):
+ rawcode = py.code.getrawcode(rawcode)
+ try:
+ self.filename = rawcode.co_filename
+ self.firstlineno = rawcode.co_firstlineno - 1
+ self.name = rawcode.co_name
+ except AttributeError:
+ raise TypeError("not a code object: %r" % (rawcode,))
+ self.raw = rawcode
+
+ def __eq__(self, other):
+ return self.raw == other.raw
+
+ def __ne__(self, other):
+ return not self == other
+
+ @property
+ def path(self):
+ """ return a path object pointing to source code (note that it
+ might not point to an actually existing file). """
+ p = py.path.local(self.raw.co_filename)
+ # maybe don't try this checking
+ if not p.check():
+ # XXX maybe try harder like the weird logic
+ # in the standard lib [linecache.updatecache] does?
+ p = self.raw.co_filename
+ return p
+
+ @property
+ def fullsource(self):
+ """ return a py.code.Source object for the full source file of the code
+ """
+ from py._code import source
+ full, _ = source.findsource(self.raw)
+ return full
+
+ def source(self):
+ """ return a py.code.Source object for the code object's source only
+ """
+ # return source only for that part of code
+ return py.code.Source(self.raw)
+
+ def getargs(self, var=False):
+ """ return a tuple with the argument names for the code object
+
+ if 'var' is set True also return the names of the variable and
+ keyword arguments when present
+ """
+ # handfull shortcut for getting args
+ raw = self.raw
+ argcount = raw.co_argcount
+ if var:
+ argcount += raw.co_flags & CO_VARARGS
+ argcount += raw.co_flags & CO_VARKEYWORDS
+ return raw.co_varnames[:argcount]
+
+class Frame(object):
+ """Wrapper around a Python frame holding f_locals and f_globals
+ in which expressions can be evaluated."""
+
+ def __init__(self, frame):
+ self.lineno = frame.f_lineno - 1
+ self.f_globals = frame.f_globals
+ self.f_locals = frame.f_locals
+ self.raw = frame
+ self.code = py.code.Code(frame.f_code)
+
+ @property
+ def statement(self):
+ """ statement this frame is at """
+ if self.code.fullsource is None:
+ return py.code.Source("")
+ return self.code.fullsource.getstatement(self.lineno)
+
+ def eval(self, code, **vars):
+ """ evaluate 'code' in the frame
+
+ 'vars' are optional additional local variables
+
+ returns the result of the evaluation
+ """
+ f_locals = self.f_locals.copy()
+ f_locals.update(vars)
+ return eval(code, self.f_globals, f_locals)
+
+ def exec_(self, code, **vars):
+ """ exec 'code' in the frame
+
+ 'vars' are optiona; additional local variables
+ """
+ f_locals = self.f_locals.copy()
+ f_locals.update(vars)
+ py.builtin.exec_(code, self.f_globals, f_locals)
+
+ def repr(self, object):
+ """ return a 'safe' (non-recursive, one-line) string repr for 'object'
+ """
+ return py.io.saferepr(object)
+
+ def is_true(self, object):
+ return object
+
+ def getargs(self, var=False):
+ """ return a list of tuples (name, value) for all arguments
+
+ if 'var' is set True also include the variable and keyword
+ arguments when present
+ """
+ retval = []
+ for arg in self.code.getargs(var):
+ try:
+ retval.append((arg, self.f_locals[arg]))
+ except KeyError:
+ pass # this can occur when using Psyco
+ return retval
+
+
+class TracebackEntry(object):
+ """ a single entry in a traceback """
+
+ _repr_style = None
+ exprinfo = None
+
+ def __init__(self, rawentry):
+ self._rawentry = rawentry
+ self.lineno = rawentry.tb_lineno - 1
+
+ def set_repr_style(self, mode):
+ assert mode in ("short", "long")
+ self._repr_style = mode
+
+ @property
+ def frame(self):
+ return py.code.Frame(self._rawentry.tb_frame)
+
+ @property
+ def relline(self):
+ return self.lineno - self.frame.code.firstlineno
+
+ def __repr__(self):
+ return "<TracebackEntry %s:%d>" % (self.frame.code.path, self.lineno+1)
+
+ @property
+ def statement(self):
+ """ py.code.Source object for the current statement """
+ source = self.frame.code.fullsource
+ return source.getstatement(self.lineno)
+
+ @property
+ def path(self):
+ """ path to the source code """
+ return self.frame.code.path
+
+ def getlocals(self):
+ return self.frame.f_locals
+ locals = property(getlocals, None, None, "locals of underlaying frame")
+
+ def reinterpret(self):
+ """Reinterpret the failing statement and returns a detailed information
+ about what operations are performed."""
+ if self.exprinfo is None:
+ source = str(self.statement).strip()
+ x = py.code._reinterpret(source, self.frame, should_fail=True)
+ if not isinstance(x, str):
+ raise TypeError("interpret returned non-string %r" % (x,))
+ self.exprinfo = x
+ return self.exprinfo
+
+ def getfirstlinesource(self):
+ # on Jython this firstlineno can be -1 apparently
+ return max(self.frame.code.firstlineno, 0)
+
+ def getsource(self, astcache=None):
+ """ return failing source code. """
+ # we use the passed in astcache to not reparse asttrees
+ # within exception info printing
+ from py._code.source import getstatementrange_ast
+ source = self.frame.code.fullsource
+ if source is None:
+ return None
+ key = astnode = None
+ if astcache is not None:
+ key = self.frame.code.path
+ if key is not None:
+ astnode = astcache.get(key, None)
+ start = self.getfirstlinesource()
+ try:
+ astnode, _, end = getstatementrange_ast(self.lineno, source,
+ astnode=astnode)
+ except SyntaxError:
+ end = self.lineno + 1
+ else:
+ if key is not None:
+ astcache[key] = astnode
+ return source[start:end]
+
+ source = property(getsource)
+
+ def ishidden(self):
+ """ return True if the current frame has a var __tracebackhide__
+ resolving to True
+
+ mostly for internal use
+ """
+ try:
+ return self.frame.f_locals['__tracebackhide__']
+ except KeyError:
+ try:
+ return self.frame.f_globals['__tracebackhide__']
+ except KeyError:
+ return False
+
+ def __str__(self):
+ try:
+ fn = str(self.path)
+ except py.error.Error:
+ fn = '???'
+ name = self.frame.code.name
+ try:
+ line = str(self.statement).lstrip()
+ except KeyboardInterrupt:
+ raise
+ except:
+ line = "???"
+ return " File %r:%d in %s\n %s\n" % (fn, self.lineno+1, name, line)
+
+ def name(self):
+ return self.frame.code.raw.co_name
+ name = property(name, None, None, "co_name of underlaying code")
+
+
+class Traceback(list):
+ """ Traceback objects encapsulate and offer higher level
+ access to Traceback entries.
+ """
+ Entry = TracebackEntry
+
+ def __init__(self, tb):
+ """ initialize from given python traceback object. """
+ if hasattr(tb, 'tb_next'):
+ def f(cur):
+ while cur is not None:
+ yield self.Entry(cur)
+ cur = cur.tb_next
+ list.__init__(self, f(tb))
+ else:
+ list.__init__(self, tb)
+
+ def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None):
+ """ return a Traceback instance wrapping part of this Traceback
+
+ by provding any combination of path, lineno and firstlineno, the
+ first frame to start the to-be-returned traceback is determined
+
+ this allows cutting the first part of a Traceback instance e.g.
+ for formatting reasons (removing some uninteresting bits that deal
+ with handling of the exception/traceback)
+ """
+ for x in self:
+ code = x.frame.code
+ codepath = code.path
+ if ((path is None or codepath == path) and
+ (excludepath is None or not hasattr(codepath, 'relto') or
+ not codepath.relto(excludepath)) and
+ (lineno is None or x.lineno == lineno) and
+ (firstlineno is None or x.frame.code.firstlineno == firstlineno)):
+ return Traceback(x._rawentry)
+ return self
+
+ def __getitem__(self, key):
+ val = super(Traceback, self).__getitem__(key)
+ if isinstance(key, type(slice(0))):
+ val = self.__class__(val)
+ return val
+
+ def filter(self, fn=lambda x: not x.ishidden()):
+ """ return a Traceback instance with certain items removed
+
+ fn is a function that gets a single argument, a TracebackItem
+ instance, and should return True when the item should be added
+ to the Traceback, False when not
+
+ by default this removes all the TracebackItems which are hidden
+ (see ishidden() above)
+ """
+ return Traceback(filter(fn, self))
+
+ def getcrashentry(self):
+ """ return last non-hidden traceback entry that lead
+ to the exception of a traceback.
+ """
+ for i in range(-1, -len(self)-1, -1):
+ entry = self[i]
+ if not entry.ishidden():
+ return entry
+ return self[-1]
+
+ def recursionindex(self):
+ """ return the index of the frame/TracebackItem where recursion
+ originates if appropriate, None if no recursion occurred
+ """
+ cache = {}
+ for i, entry in enumerate(self):
+ # id for the code.raw is needed to work around
+ # the strange metaprogramming in the decorator lib from pypi
+ # which generates code objects that have hash/value equality
+ #XXX needs a test
+ key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
+ #print "checking for recursion at", key
+ l = cache.setdefault(key, [])
+ if l:
+ f = entry.frame
+ loc = f.f_locals
+ for otherloc in l:
+ if f.is_true(f.eval(co_equal,
+ __recursioncache_locals_1=loc,
+ __recursioncache_locals_2=otherloc)):
+ return i
+ l.append(entry.frame.f_locals)
+ return None
+
+co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2',
+ '?', 'eval')
+
+class ExceptionInfo(object):
+ """ wraps sys.exc_info() objects and offers
+ help for navigating the traceback.
+ """
+ _striptext = ''
+ def __init__(self, tup=None, exprinfo=None):
+ if tup is None:
+ tup = sys.exc_info()
+ if exprinfo is None and isinstance(tup[1], AssertionError):
+ exprinfo = getattr(tup[1], 'msg', None)
+ if exprinfo is None:
+ exprinfo = str(tup[1])
+ if exprinfo and exprinfo.startswith('assert '):
+ self._striptext = 'AssertionError: '
+ self._excinfo = tup
+ #: the exception class
+ self.type = tup[0]
+ #: the exception instance
+ self.value = tup[1]
+ #: the exception raw traceback
+ self.tb = tup[2]
+ #: the exception type name
+ self.typename = self.type.__name__
+ #: the exception traceback (py.code.Traceback instance)
+ self.traceback = py.code.Traceback(self.tb)
+
+ def __repr__(self):
+ return "<ExceptionInfo %s tblen=%d>" % (
+ self.typename, len(self.traceback))
+
+ def exconly(self, tryshort=False):
+ """ return the exception as a string
+
+ when 'tryshort' resolves to True, and the exception is a
+ py.code._AssertionError, only the actual exception part of
+ the exception representation is returned (so 'AssertionError: ' is
+ removed from the beginning)
+ """
+ lines = format_exception_only(self.type, self.value)
+ text = ''.join(lines)
+ text = text.rstrip()
+ if tryshort:
+ if text.startswith(self._striptext):
+ text = text[len(self._striptext):]
+ return text
+
+ def errisinstance(self, exc):
+ """ return True if the exception is an instance of exc """
+ return isinstance(self.value, exc)
+
+ def _getreprcrash(self):
+ exconly = self.exconly(tryshort=True)
+ entry = self.traceback.getcrashentry()
+ path, lineno = entry.frame.code.raw.co_filename, entry.lineno
+ return ReprFileLocation(path, lineno+1, exconly)
+
+ def getrepr(self, showlocals=False, style="long",
+ abspath=False, tbfilter=True, funcargs=False):
+ """ return str()able representation of this exception info.
+ showlocals: show locals per traceback entry
+ style: long|short|no|native traceback style
+ tbfilter: hide entries (where __tracebackhide__ is true)
+
+ in case of style==native, tbfilter and showlocals is ignored.
+ """
+ if style == 'native':
+ return ReprExceptionInfo(ReprTracebackNative(
+ traceback.format_exception(
+ self.type,
+ self.value,
+ self.traceback[0]._rawentry,
+ )), self._getreprcrash())
+
+ fmt = FormattedExcinfo(
+ showlocals=showlocals, style=style,
+ abspath=abspath, tbfilter=tbfilter, funcargs=funcargs)
+ return fmt.repr_excinfo(self)
+
+ def __str__(self):
+ entry = self.traceback[-1]
+ loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
+ return str(loc)
+
+ def __unicode__(self):
+ entry = self.traceback[-1]
+ loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
+ return loc.__unicode__()
+
+
+class FormattedExcinfo(object):
+ """ presenting information about failing Functions and Generators. """
+ # for traceback entries
+ flow_marker = ">"
+ fail_marker = "E"
+
+ def __init__(self, showlocals=False, style="long",
+ abspath=True, tbfilter=True, funcargs=False):
+ self.showlocals = showlocals
+ self.style = style
+ self.tbfilter = tbfilter
+ self.funcargs = funcargs
+ self.abspath = abspath
+ self.astcache = {}
+
+ def _getindent(self, source):
+ # figure out indent for given source
+ try:
+ s = str(source.getstatement(len(source)-1))
+ except KeyboardInterrupt:
+ raise
+ except:
+ try:
+ s = str(source[-1])
+ except KeyboardInterrupt:
+ raise
+ except:
+ return 0
+ return 4 + (len(s) - len(s.lstrip()))
+
+ def _getentrysource(self, entry):
+ source = entry.getsource(self.astcache)
+ if source is not None:
+ source = source.deindent()
+ return source
+
+ def _saferepr(self, obj):
+ return py.io.saferepr(obj)
+
+ def repr_args(self, entry):
+ if self.funcargs:
+ args = []
+ for argname, argvalue in entry.frame.getargs(var=True):
+ args.append((argname, self._saferepr(argvalue)))
+ return ReprFuncArgs(args)
+
+ def get_source(self, source, line_index=-1, excinfo=None, short=False):
+ """ return formatted and marked up source lines. """
+ lines = []
+ if source is None or line_index >= len(source.lines):
+ source = py.code.Source("???")
+ line_index = 0
+ if line_index < 0:
+ line_index += len(source)
+ space_prefix = " "
+ if short:
+ lines.append(space_prefix + source.lines[line_index].strip())
+ else:
+ for line in source.lines[:line_index]:
+ lines.append(space_prefix + line)
+ lines.append(self.flow_marker + " " + source.lines[line_index])
+ for line in source.lines[line_index+1:]:
+ lines.append(space_prefix + line)
+ if excinfo is not None:
+ indent = 4 if short else self._getindent(source)
+ lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
+ return lines
+
+ def get_exconly(self, excinfo, indent=4, markall=False):
+ lines = []
+ indent = " " * indent
+ # get the real exception information out
+ exlines = excinfo.exconly(tryshort=True).split('\n')
+ failindent = self.fail_marker + indent[1:]
+ for line in exlines:
+ lines.append(failindent + line)
+ if not markall:
+ failindent = indent
+ return lines
+
+ def repr_locals(self, locals):
+ if self.showlocals:
+ lines = []
+ keys = [loc for loc in locals if loc[0] != "@"]
+ keys.sort()
+ for name in keys:
+ value = locals[name]
+ if name == '__builtins__':
+ lines.append("__builtins__ = <builtins>")
+ else:
+ # This formatting could all be handled by the
+ # _repr() function, which is only reprlib.Repr in
+ # disguise, so is very configurable.
+ str_repr = self._saferepr(value)
+ #if len(str_repr) < 70 or not isinstance(value,
+ # (list, tuple, dict)):
+ lines.append("%-10s = %s" %(name, str_repr))
+ #else:
+ # self._line("%-10s =\\" % (name,))
+ # # XXX
+ # pprint.pprint(value, stream=self.excinfowriter)
+ return ReprLocals(lines)
+
+ def repr_traceback_entry(self, entry, excinfo=None):
+ source = self._getentrysource(entry)
+ if source is None:
+ source = py.code.Source("???")
+ line_index = 0
+ else:
+ # entry.getfirstlinesource() can be -1, should be 0 on jython
+ line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
+
+ lines = []
+ style = entry._repr_style
+ if style is None:
+ style = self.style
+ if style in ("short", "long"):
+ short = style == "short"
+ reprargs = self.repr_args(entry) if not short else None
+ s = self.get_source(source, line_index, excinfo, short=short)
+ lines.extend(s)
+ if short:
+ message = "in %s" %(entry.name)
+ else:
+ message = excinfo and excinfo.typename or ""
+ path = self._makepath(entry.path)
+ filelocrepr = ReprFileLocation(path, entry.lineno+1, message)
+ localsrepr = None
+ if not short:
+ localsrepr = self.repr_locals(entry.locals)
+ return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style)
+ if excinfo:
+ lines.extend(self.get_exconly(excinfo, indent=4))
+ return ReprEntry(lines, None, None, None, style)
+
+ def _makepath(self, path):
+ if not self.abspath:
+ try:
+ np = py.path.local().bestrelpath(path)
+ except OSError:
+ return path
+ if len(np) < len(str(path)):
+ path = np
+ return path
+
+ def repr_traceback(self, excinfo):
+ traceback = excinfo.traceback
+ if self.tbfilter:
+ traceback = traceback.filter()
+ recursionindex = None
+ if excinfo.errisinstance(RuntimeError):
+ if "maximum recursion depth exceeded" in str(excinfo.value):
+ recursionindex = traceback.recursionindex()
+ last = traceback[-1]
+ entries = []
+ extraline = None
+ for index, entry in enumerate(traceback):
+ einfo = (last == entry) and excinfo or None
+ reprentry = self.repr_traceback_entry(entry, einfo)
+ entries.append(reprentry)
+ if index == recursionindex:
+ extraline = "!!! Recursion detected (same locals & position)"
+ break
+ return ReprTraceback(entries, extraline, style=self.style)
+
+ def repr_excinfo(self, excinfo):
+ reprtraceback = self.repr_traceback(excinfo)
+ reprcrash = excinfo._getreprcrash()
+ return ReprExceptionInfo(reprtraceback, reprcrash)
+
+class TerminalRepr:
+ def __str__(self):
+ s = self.__unicode__()
+ if sys.version_info[0] < 3:
+ s = s.encode('utf-8')
+ return s
+
+ def __unicode__(self):
+ # FYI this is called from pytest-xdist's serialization of exception
+ # information.
+ io = py.io.TextIO()
+ tw = py.io.TerminalWriter(file=io)
+ self.toterminal(tw)
+ return io.getvalue().strip()
+
+ def __repr__(self):
+ return "<%s instance at %0x>" %(self.__class__, id(self))
+
+
+class ReprExceptionInfo(TerminalRepr):
+ def __init__(self, reprtraceback, reprcrash):
+ self.reprtraceback = reprtraceback
+ self.reprcrash = reprcrash
+ self.sections = []
+
+ def addsection(self, name, content, sep="-"):
+ self.sections.append((name, content, sep))
+
+ def toterminal(self, tw):
+ self.reprtraceback.toterminal(tw)
+ for name, content, sep in self.sections:
+ tw.sep(sep, name)
+ tw.line(content)
+
+class ReprTraceback(TerminalRepr):
+ entrysep = "_ "
+
+ def __init__(self, reprentries, extraline, style):
+ self.reprentries = reprentries
+ self.extraline = extraline
+ self.style = style
+
+ def toterminal(self, tw):
+ # the entries might have different styles
+ last_style = None
+ for i, entry in enumerate(self.reprentries):
+ if entry.style == "long":
+ tw.line("")
+ entry.toterminal(tw)
+ if i < len(self.reprentries) - 1:
+ next_entry = self.reprentries[i+1]
+ if entry.style == "long" or \
+ entry.style == "short" and next_entry.style == "long":
+ tw.sep(self.entrysep)
+
+ if self.extraline:
+ tw.line(self.extraline)
+
+class ReprTracebackNative(ReprTraceback):
+ def __init__(self, tblines):
+ self.style = "native"
+ self.reprentries = [ReprEntryNative(tblines)]
+ self.extraline = None
+
+class ReprEntryNative(TerminalRepr):
+ style = "native"
+
+ def __init__(self, tblines):
+ self.lines = tblines
+
+ def toterminal(self, tw):
+ tw.write("".join(self.lines))
+
+class ReprEntry(TerminalRepr):
+ localssep = "_ "
+
+ def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style):
+ self.lines = lines
+ self.reprfuncargs = reprfuncargs
+ self.reprlocals = reprlocals
+ self.reprfileloc = filelocrepr
+ self.style = style
+
+ def toterminal(self, tw):
+ if self.style == "short":
+ self.reprfileloc.toterminal(tw)
+ for line in self.lines:
+ red = line.startswith("E ")
+ tw.line(line, bold=True, red=red)
+ #tw.line("")
+ return
+ if self.reprfuncargs:
+ self.reprfuncargs.toterminal(tw)
+ for line in self.lines:
+ red = line.startswith("E ")
+ tw.line(line, bold=True, red=red)
+ if self.reprlocals:
+ #tw.sep(self.localssep, "Locals")
+ tw.line("")
+ self.reprlocals.toterminal(tw)
+ if self.reprfileloc:
+ if self.lines:
+ tw.line("")
+ self.reprfileloc.toterminal(tw)
+
+ def __str__(self):
+ return "%s\n%s\n%s" % ("\n".join(self.lines),
+ self.reprlocals,
+ self.reprfileloc)
+
+class ReprFileLocation(TerminalRepr):
+ def __init__(self, path, lineno, message):
+ self.path = str(path)
+ self.lineno = lineno
+ self.message = message
+
+ def toterminal(self, tw):
+ # filename and lineno output for each entry,
+ # using an output format that most editors unterstand
+ msg = self.message
+ i = msg.find("\n")
+ if i != -1:
+ msg = msg[:i]
+ tw.line("%s:%s: %s" %(self.path, self.lineno, msg))
+
+class ReprLocals(TerminalRepr):
+ def __init__(self, lines):
+ self.lines = lines
+
+ def toterminal(self, tw):
+ for line in self.lines:
+ tw.line(line)
+
+class ReprFuncArgs(TerminalRepr):
+ def __init__(self, args):
+ self.args = args
+
+ def toterminal(self, tw):
+ if self.args:
+ linesofar = ""
+ for name, value in self.args:
+ ns = "%s = %s" %(name, value)
+ if len(ns) + len(linesofar) + 2 > tw.fullwidth:
+ if linesofar:
+ tw.line(linesofar)
+ linesofar = ns
+ else:
+ if linesofar:
+ linesofar += ", " + ns
+ else:
+ linesofar = ns
+ if linesofar:
+ tw.line(linesofar)
+ tw.line("")
+
+
+
+oldbuiltins = {}
+
+def patch_builtins(assertion=True, compile=True):
+ """ put compile and AssertionError builtins to Python's builtins. """
+ if assertion:
+ from py._code import assertion
+ l = oldbuiltins.setdefault('AssertionError', [])
+ l.append(py.builtin.builtins.AssertionError)
+ py.builtin.builtins.AssertionError = assertion.AssertionError
+ if compile:
+ l = oldbuiltins.setdefault('compile', [])
+ l.append(py.builtin.builtins.compile)
+ py.builtin.builtins.compile = py.code.compile
+
+def unpatch_builtins(assertion=True, compile=True):
+ """ remove compile and AssertionError builtins from Python builtins. """
+ if assertion:
+ py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop()
+ if compile:
+ py.builtin.builtins.compile = oldbuiltins['compile'].pop()
+
+def getrawcode(obj, trycall=True):
+ """ return code object for given function. """
+ try:
+ return obj.__code__
+ except AttributeError:
+ obj = getattr(obj, 'im_func', obj)
+ obj = getattr(obj, 'func_code', obj)
+ obj = getattr(obj, 'f_code', obj)
+ obj = getattr(obj, '__code__', obj)
+ if trycall and not hasattr(obj, 'co_firstlineno'):
+ if hasattr(obj, '__call__') and not isclass(obj):
+ x = getrawcode(obj.__call__, trycall=False)
+ if hasattr(x, 'co_firstlineno'):
+ return x
+ return obj
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/source.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/source.py
new file mode 100644
index 00000000000..7fc7b23a96c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_code/source.py
@@ -0,0 +1,410 @@
+from __future__ import generators
+
+from bisect import bisect_right
+import sys
+import inspect, tokenize
+import py
+from types import ModuleType
+cpy_compile = compile
+
+try:
+ import _ast
+ from _ast import PyCF_ONLY_AST as _AST_FLAG
+except ImportError:
+ _AST_FLAG = 0
+ _ast = None
+
+
+class Source(object):
+ """ a immutable object holding a source code fragment,
+ possibly deindenting it.
+ """
+ _compilecounter = 0
+ def __init__(self, *parts, **kwargs):
+ self.lines = lines = []
+ de = kwargs.get('deindent', True)
+ rstrip = kwargs.get('rstrip', True)
+ for part in parts:
+ if not part:
+ partlines = []
+ if isinstance(part, Source):
+ partlines = part.lines
+ elif isinstance(part, (tuple, list)):
+ partlines = [x.rstrip("\n") for x in part]
+ elif isinstance(part, py.builtin._basestring):
+ partlines = part.split('\n')
+ if rstrip:
+ while partlines:
+ if partlines[-1].strip():
+ break
+ partlines.pop()
+ else:
+ partlines = getsource(part, deindent=de).lines
+ if de:
+ partlines = deindent(partlines)
+ lines.extend(partlines)
+
+ def __eq__(self, other):
+ try:
+ return self.lines == other.lines
+ except AttributeError:
+ if isinstance(other, str):
+ return str(self) == other
+ return False
+
+ def __getitem__(self, key):
+ if isinstance(key, int):
+ return self.lines[key]
+ else:
+ if key.step not in (None, 1):
+ raise IndexError("cannot slice a Source with a step")
+ return self.__getslice__(key.start, key.stop)
+
+ def __len__(self):
+ return len(self.lines)
+
+ def __getslice__(self, start, end):
+ newsource = Source()
+ newsource.lines = self.lines[start:end]
+ return newsource
+
+ def strip(self):
+ """ return new source object with trailing
+ and leading blank lines removed.
+ """
+ start, end = 0, len(self)
+ while start < end and not self.lines[start].strip():
+ start += 1
+ while end > start and not self.lines[end-1].strip():
+ end -= 1
+ source = Source()
+ source.lines[:] = self.lines[start:end]
+ return source
+
+ def putaround(self, before='', after='', indent=' ' * 4):
+ """ return a copy of the source object with
+ 'before' and 'after' wrapped around it.
+ """
+ before = Source(before)
+ after = Source(after)
+ newsource = Source()
+ lines = [ (indent + line) for line in self.lines]
+ newsource.lines = before.lines + lines + after.lines
+ return newsource
+
+ def indent(self, indent=' ' * 4):
+ """ return a copy of the source object with
+ all lines indented by the given indent-string.
+ """
+ newsource = Source()
+ newsource.lines = [(indent+line) for line in self.lines]
+ return newsource
+
+ def getstatement(self, lineno, assertion=False):
+ """ return Source statement which contains the
+ given linenumber (counted from 0).
+ """
+ start, end = self.getstatementrange(lineno, assertion)
+ return self[start:end]
+
+ def getstatementrange(self, lineno, assertion=False):
+ """ return (start, end) tuple which spans the minimal
+ statement region which containing the given lineno.
+ """
+ if not (0 <= lineno < len(self)):
+ raise IndexError("lineno out of range")
+ ast, start, end = getstatementrange_ast(lineno, self)
+ return start, end
+
+ def deindent(self, offset=None):
+ """ return a new source object deindented by offset.
+ If offset is None then guess an indentation offset from
+ the first non-blank line. Subsequent lines which have a
+ lower indentation offset will be copied verbatim as
+ they are assumed to be part of multilines.
+ """
+ # XXX maybe use the tokenizer to properly handle multiline
+ # strings etc.pp?
+ newsource = Source()
+ newsource.lines[:] = deindent(self.lines, offset)
+ return newsource
+
+ def isparseable(self, deindent=True):
+ """ return True if source is parseable, heuristically
+ deindenting it by default.
+ """
+ try:
+ import parser
+ except ImportError:
+ syntax_checker = lambda x: compile(x, 'asd', 'exec')
+ else:
+ syntax_checker = parser.suite
+
+ if deindent:
+ source = str(self.deindent())
+ else:
+ source = str(self)
+ try:
+ #compile(source+'\n', "x", "exec")
+ syntax_checker(source+'\n')
+ except KeyboardInterrupt:
+ raise
+ except Exception:
+ return False
+ else:
+ return True
+
+ def __str__(self):
+ return "\n".join(self.lines)
+
+ def compile(self, filename=None, mode='exec',
+ flag=generators.compiler_flag,
+ dont_inherit=0, _genframe=None):
+ """ return compiled code object. if filename is None
+ invent an artificial filename which displays
+ the source/line position of the caller frame.
+ """
+ if not filename or py.path.local(filename).check(file=0):
+ if _genframe is None:
+ _genframe = sys._getframe(1) # the caller
+ fn,lineno = _genframe.f_code.co_filename, _genframe.f_lineno
+ base = "<%d-codegen " % self._compilecounter
+ self.__class__._compilecounter += 1
+ if not filename:
+ filename = base + '%s:%d>' % (fn, lineno)
+ else:
+ filename = base + '%r %s:%d>' % (filename, fn, lineno)
+ source = "\n".join(self.lines) + '\n'
+ try:
+ co = cpy_compile(source, filename, mode, flag)
+ except SyntaxError:
+ ex = sys.exc_info()[1]
+ # re-represent syntax errors from parsing python strings
+ msglines = self.lines[:ex.lineno]
+ if ex.offset:
+ msglines.append(" "*ex.offset + '^')
+ msglines.append("(code was compiled probably from here: %s)" % filename)
+ newex = SyntaxError('\n'.join(msglines))
+ newex.offset = ex.offset
+ newex.lineno = ex.lineno
+ newex.text = ex.text
+ raise newex
+ else:
+ if flag & _AST_FLAG:
+ return co
+ lines = [(x + "\n") for x in self.lines]
+ import linecache
+ linecache.cache[filename] = (1, None, lines, filename)
+ return co
+
+#
+# public API shortcut functions
+#
+
+def compile_(source, filename=None, mode='exec', flags=
+ generators.compiler_flag, dont_inherit=0):
+ """ compile the given source to a raw code object,
+ and maintain an internal cache which allows later
+ retrieval of the source code for the code object
+ and any recursively created code objects.
+ """
+ if _ast is not None and isinstance(source, _ast.AST):
+ # XXX should Source support having AST?
+ return cpy_compile(source, filename, mode, flags, dont_inherit)
+ _genframe = sys._getframe(1) # the caller
+ s = Source(source)
+ co = s.compile(filename, mode, flags, _genframe=_genframe)
+ return co
+
+
+def getfslineno(obj):
+ """ Return source location (path, lineno) for the given object.
+ If the source cannot be determined return ("", -1)
+ """
+ try:
+ code = py.code.Code(obj)
+ except TypeError:
+ try:
+ fn = (inspect.getsourcefile(obj) or
+ inspect.getfile(obj))
+ except TypeError:
+ return "", -1
+
+ fspath = fn and py.path.local(fn) or None
+ lineno = -1
+ if fspath:
+ try:
+ _, lineno = findsource(obj)
+ except IOError:
+ pass
+ else:
+ fspath = code.path
+ lineno = code.firstlineno
+ assert isinstance(lineno, int)
+ return fspath, lineno
+
+#
+# helper functions
+#
+
+def findsource(obj):
+ try:
+ sourcelines, lineno = inspect.findsource(obj)
+ except py.builtin._sysex:
+ raise
+ except:
+ return None, -1
+ source = Source()
+ source.lines = [line.rstrip() for line in sourcelines]
+ return source, lineno
+
+def getsource(obj, **kwargs):
+ obj = py.code.getrawcode(obj)
+ try:
+ strsrc = inspect.getsource(obj)
+ except IndentationError:
+ strsrc = "\"Buggy python version consider upgrading, cannot get source\""
+ assert isinstance(strsrc, str)
+ return Source(strsrc, **kwargs)
+
+def deindent(lines, offset=None):
+ if offset is None:
+ for line in lines:
+ line = line.expandtabs()
+ s = line.lstrip()
+ if s:
+ offset = len(line)-len(s)
+ break
+ else:
+ offset = 0
+ if offset == 0:
+ return list(lines)
+ newlines = []
+ def readline_generator(lines):
+ for line in lines:
+ yield line + '\n'
+ while True:
+ yield ''
+
+ it = readline_generator(lines)
+
+ try:
+ for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(lambda: next(it)):
+ if sline > len(lines):
+ break # End of input reached
+ if sline > len(newlines):
+ line = lines[sline - 1].expandtabs()
+ if line.lstrip() and line[:offset].isspace():
+ line = line[offset:] # Deindent
+ newlines.append(line)
+
+ for i in range(sline, eline):
+ # Don't deindent continuing lines of
+ # multiline tokens (i.e. multiline strings)
+ newlines.append(lines[i])
+ except (IndentationError, tokenize.TokenError):
+ pass
+ # Add any lines we didn't see. E.g. if an exception was raised.
+ newlines.extend(lines[len(newlines):])
+ return newlines
+
+
+def get_statement_startend2(lineno, node):
+ import ast
+ # flatten all statements and except handlers into one lineno-list
+ # AST's line numbers start indexing at 1
+ l = []
+ for x in ast.walk(node):
+ if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler):
+ l.append(x.lineno - 1)
+ for name in "finalbody", "orelse":
+ val = getattr(x, name, None)
+ if val:
+ # treat the finally/orelse part as its own statement
+ l.append(val[0].lineno - 1 - 1)
+ l.sort()
+ insert_index = bisect_right(l, lineno)
+ start = l[insert_index - 1]
+ if insert_index >= len(l):
+ end = None
+ else:
+ end = l[insert_index]
+ return start, end
+
+
+def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
+ if astnode is None:
+ content = str(source)
+ try:
+ astnode = compile(content, "source", "exec", 1024) # 1024 for AST
+ except ValueError:
+ start, end = getstatementrange_old(lineno, source, assertion)
+ return None, start, end
+ start, end = get_statement_startend2(lineno, astnode)
+ # we need to correct the end:
+ # - ast-parsing strips comments
+ # - there might be empty lines
+ # - we might have lesser indented code blocks at the end
+ if end is None:
+ end = len(source.lines)
+
+ if end > start + 1:
+ # make sure we don't span differently indented code blocks
+ # by using the BlockFinder helper used which inspect.getsource() uses itself
+ block_finder = inspect.BlockFinder()
+ # if we start with an indented line, put blockfinder to "started" mode
+ block_finder.started = source.lines[start][0].isspace()
+ it = ((x + "\n") for x in source.lines[start:end])
+ try:
+ for tok in tokenize.generate_tokens(lambda: next(it)):
+ block_finder.tokeneater(*tok)
+ except (inspect.EndOfBlock, IndentationError):
+ end = block_finder.last + start
+ except Exception:
+ pass
+
+ # the end might still point to a comment or empty line, correct it
+ while end:
+ line = source.lines[end - 1].lstrip()
+ if line.startswith("#") or not line:
+ end -= 1
+ else:
+ break
+ return astnode, start, end
+
+
+def getstatementrange_old(lineno, source, assertion=False):
+ """ return (start, end) tuple which spans the minimal
+ statement region which containing the given lineno.
+ raise an IndexError if no such statementrange can be found.
+ """
+ # XXX this logic is only used on python2.4 and below
+ # 1. find the start of the statement
+ from codeop import compile_command
+ for start in range(lineno, -1, -1):
+ if assertion:
+ line = source.lines[start]
+ # the following lines are not fully tested, change with care
+ if 'super' in line and 'self' in line and '__init__' in line:
+ raise IndexError("likely a subclass")
+ if "assert" not in line and "raise" not in line:
+ continue
+ trylines = source.lines[start:lineno+1]
+ # quick hack to prepare parsing an indented line with
+ # compile_command() (which errors on "return" outside defs)
+ trylines.insert(0, 'def xxx():')
+ trysource = '\n '.join(trylines)
+ # ^ space here
+ try:
+ compile_command(trysource)
+ except (SyntaxError, OverflowError, ValueError):
+ continue
+
+ # 2. find the end of the statement
+ for end in range(lineno+1, len(source)+1):
+ trysource = source[start:end]
+ if trysource.isparseable():
+ return start, end
+ raise SyntaxError("no valid source range around line %d " % (lineno,))
+
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_error.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_error.py
new file mode 100644
index 00000000000..a6375de9fa2
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_error.py
@@ -0,0 +1,91 @@
+"""
+create errno-specific classes for IO or os calls.
+
+"""
+from types import ModuleType
+import sys, os, errno
+
+class Error(EnvironmentError):
+ def __repr__(self):
+ return "%s.%s %r: %s " %(self.__class__.__module__,
+ self.__class__.__name__,
+ self.__class__.__doc__,
+ " ".join(map(str, self.args)),
+ #repr(self.args)
+ )
+
+ def __str__(self):
+ s = "[%s]: %s" %(self.__class__.__doc__,
+ " ".join(map(str, self.args)),
+ )
+ return s
+
+_winerrnomap = {
+ 2: errno.ENOENT,
+ 3: errno.ENOENT,
+ 17: errno.EEXIST,
+ 18: errno.EXDEV,
+ 13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailiable
+ 22: errno.ENOTDIR,
+ 20: errno.ENOTDIR,
+ 267: errno.ENOTDIR,
+ 5: errno.EACCES, # anything better?
+}
+
+class ErrorMaker(ModuleType):
+ """ lazily provides Exception classes for each possible POSIX errno
+ (as defined per the 'errno' module). All such instances
+ subclass EnvironmentError.
+ """
+ Error = Error
+ _errno2class = {}
+
+ def __getattr__(self, name):
+ if name[0] == "_":
+ raise AttributeError(name)
+ eno = getattr(errno, name)
+ cls = self._geterrnoclass(eno)
+ setattr(self, name, cls)
+ return cls
+
+ def _geterrnoclass(self, eno):
+ try:
+ return self._errno2class[eno]
+ except KeyError:
+ clsname = errno.errorcode.get(eno, "UnknownErrno%d" %(eno,))
+ errorcls = type(Error)(clsname, (Error,),
+ {'__module__':'py.error',
+ '__doc__': os.strerror(eno)})
+ self._errno2class[eno] = errorcls
+ return errorcls
+
+ def checked_call(self, func, *args, **kwargs):
+ """ call a function and raise an errno-exception if applicable. """
+ __tracebackhide__ = True
+ try:
+ return func(*args, **kwargs)
+ except self.Error:
+ raise
+ except (OSError, EnvironmentError):
+ cls, value, tb = sys.exc_info()
+ if not hasattr(value, 'errno'):
+ raise
+ __tracebackhide__ = False
+ errno = value.errno
+ try:
+ if not isinstance(value, WindowsError):
+ raise NameError
+ except NameError:
+ # we are not on Windows, or we got a proper OSError
+ cls = self._geterrnoclass(errno)
+ else:
+ try:
+ cls = self._geterrnoclass(_winerrnomap[errno])
+ except KeyError:
+ raise value
+ raise cls("%s%r" % (func.__name__, args))
+ __tracebackhide__ = True
+
+
+error = ErrorMaker('py.error')
+sys.modules[error.__name__] = error \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_io/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_io/__init__.py
index 835f01f3ab9..835f01f3ab9 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_io/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_io/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_io/capture.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_io/capture.py
index bc157ed978f..bc157ed978f 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_io/capture.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_io/capture.py
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_io/saferepr.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_io/saferepr.py
index 8518290efdd..8518290efdd 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_io/saferepr.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_io/saferepr.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_io/terminalwriter.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_io/terminalwriter.py
new file mode 100644
index 00000000000..74d31259dac
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_io/terminalwriter.py
@@ -0,0 +1,384 @@
+"""
+
+Helper functions for writing to terminals and files.
+
+"""
+
+
+import sys, os
+import py
+py3k = sys.version_info[0] >= 3
+from py.builtin import text, bytes
+
+win32_and_ctypes = False
+colorama = None
+if sys.platform == "win32":
+ try:
+ import colorama
+ except ImportError:
+ try:
+ import ctypes
+ win32_and_ctypes = True
+ except ImportError:
+ pass
+
+
+def _getdimensions():
+ import termios,fcntl,struct
+ call = fcntl.ioctl(1,termios.TIOCGWINSZ,"\000"*8)
+ height,width = struct.unpack( "hhhh", call ) [:2]
+ return height, width
+
+
+def get_terminal_width():
+ width = 0
+ try:
+ _, width = _getdimensions()
+ except py.builtin._sysex:
+ raise
+ except:
+ # pass to fallback below
+ pass
+
+ if width == 0:
+ # FALLBACK:
+ # * some exception happened
+ # * or this is emacs terminal which reports (0,0)
+ width = int(os.environ.get('COLUMNS', 80))
+
+ # XXX the windows getdimensions may be bogus, let's sanify a bit
+ if width < 40:
+ width = 80
+ return width
+
+terminal_width = get_terminal_width()
+
+# XXX unify with _escaped func below
+def ansi_print(text, esc, file=None, newline=True, flush=False):
+ if file is None:
+ file = sys.stderr
+ text = text.rstrip()
+ if esc and not isinstance(esc, tuple):
+ esc = (esc,)
+ if esc and sys.platform != "win32" and file.isatty():
+ text = (''.join(['\x1b[%sm' % cod for cod in esc]) +
+ text +
+ '\x1b[0m') # ANSI color code "reset"
+ if newline:
+ text += '\n'
+
+ if esc and win32_and_ctypes and file.isatty():
+ if 1 in esc:
+ bold = True
+ esc = tuple([x for x in esc if x != 1])
+ else:
+ bold = False
+ esctable = {() : FOREGROUND_WHITE, # normal
+ (31,): FOREGROUND_RED, # red
+ (32,): FOREGROUND_GREEN, # green
+ (33,): FOREGROUND_GREEN|FOREGROUND_RED, # yellow
+ (34,): FOREGROUND_BLUE, # blue
+ (35,): FOREGROUND_BLUE|FOREGROUND_RED, # purple
+ (36,): FOREGROUND_BLUE|FOREGROUND_GREEN, # cyan
+ (37,): FOREGROUND_WHITE, # white
+ (39,): FOREGROUND_WHITE, # reset
+ }
+ attr = esctable.get(esc, FOREGROUND_WHITE)
+ if bold:
+ attr |= FOREGROUND_INTENSITY
+ STD_OUTPUT_HANDLE = -11
+ STD_ERROR_HANDLE = -12
+ if file is sys.stderr:
+ handle = GetStdHandle(STD_ERROR_HANDLE)
+ else:
+ handle = GetStdHandle(STD_OUTPUT_HANDLE)
+ oldcolors = GetConsoleInfo(handle).wAttributes
+ attr |= (oldcolors & 0x0f0)
+ SetConsoleTextAttribute(handle, attr)
+ while len(text) > 32768:
+ file.write(text[:32768])
+ text = text[32768:]
+ if text:
+ file.write(text)
+ SetConsoleTextAttribute(handle, oldcolors)
+ else:
+ file.write(text)
+
+ if flush:
+ file.flush()
+
+def should_do_markup(file):
+ if os.environ.get('PY_COLORS') == '1':
+ return True
+ if os.environ.get('PY_COLORS') == '0':
+ return False
+ return hasattr(file, 'isatty') and file.isatty() \
+ and os.environ.get('TERM') != 'dumb' \
+ and not (sys.platform.startswith('java') and os._name == 'nt')
+
+class TerminalWriter(object):
+ _esctable = dict(black=30, red=31, green=32, yellow=33,
+ blue=34, purple=35, cyan=36, white=37,
+ Black=40, Red=41, Green=42, Yellow=43,
+ Blue=44, Purple=45, Cyan=46, White=47,
+ bold=1, light=2, blink=5, invert=7)
+
+ # XXX deprecate stringio argument
+ def __init__(self, file=None, stringio=False, encoding=None):
+ if file is None:
+ if stringio:
+ self.stringio = file = py.io.TextIO()
+ else:
+ from sys import stdout as file
+ elif py.builtin.callable(file) and not (
+ hasattr(file, "write") and hasattr(file, "flush")):
+ file = WriteFile(file, encoding=encoding)
+ if hasattr(file, "isatty") and file.isatty() and colorama:
+ file = colorama.AnsiToWin32(file).stream
+ self.encoding = encoding or getattr(file, 'encoding', "utf-8")
+ self._file = file
+ self.hasmarkup = should_do_markup(file)
+ self._lastlen = 0
+ self._chars_on_current_line = 0
+
+ @property
+ def fullwidth(self):
+ if hasattr(self, '_terminal_width'):
+ return self._terminal_width
+ return get_terminal_width()
+
+ @fullwidth.setter
+ def fullwidth(self, value):
+ self._terminal_width = value
+
+ @property
+ def chars_on_current_line(self):
+ """Return the number of characters written so far in the current line.
+
+ Please note that this count does not produce correct results after a reline() call,
+ see #164.
+
+ .. versionadded:: 1.5.0
+
+ :rtype: int
+ """
+ return self._chars_on_current_line
+
+ def _escaped(self, text, esc):
+ if esc and self.hasmarkup:
+ text = (''.join(['\x1b[%sm' % cod for cod in esc]) +
+ text +'\x1b[0m')
+ return text
+
+ def markup(self, text, **kw):
+ esc = []
+ for name in kw:
+ if name not in self._esctable:
+ raise ValueError("unknown markup: %r" %(name,))
+ if kw[name]:
+ esc.append(self._esctable[name])
+ return self._escaped(text, tuple(esc))
+
+ def sep(self, sepchar, title=None, fullwidth=None, **kw):
+ if fullwidth is None:
+ fullwidth = self.fullwidth
+ # the goal is to have the line be as long as possible
+ # under the condition that len(line) <= fullwidth
+ if sys.platform == "win32":
+ # if we print in the last column on windows we are on a
+ # new line but there is no way to verify/neutralize this
+ # (we may not know the exact line width)
+ # so let's be defensive to avoid empty lines in the output
+ fullwidth -= 1
+ if title is not None:
+ # we want 2 + 2*len(fill) + len(title) <= fullwidth
+ # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth
+ # 2*len(sepchar)*N <= fullwidth - len(title) - 2
+ # N <= (fullwidth - len(title) - 2) // (2*len(sepchar))
+ N = (fullwidth - len(title) - 2) // (2*len(sepchar))
+ fill = sepchar * N
+ line = "%s %s %s" % (fill, title, fill)
+ else:
+ # we want len(sepchar)*N <= fullwidth
+ # i.e. N <= fullwidth // len(sepchar)
+ line = sepchar * (fullwidth // len(sepchar))
+ # in some situations there is room for an extra sepchar at the right,
+ # in particular if we consider that with a sepchar like "_ " the
+ # trailing space is not important at the end of the line
+ if len(line) + len(sepchar.rstrip()) <= fullwidth:
+ line += sepchar.rstrip()
+
+ self.line(line, **kw)
+
+ def write(self, msg, **kw):
+ if msg:
+ if not isinstance(msg, (bytes, text)):
+ msg = text(msg)
+
+ self._update_chars_on_current_line(msg)
+
+ if self.hasmarkup and kw:
+ markupmsg = self.markup(msg, **kw)
+ else:
+ markupmsg = msg
+ write_out(self._file, markupmsg)
+
+ def _update_chars_on_current_line(self, text):
+ fields = text.rsplit('\n', 1)
+ if '\n' in text:
+ self._chars_on_current_line = len(fields[-1])
+ else:
+ self._chars_on_current_line += len(fields[-1])
+
+ def line(self, s='', **kw):
+ self.write(s, **kw)
+ self._checkfill(s)
+ self.write('\n')
+
+ def reline(self, line, **kw):
+ if not self.hasmarkup:
+ raise ValueError("cannot use rewrite-line without terminal")
+ self.write(line, **kw)
+ self._checkfill(line)
+ self.write('\r')
+ self._lastlen = len(line)
+
+ def _checkfill(self, line):
+ diff2last = self._lastlen - len(line)
+ if diff2last > 0:
+ self.write(" " * diff2last)
+
+class Win32ConsoleWriter(TerminalWriter):
+ def write(self, msg, **kw):
+ if msg:
+ if not isinstance(msg, (bytes, text)):
+ msg = text(msg)
+
+ self._update_chars_on_current_line(msg)
+
+ oldcolors = None
+ if self.hasmarkup and kw:
+ handle = GetStdHandle(STD_OUTPUT_HANDLE)
+ oldcolors = GetConsoleInfo(handle).wAttributes
+ default_bg = oldcolors & 0x00F0
+ attr = default_bg
+ if kw.pop('bold', False):
+ attr |= FOREGROUND_INTENSITY
+
+ if kw.pop('red', False):
+ attr |= FOREGROUND_RED
+ elif kw.pop('blue', False):
+ attr |= FOREGROUND_BLUE
+ elif kw.pop('green', False):
+ attr |= FOREGROUND_GREEN
+ elif kw.pop('yellow', False):
+ attr |= FOREGROUND_GREEN|FOREGROUND_RED
+ else:
+ attr |= oldcolors & 0x0007
+
+ SetConsoleTextAttribute(handle, attr)
+ write_out(self._file, msg)
+ if oldcolors:
+ SetConsoleTextAttribute(handle, oldcolors)
+
+class WriteFile(object):
+ def __init__(self, writemethod, encoding=None):
+ self.encoding = encoding
+ self._writemethod = writemethod
+
+ def write(self, data):
+ if self.encoding:
+ data = data.encode(self.encoding, "replace")
+ self._writemethod(data)
+
+ def flush(self):
+ return
+
+
+if win32_and_ctypes:
+ TerminalWriter = Win32ConsoleWriter
+ import ctypes
+ from ctypes import wintypes
+
+ # ctypes access to the Windows console
+ STD_OUTPUT_HANDLE = -11
+ STD_ERROR_HANDLE = -12
+ FOREGROUND_BLACK = 0x0000 # black text
+ FOREGROUND_BLUE = 0x0001 # text color contains blue.
+ FOREGROUND_GREEN = 0x0002 # text color contains green.
+ FOREGROUND_RED = 0x0004 # text color contains red.
+ FOREGROUND_WHITE = 0x0007
+ FOREGROUND_INTENSITY = 0x0008 # text color is intensified.
+ BACKGROUND_BLACK = 0x0000 # background color black
+ BACKGROUND_BLUE = 0x0010 # background color contains blue.
+ BACKGROUND_GREEN = 0x0020 # background color contains green.
+ BACKGROUND_RED = 0x0040 # background color contains red.
+ BACKGROUND_WHITE = 0x0070
+ BACKGROUND_INTENSITY = 0x0080 # background color is intensified.
+
+ SHORT = ctypes.c_short
+ class COORD(ctypes.Structure):
+ _fields_ = [('X', SHORT),
+ ('Y', SHORT)]
+ class SMALL_RECT(ctypes.Structure):
+ _fields_ = [('Left', SHORT),
+ ('Top', SHORT),
+ ('Right', SHORT),
+ ('Bottom', SHORT)]
+ class CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
+ _fields_ = [('dwSize', COORD),
+ ('dwCursorPosition', COORD),
+ ('wAttributes', wintypes.WORD),
+ ('srWindow', SMALL_RECT),
+ ('dwMaximumWindowSize', COORD)]
+
+ _GetStdHandle = ctypes.windll.kernel32.GetStdHandle
+ _GetStdHandle.argtypes = [wintypes.DWORD]
+ _GetStdHandle.restype = wintypes.HANDLE
+ def GetStdHandle(kind):
+ return _GetStdHandle(kind)
+
+ SetConsoleTextAttribute = ctypes.windll.kernel32.SetConsoleTextAttribute
+ SetConsoleTextAttribute.argtypes = [wintypes.HANDLE, wintypes.WORD]
+ SetConsoleTextAttribute.restype = wintypes.BOOL
+
+ _GetConsoleScreenBufferInfo = \
+ ctypes.windll.kernel32.GetConsoleScreenBufferInfo
+ _GetConsoleScreenBufferInfo.argtypes = [wintypes.HANDLE,
+ ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO)]
+ _GetConsoleScreenBufferInfo.restype = wintypes.BOOL
+ def GetConsoleInfo(handle):
+ info = CONSOLE_SCREEN_BUFFER_INFO()
+ _GetConsoleScreenBufferInfo(handle, ctypes.byref(info))
+ return info
+
+ def _getdimensions():
+ handle = GetStdHandle(STD_OUTPUT_HANDLE)
+ info = GetConsoleInfo(handle)
+ # Substract one from the width, otherwise the cursor wraps
+ # and the ending \n causes an empty line to display.
+ return info.dwSize.Y, info.dwSize.X - 1
+
+def write_out(fil, msg):
+ # XXX sometimes "msg" is of type bytes, sometimes text which
+ # complicates the situation. Should we try to enforce unicode?
+ try:
+ # on py27 and above writing out to sys.stdout with an encoding
+ # should usually work for unicode messages (if the encoding is
+ # capable of it)
+ fil.write(msg)
+ except UnicodeEncodeError:
+ # on py26 it might not work because stdout expects bytes
+ if fil.encoding:
+ try:
+ fil.write(msg.encode(fil.encoding))
+ except UnicodeEncodeError:
+ # it might still fail if the encoding is not capable
+ pass
+ else:
+ fil.flush()
+ return
+ # fallback: escape all unicode characters
+ msg = msg.encode("unicode-escape").decode("ascii")
+ fil.write(msg)
+ fil.flush()
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_log/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_log/__init__.py
index fad62e960d4..fad62e960d4 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_log/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_log/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_log/log.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_log/log.py
new file mode 100644
index 00000000000..56969bcb58c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_log/log.py
@@ -0,0 +1,206 @@
+"""
+basic logging functionality based on a producer/consumer scheme.
+
+XXX implement this API: (maybe put it into slogger.py?)
+
+ log = Logger(
+ info=py.log.STDOUT,
+ debug=py.log.STDOUT,
+ command=None)
+ log.info("hello", "world")
+ log.command("hello", "world")
+
+ log = Logger(info=Logger(something=...),
+ debug=py.log.STDOUT,
+ command=None)
+"""
+import py
+import sys
+
+
+class Message(object):
+ def __init__(self, keywords, args):
+ self.keywords = keywords
+ self.args = args
+
+ def content(self):
+ return " ".join(map(str, self.args))
+
+ def prefix(self):
+ return "[%s] " % (":".join(self.keywords))
+
+ def __str__(self):
+ return self.prefix() + self.content()
+
+
+class Producer(object):
+ """ (deprecated) Log producer API which sends messages to be logged
+ to a 'consumer' object, which then prints them to stdout,
+ stderr, files, etc. Used extensively by PyPy-1.1.
+ """
+
+ Message = Message # to allow later customization
+ keywords2consumer = {}
+
+ def __init__(self, keywords, keywordmapper=None, **kw):
+ if hasattr(keywords, 'split'):
+ keywords = tuple(keywords.split())
+ self._keywords = keywords
+ if keywordmapper is None:
+ keywordmapper = default_keywordmapper
+ self._keywordmapper = keywordmapper
+
+ def __repr__(self):
+ return "<py.log.Producer %s>" % ":".join(self._keywords)
+
+ def __getattr__(self, name):
+ if '_' in name:
+ raise AttributeError(name)
+ producer = self.__class__(self._keywords + (name,))
+ setattr(self, name, producer)
+ return producer
+
+ def __call__(self, *args):
+ """ write a message to the appropriate consumer(s) """
+ func = self._keywordmapper.getconsumer(self._keywords)
+ if func is not None:
+ func(self.Message(self._keywords, args))
+
+class KeywordMapper:
+ def __init__(self):
+ self.keywords2consumer = {}
+
+ def getstate(self):
+ return self.keywords2consumer.copy()
+
+ def setstate(self, state):
+ self.keywords2consumer.clear()
+ self.keywords2consumer.update(state)
+
+ def getconsumer(self, keywords):
+ """ return a consumer matching the given keywords.
+
+ tries to find the most suitable consumer by walking, starting from
+ the back, the list of keywords, the first consumer matching a
+ keyword is returned (falling back to py.log.default)
+ """
+ for i in range(len(keywords), 0, -1):
+ try:
+ return self.keywords2consumer[keywords[:i]]
+ except KeyError:
+ continue
+ return self.keywords2consumer.get('default', default_consumer)
+
+ def setconsumer(self, keywords, consumer):
+ """ set a consumer for a set of keywords. """
+ # normalize to tuples
+ if isinstance(keywords, str):
+ keywords = tuple(filter(None, keywords.split()))
+ elif hasattr(keywords, '_keywords'):
+ keywords = keywords._keywords
+ elif not isinstance(keywords, tuple):
+ raise TypeError("key %r is not a string or tuple" % (keywords,))
+ if consumer is not None and not py.builtin.callable(consumer):
+ if not hasattr(consumer, 'write'):
+ raise TypeError(
+ "%r should be None, callable or file-like" % (consumer,))
+ consumer = File(consumer)
+ self.keywords2consumer[keywords] = consumer
+
+
+def default_consumer(msg):
+ """ the default consumer, prints the message to stdout (using 'print') """
+ sys.stderr.write(str(msg)+"\n")
+
+default_keywordmapper = KeywordMapper()
+
+
+def setconsumer(keywords, consumer):
+ default_keywordmapper.setconsumer(keywords, consumer)
+
+
+def setstate(state):
+ default_keywordmapper.setstate(state)
+
+
+def getstate():
+ return default_keywordmapper.getstate()
+
+#
+# Consumers
+#
+
+
+class File(object):
+ """ log consumer wrapping a file(-like) object """
+ def __init__(self, f):
+ assert hasattr(f, 'write')
+ # assert isinstance(f, file) or not hasattr(f, 'open')
+ self._file = f
+
+ def __call__(self, msg):
+ """ write a message to the log """
+ self._file.write(str(msg) + "\n")
+ if hasattr(self._file, 'flush'):
+ self._file.flush()
+
+
+class Path(object):
+ """ log consumer that opens and writes to a Path """
+ def __init__(self, filename, append=False,
+ delayed_create=False, buffering=False):
+ self._append = append
+ self._filename = str(filename)
+ self._buffering = buffering
+ if not delayed_create:
+ self._openfile()
+
+ def _openfile(self):
+ mode = self._append and 'a' or 'w'
+ f = open(self._filename, mode)
+ self._file = f
+
+ def __call__(self, msg):
+ """ write a message to the log """
+ if not hasattr(self, "_file"):
+ self._openfile()
+ self._file.write(str(msg) + "\n")
+ if not self._buffering:
+ self._file.flush()
+
+
+def STDOUT(msg):
+ """ consumer that writes to sys.stdout """
+ sys.stdout.write(str(msg)+"\n")
+
+
+def STDERR(msg):
+ """ consumer that writes to sys.stderr """
+ sys.stderr.write(str(msg)+"\n")
+
+
+class Syslog:
+ """ consumer that writes to the syslog daemon """
+
+ def __init__(self, priority=None):
+ if priority is None:
+ priority = self.LOG_INFO
+ self.priority = priority
+
+ def __call__(self, msg):
+ """ write a message to the log """
+ import syslog
+ syslog.syslog(self.priority, str(msg))
+
+
+try:
+ import syslog
+except ImportError:
+ pass
+else:
+ for _prio in "EMERG ALERT CRIT ERR WARNING NOTICE INFO DEBUG".split():
+ _prio = "LOG_" + _prio
+ try:
+ setattr(Syslog, _prio, getattr(syslog, _prio))
+ except AttributeError:
+ pass
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_log/warning.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_log/warning.py
new file mode 100644
index 00000000000..6ef20d98a2d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_log/warning.py
@@ -0,0 +1,79 @@
+import py, sys
+
+class DeprecationWarning(DeprecationWarning):
+ def __init__(self, msg, path, lineno):
+ self.msg = msg
+ self.path = path
+ self.lineno = lineno
+ def __repr__(self):
+ return "%s:%d: %s" %(self.path, self.lineno+1, self.msg)
+ def __str__(self):
+ return self.msg
+
+def _apiwarn(startversion, msg, stacklevel=2, function=None):
+ # below is mostly COPIED from python2.4/warnings.py's def warn()
+ # Get context information
+ if isinstance(stacklevel, str):
+ frame = sys._getframe(1)
+ level = 1
+ found = frame.f_code.co_filename.find(stacklevel) != -1
+ while frame:
+ co = frame.f_code
+ if co.co_filename.find(stacklevel) == -1:
+ if found:
+ stacklevel = level
+ break
+ else:
+ found = True
+ level += 1
+ frame = frame.f_back
+ else:
+ stacklevel = 1
+ msg = "%s (since version %s)" %(msg, startversion)
+ warn(msg, stacklevel=stacklevel+1, function=function)
+
+
+def warn(msg, stacklevel=1, function=None):
+ if function is not None:
+ import inspect
+ filename = inspect.getfile(function)
+ lineno = py.code.getrawcode(function).co_firstlineno
+ else:
+ try:
+ caller = sys._getframe(stacklevel)
+ except ValueError:
+ globals = sys.__dict__
+ lineno = 1
+ else:
+ globals = caller.f_globals
+ lineno = caller.f_lineno
+ if '__name__' in globals:
+ module = globals['__name__']
+ else:
+ module = "<string>"
+ filename = globals.get('__file__')
+ if filename:
+ fnl = filename.lower()
+ if fnl.endswith(".pyc") or fnl.endswith(".pyo"):
+ filename = filename[:-1]
+ elif fnl.endswith("$py.class"):
+ filename = filename.replace('$py.class', '.py')
+ else:
+ if module == "__main__":
+ try:
+ filename = sys.argv[0]
+ except AttributeError:
+ # embedded interpreters don't have sys.argv, see bug #839151
+ filename = '__main__'
+ if not filename:
+ filename = module
+ path = py.path.local(filename)
+ warning = DeprecationWarning(msg, path, lineno)
+ import warnings
+ warnings.warn_explicit(warning, category=Warning,
+ filename=str(warning.path),
+ lineno=warning.lineno,
+ registry=warnings.__dict__.setdefault(
+ "__warningsregistry__", {})
+ )
+
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_path/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/__init__.py
index 51f3246f807..51f3246f807 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_path/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_path/cacheutil.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/cacheutil.py
index 99225047502..99225047502 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_path/cacheutil.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/cacheutil.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/common.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/common.py
new file mode 100644
index 00000000000..2d490b56a86
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/common.py
@@ -0,0 +1,453 @@
+"""
+"""
+import warnings
+import os
+import sys
+import posixpath
+import fnmatch
+import py
+
+# Moved from local.py.
+iswin32 = sys.platform == "win32" or (getattr(os, '_name', False) == 'nt')
+
+try:
+ from os import fspath
+except ImportError:
+ def fspath(path):
+ """
+ Return the string representation of the path.
+ If str or bytes is passed in, it is returned unchanged.
+ This code comes from PEP 519, modified to support earlier versions of
+ python.
+
+ This is required for python < 3.6.
+ """
+ if isinstance(path, (py.builtin.text, py.builtin.bytes)):
+ return path
+
+ # Work from the object's type to match method resolution of other magic
+ # methods.
+ path_type = type(path)
+ try:
+ return path_type.__fspath__(path)
+ except AttributeError:
+ if hasattr(path_type, '__fspath__'):
+ raise
+ try:
+ import pathlib
+ except ImportError:
+ pass
+ else:
+ if isinstance(path, pathlib.PurePath):
+ return py.builtin.text(path)
+
+ raise TypeError("expected str, bytes or os.PathLike object, not "
+ + path_type.__name__)
+
+class Checkers:
+ _depend_on_existence = 'exists', 'link', 'dir', 'file'
+
+ def __init__(self, path):
+ self.path = path
+
+ def dir(self):
+ raise NotImplementedError
+
+ def file(self):
+ raise NotImplementedError
+
+ def dotfile(self):
+ return self.path.basename.startswith('.')
+
+ def ext(self, arg):
+ if not arg.startswith('.'):
+ arg = '.' + arg
+ return self.path.ext == arg
+
+ def exists(self):
+ raise NotImplementedError
+
+ def basename(self, arg):
+ return self.path.basename == arg
+
+ def basestarts(self, arg):
+ return self.path.basename.startswith(arg)
+
+ def relto(self, arg):
+ return self.path.relto(arg)
+
+ def fnmatch(self, arg):
+ return self.path.fnmatch(arg)
+
+ def endswith(self, arg):
+ return str(self.path).endswith(arg)
+
+ def _evaluate(self, kw):
+ for name, value in kw.items():
+ invert = False
+ meth = None
+ try:
+ meth = getattr(self, name)
+ except AttributeError:
+ if name[:3] == 'not':
+ invert = True
+ try:
+ meth = getattr(self, name[3:])
+ except AttributeError:
+ pass
+ if meth is None:
+ raise TypeError(
+ "no %r checker available for %r" % (name, self.path))
+ try:
+ if py.code.getrawcode(meth).co_argcount > 1:
+ if (not meth(value)) ^ invert:
+ return False
+ else:
+ if bool(value) ^ bool(meth()) ^ invert:
+ return False
+ except (py.error.ENOENT, py.error.ENOTDIR, py.error.EBUSY):
+ # EBUSY feels not entirely correct,
+ # but its kind of necessary since ENOMEDIUM
+ # is not accessible in python
+ for name in self._depend_on_existence:
+ if name in kw:
+ if kw.get(name):
+ return False
+ name = 'not' + name
+ if name in kw:
+ if not kw.get(name):
+ return False
+ return True
+
+class NeverRaised(Exception):
+ pass
+
+class PathBase(object):
+ """ shared implementation for filesystem path objects."""
+ Checkers = Checkers
+
+ def __div__(self, other):
+ return self.join(fspath(other))
+ __truediv__ = __div__ # py3k
+
+ def basename(self):
+ """ basename part of path. """
+ return self._getbyspec('basename')[0]
+ basename = property(basename, None, None, basename.__doc__)
+
+ def dirname(self):
+ """ dirname part of path. """
+ return self._getbyspec('dirname')[0]
+ dirname = property(dirname, None, None, dirname.__doc__)
+
+ def purebasename(self):
+ """ pure base name of the path."""
+ return self._getbyspec('purebasename')[0]
+ purebasename = property(purebasename, None, None, purebasename.__doc__)
+
+ def ext(self):
+ """ extension of the path (including the '.')."""
+ return self._getbyspec('ext')[0]
+ ext = property(ext, None, None, ext.__doc__)
+
+ def dirpath(self, *args, **kwargs):
+ """ return the directory path joined with any given path arguments. """
+ return self.new(basename='').join(*args, **kwargs)
+
+ def read_binary(self):
+ """ read and return a bytestring from reading the path. """
+ with self.open('rb') as f:
+ return f.read()
+
+ def read_text(self, encoding):
+ """ read and return a Unicode string from reading the path. """
+ with self.open("r", encoding=encoding) as f:
+ return f.read()
+
+
+ def read(self, mode='r'):
+ """ read and return a bytestring from reading the path. """
+ with self.open(mode) as f:
+ return f.read()
+
+ def readlines(self, cr=1):
+ """ read and return a list of lines from the path. if cr is False, the
+newline will be removed from the end of each line. """
+ if sys.version_info < (3, ):
+ mode = 'rU'
+ else: # python 3 deprecates mode "U" in favor of "newline" option
+ mode = 'r'
+
+ if not cr:
+ content = self.read(mode)
+ return content.split('\n')
+ else:
+ f = self.open(mode)
+ try:
+ return f.readlines()
+ finally:
+ f.close()
+
+ def load(self):
+ """ (deprecated) return object unpickled from self.read() """
+ f = self.open('rb')
+ try:
+ import pickle
+ return py.error.checked_call(pickle.load, f)
+ finally:
+ f.close()
+
+ def move(self, target):
+ """ move this path to target. """
+ if target.relto(self):
+ raise py.error.EINVAL(
+ target,
+ "cannot move path into a subdirectory of itself")
+ try:
+ self.rename(target)
+ except py.error.EXDEV: # invalid cross-device link
+ self.copy(target)
+ self.remove()
+
+ def __repr__(self):
+ """ return a string representation of this path. """
+ return repr(str(self))
+
+ def check(self, **kw):
+ """ check a path for existence and properties.
+
+ Without arguments, return True if the path exists, otherwise False.
+
+ valid checkers::
+
+ file=1 # is a file
+ file=0 # is not a file (may not even exist)
+ dir=1 # is a dir
+ link=1 # is a link
+ exists=1 # exists
+
+ You can specify multiple checker definitions, for example::
+
+ path.check(file=1, link=1) # a link pointing to a file
+ """
+ if not kw:
+ kw = {'exists': 1}
+ return self.Checkers(self)._evaluate(kw)
+
+ def fnmatch(self, pattern):
+ """return true if the basename/fullname matches the glob-'pattern'.
+
+ valid pattern characters::
+
+ * matches everything
+ ? matches any single character
+ [seq] matches any character in seq
+ [!seq] matches any char not in seq
+
+ If the pattern contains a path-separator then the full path
+ is used for pattern matching and a '*' is prepended to the
+ pattern.
+
+ if the pattern doesn't contain a path-separator the pattern
+ is only matched against the basename.
+ """
+ return FNMatcher(pattern)(self)
+
+ def relto(self, relpath):
+ """ return a string which is the relative part of the path
+ to the given 'relpath'.
+ """
+ if not isinstance(relpath, (str, PathBase)):
+ raise TypeError("%r: not a string or path object" %(relpath,))
+ strrelpath = str(relpath)
+ if strrelpath and strrelpath[-1] != self.sep:
+ strrelpath += self.sep
+ #assert strrelpath[-1] == self.sep
+ #assert strrelpath[-2] != self.sep
+ strself = self.strpath
+ if sys.platform == "win32" or getattr(os, '_name', None) == 'nt':
+ if os.path.normcase(strself).startswith(
+ os.path.normcase(strrelpath)):
+ return strself[len(strrelpath):]
+ elif strself.startswith(strrelpath):
+ return strself[len(strrelpath):]
+ return ""
+
+ def ensure_dir(self, *args):
+ """ ensure the path joined with args is a directory. """
+ return self.ensure(*args, **{"dir": True})
+
+ def bestrelpath(self, dest):
+ """ return a string which is a relative path from self
+ (assumed to be a directory) to dest such that
+ self.join(bestrelpath) == dest and if not such
+ path can be determined return dest.
+ """
+ try:
+ if self == dest:
+ return os.curdir
+ base = self.common(dest)
+ if not base: # can be the case on windows
+ return str(dest)
+ self2base = self.relto(base)
+ reldest = dest.relto(base)
+ if self2base:
+ n = self2base.count(self.sep) + 1
+ else:
+ n = 0
+ l = [os.pardir] * n
+ if reldest:
+ l.append(reldest)
+ target = dest.sep.join(l)
+ return target
+ except AttributeError:
+ return str(dest)
+
+ def exists(self):
+ return self.check()
+
+ def isdir(self):
+ return self.check(dir=1)
+
+ def isfile(self):
+ return self.check(file=1)
+
+ def parts(self, reverse=False):
+ """ return a root-first list of all ancestor directories
+ plus the path itself.
+ """
+ current = self
+ l = [self]
+ while 1:
+ last = current
+ current = current.dirpath()
+ if last == current:
+ break
+ l.append(current)
+ if not reverse:
+ l.reverse()
+ return l
+
+ def common(self, other):
+ """ return the common part shared with the other path
+ or None if there is no common part.
+ """
+ last = None
+ for x, y in zip(self.parts(), other.parts()):
+ if x != y:
+ return last
+ last = x
+ return last
+
+ def __add__(self, other):
+ """ return new path object with 'other' added to the basename"""
+ return self.new(basename=self.basename+str(other))
+
+ def __cmp__(self, other):
+ """ return sort value (-1, 0, +1). """
+ try:
+ return cmp(self.strpath, other.strpath)
+ except AttributeError:
+ return cmp(str(self), str(other)) # self.path, other.path)
+
+ def __lt__(self, other):
+ try:
+ return self.strpath < other.strpath
+ except AttributeError:
+ return str(self) < str(other)
+
+ def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False):
+ """ yields all paths below the current one
+
+ fil is a filter (glob pattern or callable), if not matching the
+ path will not be yielded, defaulting to None (everything is
+ returned)
+
+ rec is a filter (glob pattern or callable) that controls whether
+ a node is descended, defaulting to None
+
+ ignore is an Exception class that is ignoredwhen calling dirlist()
+ on any of the paths (by default, all exceptions are reported)
+
+ bf if True will cause a breadthfirst search instead of the
+ default depthfirst. Default: False
+
+ sort if True will sort entries within each directory level.
+ """
+ for x in Visitor(fil, rec, ignore, bf, sort).gen(self):
+ yield x
+
+ def _sortlist(self, res, sort):
+ if sort:
+ if hasattr(sort, '__call__'):
+ warnings.warn(DeprecationWarning(
+ "listdir(sort=callable) is deprecated and breaks on python3"
+ ), stacklevel=3)
+ res.sort(sort)
+ else:
+ res.sort()
+
+ def samefile(self, other):
+ """ return True if other refers to the same stat object as self. """
+ return self.strpath == str(other)
+
+ def __fspath__(self):
+ return self.strpath
+
+class Visitor:
+ def __init__(self, fil, rec, ignore, bf, sort):
+ if isinstance(fil, py.builtin._basestring):
+ fil = FNMatcher(fil)
+ if isinstance(rec, py.builtin._basestring):
+ self.rec = FNMatcher(rec)
+ elif not hasattr(rec, '__call__') and rec:
+ self.rec = lambda path: True
+ else:
+ self.rec = rec
+ self.fil = fil
+ self.ignore = ignore
+ self.breadthfirst = bf
+ self.optsort = sort and sorted or (lambda x: x)
+
+ def gen(self, path):
+ try:
+ entries = path.listdir()
+ except self.ignore:
+ return
+ rec = self.rec
+ dirs = self.optsort([p for p in entries
+ if p.check(dir=1) and (rec is None or rec(p))])
+ if not self.breadthfirst:
+ for subdir in dirs:
+ for p in self.gen(subdir):
+ yield p
+ for p in self.optsort(entries):
+ if self.fil is None or self.fil(p):
+ yield p
+ if self.breadthfirst:
+ for subdir in dirs:
+ for p in self.gen(subdir):
+ yield p
+
+class FNMatcher:
+ def __init__(self, pattern):
+ self.pattern = pattern
+
+ def __call__(self, path):
+ pattern = self.pattern
+
+ if (pattern.find(path.sep) == -1 and
+ iswin32 and
+ pattern.find(posixpath.sep) != -1):
+ # Running on Windows, the pattern has no Windows path separators,
+ # and the pattern has one or more Posix path separators. Replace
+ # the Posix path separators with the Windows path separator.
+ pattern = pattern.replace(posixpath.sep, path.sep)
+
+ if pattern.find(path.sep) == -1:
+ name = path.basename
+ else:
+ name = str(path) # path.strpath # XXX svn?
+ if not os.path.isabs(pattern):
+ pattern = '*' + path.sep + pattern
+ return fnmatch.fnmatch(name, pattern)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/local.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/local.py
new file mode 100644
index 00000000000..c550fa2f1ab
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/local.py
@@ -0,0 +1,992 @@
+"""
+local path implementation.
+"""
+from __future__ import with_statement
+
+from contextlib import contextmanager
+import sys, os, re, atexit, io, uuid
+import py
+from py._path import common
+from py._path.common import iswin32, fspath
+from stat import S_ISLNK, S_ISDIR, S_ISREG
+
+from os.path import abspath, normcase, normpath, isabs, exists, isdir, isfile, islink, dirname
+
+if sys.version_info > (3,0):
+ def map_as_list(func, iter):
+ return list(map(func, iter))
+else:
+ map_as_list = map
+
+class Stat(object):
+ def __getattr__(self, name):
+ return getattr(self._osstatresult, "st_" + name)
+
+ def __init__(self, path, osstatresult):
+ self.path = path
+ self._osstatresult = osstatresult
+
+ @property
+ def owner(self):
+ if iswin32:
+ raise NotImplementedError("XXX win32")
+ import pwd
+ entry = py.error.checked_call(pwd.getpwuid, self.uid)
+ return entry[0]
+
+ @property
+ def group(self):
+ """ return group name of file. """
+ if iswin32:
+ raise NotImplementedError("XXX win32")
+ import grp
+ entry = py.error.checked_call(grp.getgrgid, self.gid)
+ return entry[0]
+
+ def isdir(self):
+ return S_ISDIR(self._osstatresult.st_mode)
+
+ def isfile(self):
+ return S_ISREG(self._osstatresult.st_mode)
+
+ def islink(self):
+ st = self.path.lstat()
+ return S_ISLNK(self._osstatresult.st_mode)
+
+class PosixPath(common.PathBase):
+ def chown(self, user, group, rec=0):
+ """ change ownership to the given user and group.
+ user and group may be specified by a number or
+ by a name. if rec is True change ownership
+ recursively.
+ """
+ uid = getuserid(user)
+ gid = getgroupid(group)
+ if rec:
+ for x in self.visit(rec=lambda x: x.check(link=0)):
+ if x.check(link=0):
+ py.error.checked_call(os.chown, str(x), uid, gid)
+ py.error.checked_call(os.chown, str(self), uid, gid)
+
+ def readlink(self):
+ """ return value of a symbolic link. """
+ return py.error.checked_call(os.readlink, self.strpath)
+
+ def mklinkto(self, oldname):
+ """ posix style hard link to another name. """
+ py.error.checked_call(os.link, str(oldname), str(self))
+
+ def mksymlinkto(self, value, absolute=1):
+ """ create a symbolic link with the given value (pointing to another name). """
+ if absolute:
+ py.error.checked_call(os.symlink, str(value), self.strpath)
+ else:
+ base = self.common(value)
+ # with posix local paths '/' is always a common base
+ relsource = self.__class__(value).relto(base)
+ reldest = self.relto(base)
+ n = reldest.count(self.sep)
+ target = self.sep.join(('..', )*n + (relsource, ))
+ py.error.checked_call(os.symlink, target, self.strpath)
+
+def getuserid(user):
+ import pwd
+ if not isinstance(user, int):
+ user = pwd.getpwnam(user)[2]
+ return user
+
+def getgroupid(group):
+ import grp
+ if not isinstance(group, int):
+ group = grp.getgrnam(group)[2]
+ return group
+
+FSBase = not iswin32 and PosixPath or common.PathBase
+
+class LocalPath(FSBase):
+ """ object oriented interface to os.path and other local filesystem
+ related information.
+ """
+ class ImportMismatchError(ImportError):
+ """ raised on pyimport() if there is a mismatch of __file__'s"""
+
+ sep = os.sep
+ class Checkers(common.Checkers):
+ def _stat(self):
+ try:
+ return self._statcache
+ except AttributeError:
+ try:
+ self._statcache = self.path.stat()
+ except py.error.ELOOP:
+ self._statcache = self.path.lstat()
+ return self._statcache
+
+ def dir(self):
+ return S_ISDIR(self._stat().mode)
+
+ def file(self):
+ return S_ISREG(self._stat().mode)
+
+ def exists(self):
+ return self._stat()
+
+ def link(self):
+ st = self.path.lstat()
+ return S_ISLNK(st.mode)
+
+ def __init__(self, path=None, expanduser=False):
+ """ Initialize and return a local Path instance.
+
+ Path can be relative to the current directory.
+ If path is None it defaults to the current working directory.
+ If expanduser is True, tilde-expansion is performed.
+ Note that Path instances always carry an absolute path.
+ Note also that passing in a local path object will simply return
+ the exact same path object. Use new() to get a new copy.
+ """
+ if path is None:
+ self.strpath = py.error.checked_call(os.getcwd)
+ else:
+ try:
+ path = fspath(path)
+ except TypeError:
+ raise ValueError("can only pass None, Path instances "
+ "or non-empty strings to LocalPath")
+ if expanduser:
+ path = os.path.expanduser(path)
+ self.strpath = abspath(path)
+
+ def __hash__(self):
+ return hash(self.strpath)
+
+ def __eq__(self, other):
+ s1 = fspath(self)
+ try:
+ s2 = fspath(other)
+ except TypeError:
+ return False
+ if iswin32:
+ s1 = s1.lower()
+ try:
+ s2 = s2.lower()
+ except AttributeError:
+ return False
+ return s1 == s2
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __lt__(self, other):
+ return fspath(self) < fspath(other)
+
+ def __gt__(self, other):
+ return fspath(self) > fspath(other)
+
+ def samefile(self, other):
+ """ return True if 'other' references the same file as 'self'.
+ """
+ other = fspath(other)
+ if not isabs(other):
+ other = abspath(other)
+ if self == other:
+ return True
+ if iswin32:
+ return False # there is no samefile
+ return py.error.checked_call(
+ os.path.samefile, self.strpath, other)
+
+ def remove(self, rec=1, ignore_errors=False):
+ """ remove a file or directory (or a directory tree if rec=1).
+ if ignore_errors is True, errors while removing directories will
+ be ignored.
+ """
+ if self.check(dir=1, link=0):
+ if rec:
+ # force remove of readonly files on windows
+ if iswin32:
+ self.chmod(0o700, rec=1)
+ import shutil
+ py.error.checked_call(
+ shutil.rmtree, self.strpath,
+ ignore_errors=ignore_errors)
+ else:
+ py.error.checked_call(os.rmdir, self.strpath)
+ else:
+ if iswin32:
+ self.chmod(0o700)
+ py.error.checked_call(os.remove, self.strpath)
+
+ def computehash(self, hashtype="md5", chunksize=524288):
+ """ return hexdigest of hashvalue for this file. """
+ try:
+ try:
+ import hashlib as mod
+ except ImportError:
+ if hashtype == "sha1":
+ hashtype = "sha"
+ mod = __import__(hashtype)
+ hash = getattr(mod, hashtype)()
+ except (AttributeError, ImportError):
+ raise ValueError("Don't know how to compute %r hash" %(hashtype,))
+ f = self.open('rb')
+ try:
+ while 1:
+ buf = f.read(chunksize)
+ if not buf:
+ return hash.hexdigest()
+ hash.update(buf)
+ finally:
+ f.close()
+
+ def new(self, **kw):
+ """ create a modified version of this path.
+ the following keyword arguments modify various path parts::
+
+ a:/some/path/to/a/file.ext
+ xx drive
+ xxxxxxxxxxxxxxxxx dirname
+ xxxxxxxx basename
+ xxxx purebasename
+ xxx ext
+ """
+ obj = object.__new__(self.__class__)
+ if not kw:
+ obj.strpath = self.strpath
+ return obj
+ drive, dirname, basename, purebasename,ext = self._getbyspec(
+ "drive,dirname,basename,purebasename,ext")
+ if 'basename' in kw:
+ if 'purebasename' in kw or 'ext' in kw:
+ raise ValueError("invalid specification %r" % kw)
+ else:
+ pb = kw.setdefault('purebasename', purebasename)
+ try:
+ ext = kw['ext']
+ except KeyError:
+ pass
+ else:
+ if ext and not ext.startswith('.'):
+ ext = '.' + ext
+ kw['basename'] = pb + ext
+
+ if ('dirname' in kw and not kw['dirname']):
+ kw['dirname'] = drive
+ else:
+ kw.setdefault('dirname', dirname)
+ kw.setdefault('sep', self.sep)
+ obj.strpath = normpath(
+ "%(dirname)s%(sep)s%(basename)s" % kw)
+ return obj
+
+ def _getbyspec(self, spec):
+ """ see new for what 'spec' can be. """
+ res = []
+ parts = self.strpath.split(self.sep)
+
+ args = filter(None, spec.split(',') )
+ append = res.append
+ for name in args:
+ if name == 'drive':
+ append(parts[0])
+ elif name == 'dirname':
+ append(self.sep.join(parts[:-1]))
+ else:
+ basename = parts[-1]
+ if name == 'basename':
+ append(basename)
+ else:
+ i = basename.rfind('.')
+ if i == -1:
+ purebasename, ext = basename, ''
+ else:
+ purebasename, ext = basename[:i], basename[i:]
+ if name == 'purebasename':
+ append(purebasename)
+ elif name == 'ext':
+ append(ext)
+ else:
+ raise ValueError("invalid part specification %r" % name)
+ return res
+
+ def dirpath(self, *args, **kwargs):
+ """ return the directory path joined with any given path arguments. """
+ if not kwargs:
+ path = object.__new__(self.__class__)
+ path.strpath = dirname(self.strpath)
+ if args:
+ path = path.join(*args)
+ return path
+ return super(LocalPath, self).dirpath(*args, **kwargs)
+
+ def join(self, *args, **kwargs):
+ """ return a new path by appending all 'args' as path
+ components. if abs=1 is used restart from root if any
+ of the args is an absolute path.
+ """
+ sep = self.sep
+ strargs = [fspath(arg) for arg in args]
+ strpath = self.strpath
+ if kwargs.get('abs'):
+ newargs = []
+ for arg in reversed(strargs):
+ if isabs(arg):
+ strpath = arg
+ strargs = newargs
+ break
+ newargs.insert(0, arg)
+ # special case for when we have e.g. strpath == "/"
+ actual_sep = "" if strpath.endswith(sep) else sep
+ for arg in strargs:
+ arg = arg.strip(sep)
+ if iswin32:
+ # allow unix style paths even on windows.
+ arg = arg.strip('/')
+ arg = arg.replace('/', sep)
+ strpath = strpath + actual_sep + arg
+ actual_sep = sep
+ obj = object.__new__(self.__class__)
+ obj.strpath = normpath(strpath)
+ return obj
+
+ def open(self, mode='r', ensure=False, encoding=None):
+ """ return an opened file with the given mode.
+
+ If ensure is True, create parent directories if needed.
+ """
+ if ensure:
+ self.dirpath().ensure(dir=1)
+ if encoding:
+ return py.error.checked_call(io.open, self.strpath, mode, encoding=encoding)
+ return py.error.checked_call(open, self.strpath, mode)
+
+ def _fastjoin(self, name):
+ child = object.__new__(self.__class__)
+ child.strpath = self.strpath + self.sep + name
+ return child
+
+ def islink(self):
+ return islink(self.strpath)
+
+ def check(self, **kw):
+ if not kw:
+ return exists(self.strpath)
+ if len(kw) == 1:
+ if "dir" in kw:
+ return not kw["dir"] ^ isdir(self.strpath)
+ if "file" in kw:
+ return not kw["file"] ^ isfile(self.strpath)
+ return super(LocalPath, self).check(**kw)
+
+ _patternchars = set("*?[" + os.path.sep)
+ def listdir(self, fil=None, sort=None):
+ """ list directory contents, possibly filter by the given fil func
+ and possibly sorted.
+ """
+ if fil is None and sort is None:
+ names = py.error.checked_call(os.listdir, self.strpath)
+ return map_as_list(self._fastjoin, names)
+ if isinstance(fil, py.builtin._basestring):
+ if not self._patternchars.intersection(fil):
+ child = self._fastjoin(fil)
+ if exists(child.strpath):
+ return [child]
+ return []
+ fil = common.FNMatcher(fil)
+ names = py.error.checked_call(os.listdir, self.strpath)
+ res = []
+ for name in names:
+ child = self._fastjoin(name)
+ if fil is None or fil(child):
+ res.append(child)
+ self._sortlist(res, sort)
+ return res
+
+ def size(self):
+ """ return size of the underlying file object """
+ return self.stat().size
+
+ def mtime(self):
+ """ return last modification time of the path. """
+ return self.stat().mtime
+
+ def copy(self, target, mode=False, stat=False):
+ """ copy path to target.
+
+ If mode is True, will copy copy permission from path to target.
+ If stat is True, copy permission, last modification
+ time, last access time, and flags from path to target.
+ """
+ if self.check(file=1):
+ if target.check(dir=1):
+ target = target.join(self.basename)
+ assert self!=target
+ copychunked(self, target)
+ if mode:
+ copymode(self.strpath, target.strpath)
+ if stat:
+ copystat(self, target)
+ else:
+ def rec(p):
+ return p.check(link=0)
+ for x in self.visit(rec=rec):
+ relpath = x.relto(self)
+ newx = target.join(relpath)
+ newx.dirpath().ensure(dir=1)
+ if x.check(link=1):
+ newx.mksymlinkto(x.readlink())
+ continue
+ elif x.check(file=1):
+ copychunked(x, newx)
+ elif x.check(dir=1):
+ newx.ensure(dir=1)
+ if mode:
+ copymode(x.strpath, newx.strpath)
+ if stat:
+ copystat(x, newx)
+
+ def rename(self, target):
+ """ rename this path to target. """
+ target = fspath(target)
+ return py.error.checked_call(os.rename, self.strpath, target)
+
+ def dump(self, obj, bin=1):
+ """ pickle object into path location"""
+ f = self.open('wb')
+ import pickle
+ try:
+ py.error.checked_call(pickle.dump, obj, f, bin)
+ finally:
+ f.close()
+
+ def mkdir(self, *args):
+ """ create & return the directory joined with args. """
+ p = self.join(*args)
+ py.error.checked_call(os.mkdir, fspath(p))
+ return p
+
+ def write_binary(self, data, ensure=False):
+ """ write binary data into path. If ensure is True create
+ missing parent directories.
+ """
+ if ensure:
+ self.dirpath().ensure(dir=1)
+ with self.open('wb') as f:
+ f.write(data)
+
+ def write_text(self, data, encoding, ensure=False):
+ """ write text data into path using the specified encoding.
+ If ensure is True create missing parent directories.
+ """
+ if ensure:
+ self.dirpath().ensure(dir=1)
+ with self.open('w', encoding=encoding) as f:
+ f.write(data)
+
+ def write(self, data, mode='w', ensure=False):
+ """ write data into path. If ensure is True create
+ missing parent directories.
+ """
+ if ensure:
+ self.dirpath().ensure(dir=1)
+ if 'b' in mode:
+ if not py.builtin._isbytes(data):
+ raise ValueError("can only process bytes")
+ else:
+ if not py.builtin._istext(data):
+ if not py.builtin._isbytes(data):
+ data = str(data)
+ else:
+ data = py.builtin._totext(data, sys.getdefaultencoding())
+ f = self.open(mode)
+ try:
+ f.write(data)
+ finally:
+ f.close()
+
+ def _ensuredirs(self):
+ parent = self.dirpath()
+ if parent == self:
+ return self
+ if parent.check(dir=0):
+ parent._ensuredirs()
+ if self.check(dir=0):
+ try:
+ self.mkdir()
+ except py.error.EEXIST:
+ # race condition: file/dir created by another thread/process.
+ # complain if it is not a dir
+ if self.check(dir=0):
+ raise
+ return self
+
+ def ensure(self, *args, **kwargs):
+ """ ensure that an args-joined path exists (by default as
+ a file). if you specify a keyword argument 'dir=True'
+ then the path is forced to be a directory path.
+ """
+ p = self.join(*args)
+ if kwargs.get('dir', 0):
+ return p._ensuredirs()
+ else:
+ p.dirpath()._ensuredirs()
+ if not p.check(file=1):
+ p.open('w').close()
+ return p
+
+ def stat(self, raising=True):
+ """ Return an os.stat() tuple. """
+ if raising == True:
+ return Stat(self, py.error.checked_call(os.stat, self.strpath))
+ try:
+ return Stat(self, os.stat(self.strpath))
+ except KeyboardInterrupt:
+ raise
+ except Exception:
+ return None
+
+ def lstat(self):
+ """ Return an os.lstat() tuple. """
+ return Stat(self, py.error.checked_call(os.lstat, self.strpath))
+
+ def setmtime(self, mtime=None):
+ """ set modification time for the given path. if 'mtime' is None
+ (the default) then the file's mtime is set to current time.
+
+ Note that the resolution for 'mtime' is platform dependent.
+ """
+ if mtime is None:
+ return py.error.checked_call(os.utime, self.strpath, mtime)
+ try:
+ return py.error.checked_call(os.utime, self.strpath, (-1, mtime))
+ except py.error.EINVAL:
+ return py.error.checked_call(os.utime, self.strpath, (self.atime(), mtime))
+
+ def chdir(self):
+ """ change directory to self and return old current directory """
+ try:
+ old = self.__class__()
+ except py.error.ENOENT:
+ old = None
+ py.error.checked_call(os.chdir, self.strpath)
+ return old
+
+
+ @contextmanager
+ def as_cwd(self):
+ """ return context manager which changes to current dir during the
+ managed "with" context. On __enter__ it returns the old dir.
+ """
+ old = self.chdir()
+ try:
+ yield old
+ finally:
+ old.chdir()
+
+ def realpath(self):
+ """ return a new path which contains no symbolic links."""
+ return self.__class__(os.path.realpath(self.strpath))
+
+ def atime(self):
+ """ return last access time of the path. """
+ return self.stat().atime
+
+ def __repr__(self):
+ return 'local(%r)' % self.strpath
+
+ def __str__(self):
+ """ return string representation of the Path. """
+ return self.strpath
+
+ def chmod(self, mode, rec=0):
+ """ change permissions to the given mode. If mode is an
+ integer it directly encodes the os-specific modes.
+ if rec is True perform recursively.
+ """
+ if not isinstance(mode, int):
+ raise TypeError("mode %r must be an integer" % (mode,))
+ if rec:
+ for x in self.visit(rec=rec):
+ py.error.checked_call(os.chmod, str(x), mode)
+ py.error.checked_call(os.chmod, self.strpath, mode)
+
+ def pypkgpath(self):
+ """ return the Python package path by looking for the last
+ directory upwards which still contains an __init__.py.
+ Return None if a pkgpath can not be determined.
+ """
+ pkgpath = None
+ for parent in self.parts(reverse=True):
+ if parent.isdir():
+ if not parent.join('__init__.py').exists():
+ break
+ if not isimportable(parent.basename):
+ break
+ pkgpath = parent
+ return pkgpath
+
+ def _ensuresyspath(self, ensuremode, path):
+ if ensuremode:
+ s = str(path)
+ if ensuremode == "append":
+ if s not in sys.path:
+ sys.path.append(s)
+ else:
+ if s != sys.path[0]:
+ sys.path.insert(0, s)
+
+ def pyimport(self, modname=None, ensuresyspath=True):
+ """ return path as an imported python module.
+
+ If modname is None, look for the containing package
+ and construct an according module name.
+ The module will be put/looked up in sys.modules.
+ if ensuresyspath is True then the root dir for importing
+ the file (taking __init__.py files into account) will
+ be prepended to sys.path if it isn't there already.
+ If ensuresyspath=="append" the root dir will be appended
+ if it isn't already contained in sys.path.
+ if ensuresyspath is False no modification of syspath happens.
+ """
+ if not self.check():
+ raise py.error.ENOENT(self)
+
+ pkgpath = None
+ if modname is None:
+ pkgpath = self.pypkgpath()
+ if pkgpath is not None:
+ pkgroot = pkgpath.dirpath()
+ names = self.new(ext="").relto(pkgroot).split(self.sep)
+ if names[-1] == "__init__":
+ names.pop()
+ modname = ".".join(names)
+ else:
+ pkgroot = self.dirpath()
+ modname = self.purebasename
+
+ self._ensuresyspath(ensuresyspath, pkgroot)
+ __import__(modname)
+ mod = sys.modules[modname]
+ if self.basename == "__init__.py":
+ return mod # we don't check anything as we might
+ # we in a namespace package ... too icky to check
+ modfile = mod.__file__
+ if modfile[-4:] in ('.pyc', '.pyo'):
+ modfile = modfile[:-1]
+ elif modfile.endswith('$py.class'):
+ modfile = modfile[:-9] + '.py'
+ if modfile.endswith(os.path.sep + "__init__.py"):
+ if self.basename != "__init__.py":
+ modfile = modfile[:-12]
+ try:
+ issame = self.samefile(modfile)
+ except py.error.ENOENT:
+ issame = False
+ if not issame:
+ raise self.ImportMismatchError(modname, modfile, self)
+ return mod
+ else:
+ try:
+ return sys.modules[modname]
+ except KeyError:
+ # we have a custom modname, do a pseudo-import
+ import types
+ mod = types.ModuleType(modname)
+ mod.__file__ = str(self)
+ sys.modules[modname] = mod
+ try:
+ py.builtin.execfile(str(self), mod.__dict__)
+ except:
+ del sys.modules[modname]
+ raise
+ return mod
+
+ def sysexec(self, *argv, **popen_opts):
+ """ return stdout text from executing a system child process,
+ where the 'self' path points to executable.
+ The process is directly invoked and not through a system shell.
+ """
+ from subprocess import Popen, PIPE
+ argv = map_as_list(str, argv)
+ popen_opts['stdout'] = popen_opts['stderr'] = PIPE
+ proc = Popen([str(self)] + argv, **popen_opts)
+ stdout, stderr = proc.communicate()
+ ret = proc.wait()
+ if py.builtin._isbytes(stdout):
+ stdout = py.builtin._totext(stdout, sys.getdefaultencoding())
+ if ret != 0:
+ if py.builtin._isbytes(stderr):
+ stderr = py.builtin._totext(stderr, sys.getdefaultencoding())
+ raise py.process.cmdexec.Error(ret, ret, str(self),
+ stdout, stderr,)
+ return stdout
+
+ def sysfind(cls, name, checker=None, paths=None):
+ """ return a path object found by looking at the systems
+ underlying PATH specification. If the checker is not None
+ it will be invoked to filter matching paths. If a binary
+ cannot be found, None is returned
+ Note: This is probably not working on plain win32 systems
+ but may work on cygwin.
+ """
+ if isabs(name):
+ p = py.path.local(name)
+ if p.check(file=1):
+ return p
+ else:
+ if paths is None:
+ if iswin32:
+ paths = os.environ['Path'].split(';')
+ if '' not in paths and '.' not in paths:
+ paths.append('.')
+ try:
+ systemroot = os.environ['SYSTEMROOT']
+ except KeyError:
+ pass
+ else:
+ paths = [path.replace('%SystemRoot%', systemroot)
+ for path in paths]
+ else:
+ paths = os.environ['PATH'].split(':')
+ tryadd = []
+ if iswin32:
+ tryadd += os.environ['PATHEXT'].split(os.pathsep)
+ tryadd.append("")
+
+ for x in paths:
+ for addext in tryadd:
+ p = py.path.local(x).join(name, abs=True) + addext
+ try:
+ if p.check(file=1):
+ if checker:
+ if not checker(p):
+ continue
+ return p
+ except py.error.EACCES:
+ pass
+ return None
+ sysfind = classmethod(sysfind)
+
+ def _gethomedir(cls):
+ try:
+ x = os.environ['HOME']
+ except KeyError:
+ try:
+ x = os.environ["HOMEDRIVE"] + os.environ['HOMEPATH']
+ except KeyError:
+ return None
+ return cls(x)
+ _gethomedir = classmethod(_gethomedir)
+
+ # """
+ # special class constructors for local filesystem paths
+ # """
+ @classmethod
+ def get_temproot(cls):
+ """ return the system's temporary directory
+ (where tempfiles are usually created in)
+ """
+ import tempfile
+ return py.path.local(tempfile.gettempdir())
+
+ @classmethod
+ def mkdtemp(cls, rootdir=None):
+ """ return a Path object pointing to a fresh new temporary directory
+ (which we created ourself).
+ """
+ import tempfile
+ if rootdir is None:
+ rootdir = cls.get_temproot()
+ return cls(py.error.checked_call(tempfile.mkdtemp, dir=str(rootdir)))
+
+ def make_numbered_dir(cls, prefix='session-', rootdir=None, keep=3,
+ lock_timeout = 172800): # two days
+ """ return unique directory with a number greater than the current
+ maximum one. The number is assumed to start directly after prefix.
+ if keep is true directories with a number less than (maxnum-keep)
+ will be removed. If .lock files are used (lock_timeout non-zero),
+ algorithm is multi-process safe.
+ """
+ if rootdir is None:
+ rootdir = cls.get_temproot()
+
+ nprefix = normcase(prefix)
+ def parse_num(path):
+ """ parse the number out of a path (if it matches the prefix) """
+ nbasename = normcase(path.basename)
+ if nbasename.startswith(nprefix):
+ try:
+ return int(nbasename[len(nprefix):])
+ except ValueError:
+ pass
+
+ def create_lockfile(path):
+ """ exclusively create lockfile. Throws when failed """
+ mypid = os.getpid()
+ lockfile = path.join('.lock')
+ if hasattr(lockfile, 'mksymlinkto'):
+ lockfile.mksymlinkto(str(mypid))
+ else:
+ fd = py.error.checked_call(os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)
+ with os.fdopen(fd, 'w') as f:
+ f.write(str(mypid))
+ return lockfile
+
+ def atexit_remove_lockfile(lockfile):
+ """ ensure lockfile is removed at process exit """
+ mypid = os.getpid()
+ def try_remove_lockfile():
+ # in a fork() situation, only the last process should
+ # remove the .lock, otherwise the other processes run the
+ # risk of seeing their temporary dir disappear. For now
+ # we remove the .lock in the parent only (i.e. we assume
+ # that the children finish before the parent).
+ if os.getpid() != mypid:
+ return
+ try:
+ lockfile.remove()
+ except py.error.Error:
+ pass
+ atexit.register(try_remove_lockfile)
+
+ # compute the maximum number currently in use with the prefix
+ lastmax = None
+ while True:
+ maxnum = -1
+ for path in rootdir.listdir():
+ num = parse_num(path)
+ if num is not None:
+ maxnum = max(maxnum, num)
+
+ # make the new directory
+ try:
+ udir = rootdir.mkdir(prefix + str(maxnum+1))
+ if lock_timeout:
+ lockfile = create_lockfile(udir)
+ atexit_remove_lockfile(lockfile)
+ except (py.error.EEXIST, py.error.ENOENT, py.error.EBUSY):
+ # race condition (1): another thread/process created the dir
+ # in the meantime - try again
+ # race condition (2): another thread/process spuriously acquired
+ # lock treating empty directory as candidate
+ # for removal - try again
+ # race condition (3): another thread/process tried to create the lock at
+ # the same time (happened in Python 3.3 on Windows)
+ # https://ci.appveyor.com/project/pytestbot/py/build/1.0.21/job/ffi85j4c0lqwsfwa
+ if lastmax == maxnum:
+ raise
+ lastmax = maxnum
+ continue
+ break
+
+ def get_mtime(path):
+ """ read file modification time """
+ try:
+ return path.lstat().mtime
+ except py.error.Error:
+ pass
+
+ garbage_prefix = prefix + 'garbage-'
+
+ def is_garbage(path):
+ """ check if path denotes directory scheduled for removal """
+ bn = path.basename
+ return bn.startswith(garbage_prefix)
+
+ # prune old directories
+ udir_time = get_mtime(udir)
+ if keep and udir_time:
+ for path in rootdir.listdir():
+ num = parse_num(path)
+ if num is not None and num <= (maxnum - keep):
+ try:
+ # try acquiring lock to remove directory as exclusive user
+ if lock_timeout:
+ create_lockfile(path)
+ except (py.error.EEXIST, py.error.ENOENT, py.error.EBUSY):
+ path_time = get_mtime(path)
+ if not path_time:
+ # assume directory doesn't exist now
+ continue
+ if abs(udir_time - path_time) < lock_timeout:
+ # assume directory with lockfile exists
+ # and lock timeout hasn't expired yet
+ continue
+
+ # path dir locked for exclusive use
+ # and scheduled for removal to avoid another thread/process
+ # treating it as a new directory or removal candidate
+ garbage_path = rootdir.join(garbage_prefix + str(uuid.uuid4()))
+ try:
+ path.rename(garbage_path)
+ garbage_path.remove(rec=1)
+ except KeyboardInterrupt:
+ raise
+ except: # this might be py.error.Error, WindowsError ...
+ pass
+ if is_garbage(path):
+ try:
+ path.remove(rec=1)
+ except KeyboardInterrupt:
+ raise
+ except: # this might be py.error.Error, WindowsError ...
+ pass
+
+ # make link...
+ try:
+ username = os.environ['USER'] #linux, et al
+ except KeyError:
+ try:
+ username = os.environ['USERNAME'] #windows
+ except KeyError:
+ username = 'current'
+
+ src = str(udir)
+ dest = src[:src.rfind('-')] + '-' + username
+ try:
+ os.unlink(dest)
+ except OSError:
+ pass
+ try:
+ os.symlink(src, dest)
+ except (OSError, AttributeError, NotImplementedError):
+ pass
+
+ return udir
+ make_numbered_dir = classmethod(make_numbered_dir)
+
+
+def copymode(src, dest):
+ """ copy permission from src to dst. """
+ import shutil
+ shutil.copymode(src, dest)
+
+
+def copystat(src, dest):
+ """ copy permission, last modification time,
+ last access time, and flags from src to dst."""
+ import shutil
+ shutil.copystat(str(src), str(dest))
+
+
+def copychunked(src, dest):
+ chunksize = 524288 # half a meg of bytes
+ fsrc = src.open('rb')
+ try:
+ fdest = dest.open('wb')
+ try:
+ while 1:
+ buf = fsrc.read(chunksize)
+ if not buf:
+ break
+ fdest.write(buf)
+ finally:
+ fdest.close()
+ finally:
+ fsrc.close()
+
+
+def isimportable(name):
+ if name and (name[0].isalpha() or name[0] == '_'):
+ name = name.replace("_", '')
+ return not name or name.isalnum()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/svnurl.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/svnurl.py
new file mode 100644
index 00000000000..6589a71d09e
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/svnurl.py
@@ -0,0 +1,380 @@
+"""
+module defining a subversion path object based on the external
+command 'svn'. This modules aims to work with svn 1.3 and higher
+but might also interact well with earlier versions.
+"""
+
+import os, sys, time, re
+import py
+from py import path, process
+from py._path import common
+from py._path import svnwc as svncommon
+from py._path.cacheutil import BuildcostAccessCache, AgingCache
+
+DEBUG=False
+
+class SvnCommandPath(svncommon.SvnPathBase):
+ """ path implementation that offers access to (possibly remote) subversion
+ repositories. """
+
+ _lsrevcache = BuildcostAccessCache(maxentries=128)
+ _lsnorevcache = AgingCache(maxentries=1000, maxseconds=60.0)
+
+ def __new__(cls, path, rev=None, auth=None):
+ self = object.__new__(cls)
+ if isinstance(path, cls):
+ rev = path.rev
+ auth = path.auth
+ path = path.strpath
+ svncommon.checkbadchars(path)
+ path = path.rstrip('/')
+ self.strpath = path
+ self.rev = rev
+ self.auth = auth
+ return self
+
+ def __repr__(self):
+ if self.rev == -1:
+ return 'svnurl(%r)' % self.strpath
+ else:
+ return 'svnurl(%r, %r)' % (self.strpath, self.rev)
+
+ def _svnwithrev(self, cmd, *args):
+ """ execute an svn command, append our own url and revision """
+ if self.rev is None:
+ return self._svnwrite(cmd, *args)
+ else:
+ args = ['-r', self.rev] + list(args)
+ return self._svnwrite(cmd, *args)
+
+ def _svnwrite(self, cmd, *args):
+ """ execute an svn command, append our own url """
+ l = ['svn %s' % cmd]
+ args = ['"%s"' % self._escape(item) for item in args]
+ l.extend(args)
+ l.append('"%s"' % self._encodedurl())
+ # fixing the locale because we can't otherwise parse
+ string = " ".join(l)
+ if DEBUG:
+ print("execing %s" % string)
+ out = self._svncmdexecauth(string)
+ return out
+
+ def _svncmdexecauth(self, cmd):
+ """ execute an svn command 'as is' """
+ cmd = svncommon.fixlocale() + cmd
+ if self.auth is not None:
+ cmd += ' ' + self.auth.makecmdoptions()
+ return self._cmdexec(cmd)
+
+ def _cmdexec(self, cmd):
+ try:
+ out = process.cmdexec(cmd)
+ except py.process.cmdexec.Error:
+ e = sys.exc_info()[1]
+ if (e.err.find('File Exists') != -1 or
+ e.err.find('File already exists') != -1):
+ raise py.error.EEXIST(self)
+ raise
+ return out
+
+ def _svnpopenauth(self, cmd):
+ """ execute an svn command, return a pipe for reading stdin """
+ cmd = svncommon.fixlocale() + cmd
+ if self.auth is not None:
+ cmd += ' ' + self.auth.makecmdoptions()
+ return self._popen(cmd)
+
+ def _popen(self, cmd):
+ return os.popen(cmd)
+
+ def _encodedurl(self):
+ return self._escape(self.strpath)
+
+ def _norev_delentry(self, path):
+ auth = self.auth and self.auth.makecmdoptions() or None
+ self._lsnorevcache.delentry((str(path), auth))
+
+ def open(self, mode='r'):
+ """ return an opened file with the given mode. """
+ if mode not in ("r", "rU",):
+ raise ValueError("mode %r not supported" % (mode,))
+ assert self.check(file=1) # svn cat returns an empty file otherwise
+ if self.rev is None:
+ return self._svnpopenauth('svn cat "%s"' % (
+ self._escape(self.strpath), ))
+ else:
+ return self._svnpopenauth('svn cat -r %s "%s"' % (
+ self.rev, self._escape(self.strpath)))
+
+ def dirpath(self, *args, **kwargs):
+ """ return the directory path of the current path joined
+ with any given path arguments.
+ """
+ l = self.strpath.split(self.sep)
+ if len(l) < 4:
+ raise py.error.EINVAL(self, "base is not valid")
+ elif len(l) == 4:
+ return self.join(*args, **kwargs)
+ else:
+ return self.new(basename='').join(*args, **kwargs)
+
+ # modifying methods (cache must be invalidated)
+ def mkdir(self, *args, **kwargs):
+ """ create & return the directory joined with args.
+ pass a 'msg' keyword argument to set the commit message.
+ """
+ commit_msg = kwargs.get('msg', "mkdir by py lib invocation")
+ createpath = self.join(*args)
+ createpath._svnwrite('mkdir', '-m', commit_msg)
+ self._norev_delentry(createpath.dirpath())
+ return createpath
+
+ def copy(self, target, msg='copied by py lib invocation'):
+ """ copy path to target with checkin message msg."""
+ if getattr(target, 'rev', None) is not None:
+ raise py.error.EINVAL(target, "revisions are immutable")
+ self._svncmdexecauth('svn copy -m "%s" "%s" "%s"' %(msg,
+ self._escape(self), self._escape(target)))
+ self._norev_delentry(target.dirpath())
+
+ def rename(self, target, msg="renamed by py lib invocation"):
+ """ rename this path to target with checkin message msg. """
+ if getattr(self, 'rev', None) is not None:
+ raise py.error.EINVAL(self, "revisions are immutable")
+ self._svncmdexecauth('svn move -m "%s" --force "%s" "%s"' %(
+ msg, self._escape(self), self._escape(target)))
+ self._norev_delentry(self.dirpath())
+ self._norev_delentry(self)
+
+ def remove(self, rec=1, msg='removed by py lib invocation'):
+ """ remove a file or directory (or a directory tree if rec=1) with
+checkin message msg."""
+ if self.rev is not None:
+ raise py.error.EINVAL(self, "revisions are immutable")
+ self._svncmdexecauth('svn rm -m "%s" "%s"' %(msg, self._escape(self)))
+ self._norev_delentry(self.dirpath())
+
+ def export(self, topath):
+ """ export to a local path
+
+ topath should not exist prior to calling this, returns a
+ py.path.local instance
+ """
+ topath = py.path.local(topath)
+ args = ['"%s"' % (self._escape(self),),
+ '"%s"' % (self._escape(topath),)]
+ if self.rev is not None:
+ args = ['-r', str(self.rev)] + args
+ self._svncmdexecauth('svn export %s' % (' '.join(args),))
+ return topath
+
+ def ensure(self, *args, **kwargs):
+ """ ensure that an args-joined path exists (by default as
+ a file). If you specify a keyword argument 'dir=True'
+ then the path is forced to be a directory path.
+ """
+ if getattr(self, 'rev', None) is not None:
+ raise py.error.EINVAL(self, "revisions are immutable")
+ target = self.join(*args)
+ dir = kwargs.get('dir', 0)
+ for x in target.parts(reverse=True):
+ if x.check():
+ break
+ else:
+ raise py.error.ENOENT(target, "has not any valid base!")
+ if x == target:
+ if not x.check(dir=dir):
+ raise dir and py.error.ENOTDIR(x) or py.error.EISDIR(x)
+ return x
+ tocreate = target.relto(x)
+ basename = tocreate.split(self.sep, 1)[0]
+ tempdir = py.path.local.mkdtemp()
+ try:
+ tempdir.ensure(tocreate, dir=dir)
+ cmd = 'svn import -m "%s" "%s" "%s"' % (
+ "ensure %s" % self._escape(tocreate),
+ self._escape(tempdir.join(basename)),
+ x.join(basename)._encodedurl())
+ self._svncmdexecauth(cmd)
+ self._norev_delentry(x)
+ finally:
+ tempdir.remove()
+ return target
+
+ # end of modifying methods
+ def _propget(self, name):
+ res = self._svnwithrev('propget', name)
+ return res[:-1] # strip trailing newline
+
+ def _proplist(self):
+ res = self._svnwithrev('proplist')
+ lines = res.split('\n')
+ lines = [x.strip() for x in lines[1:]]
+ return svncommon.PropListDict(self, lines)
+
+ def info(self):
+ """ return an Info structure with svn-provided information. """
+ parent = self.dirpath()
+ nameinfo_seq = parent._listdir_nameinfo()
+ bn = self.basename
+ for name, info in nameinfo_seq:
+ if name == bn:
+ return info
+ raise py.error.ENOENT(self)
+
+
+ def _listdir_nameinfo(self):
+ """ return sequence of name-info directory entries of self """
+ def builder():
+ try:
+ res = self._svnwithrev('ls', '-v')
+ except process.cmdexec.Error:
+ e = sys.exc_info()[1]
+ if e.err.find('non-existent in that revision') != -1:
+ raise py.error.ENOENT(self, e.err)
+ elif e.err.find("E200009:") != -1:
+ raise py.error.ENOENT(self, e.err)
+ elif e.err.find('File not found') != -1:
+ raise py.error.ENOENT(self, e.err)
+ elif e.err.find('not part of a repository')!=-1:
+ raise py.error.ENOENT(self, e.err)
+ elif e.err.find('Unable to open')!=-1:
+ raise py.error.ENOENT(self, e.err)
+ elif e.err.lower().find('method not allowed')!=-1:
+ raise py.error.EACCES(self, e.err)
+ raise py.error.Error(e.err)
+ lines = res.split('\n')
+ nameinfo_seq = []
+ for lsline in lines:
+ if lsline:
+ info = InfoSvnCommand(lsline)
+ if info._name != '.': # svn 1.5 produces '.' dirs,
+ nameinfo_seq.append((info._name, info))
+ nameinfo_seq.sort()
+ return nameinfo_seq
+ auth = self.auth and self.auth.makecmdoptions() or None
+ if self.rev is not None:
+ return self._lsrevcache.getorbuild((self.strpath, self.rev, auth),
+ builder)
+ else:
+ return self._lsnorevcache.getorbuild((self.strpath, auth),
+ builder)
+
+ def listdir(self, fil=None, sort=None):
+ """ list directory contents, possibly filter by the given fil func
+ and possibly sorted.
+ """
+ if isinstance(fil, str):
+ fil = common.FNMatcher(fil)
+ nameinfo_seq = self._listdir_nameinfo()
+ if len(nameinfo_seq) == 1:
+ name, info = nameinfo_seq[0]
+ if name == self.basename and info.kind == 'file':
+ #if not self.check(dir=1):
+ raise py.error.ENOTDIR(self)
+ paths = [self.join(name) for (name, info) in nameinfo_seq]
+ if fil:
+ paths = [x for x in paths if fil(x)]
+ self._sortlist(paths, sort)
+ return paths
+
+
+ def log(self, rev_start=None, rev_end=1, verbose=False):
+ """ return a list of LogEntry instances for this path.
+rev_start is the starting revision (defaulting to the first one).
+rev_end is the last revision (defaulting to HEAD).
+if verbose is True, then the LogEntry instances also know which files changed.
+"""
+ assert self.check() #make it simpler for the pipe
+ rev_start = rev_start is None and "HEAD" or rev_start
+ rev_end = rev_end is None and "HEAD" or rev_end
+
+ if rev_start == "HEAD" and rev_end == 1:
+ rev_opt = ""
+ else:
+ rev_opt = "-r %s:%s" % (rev_start, rev_end)
+ verbose_opt = verbose and "-v" or ""
+ xmlpipe = self._svnpopenauth('svn log --xml %s %s "%s"' %
+ (rev_opt, verbose_opt, self.strpath))
+ from xml.dom import minidom
+ tree = minidom.parse(xmlpipe)
+ result = []
+ for logentry in filter(None, tree.firstChild.childNodes):
+ if logentry.nodeType == logentry.ELEMENT_NODE:
+ result.append(svncommon.LogEntry(logentry))
+ return result
+
+#01234567890123456789012345678901234567890123467
+# 2256 hpk 165 Nov 24 17:55 __init__.py
+# XXX spotted by Guido, SVN 1.3.0 has different aligning, breaks the code!!!
+# 1312 johnny 1627 May 05 14:32 test_decorators.py
+#
+class InfoSvnCommand:
+ # the '0?' part in the middle is an indication of whether the resource is
+ # locked, see 'svn help ls'
+ lspattern = re.compile(
+ r'^ *(?P<rev>\d+) +(?P<author>.+?) +(0? *(?P<size>\d+))? '
+ r'*(?P<date>\w+ +\d{2} +[\d:]+) +(?P<file>.*)$')
+ def __init__(self, line):
+ # this is a typical line from 'svn ls http://...'
+ #_ 1127 jum 0 Jul 13 15:28 branch/
+ match = self.lspattern.match(line)
+ data = match.groupdict()
+ self._name = data['file']
+ if self._name[-1] == '/':
+ self._name = self._name[:-1]
+ self.kind = 'dir'
+ else:
+ self.kind = 'file'
+ #self.has_props = l.pop(0) == 'P'
+ self.created_rev = int(data['rev'])
+ self.last_author = data['author']
+ self.size = data['size'] and int(data['size']) or 0
+ self.mtime = parse_time_with_missing_year(data['date'])
+ self.time = self.mtime * 1000000
+
+ def __eq__(self, other):
+ return self.__dict__ == other.__dict__
+
+
+#____________________________________________________
+#
+# helper functions
+#____________________________________________________
+def parse_time_with_missing_year(timestr):
+ """ analyze the time part from a single line of "svn ls -v"
+ the svn output doesn't show the year makes the 'timestr'
+ ambigous.
+ """
+ import calendar
+ t_now = time.gmtime()
+
+ tparts = timestr.split()
+ month = time.strptime(tparts.pop(0), '%b')[1]
+ day = time.strptime(tparts.pop(0), '%d')[2]
+ last = tparts.pop(0) # year or hour:minute
+ try:
+ if ":" in last:
+ raise ValueError()
+ year = time.strptime(last, '%Y')[0]
+ hour = minute = 0
+ except ValueError:
+ hour, minute = time.strptime(last, '%H:%M')[3:5]
+ year = t_now[0]
+
+ t_result = (year, month, day, hour, minute, 0,0,0,0)
+ if t_result > t_now:
+ year -= 1
+ t_result = (year, month, day, hour, minute, 0,0,0,0)
+ return calendar.timegm(t_result)
+
+class PathEntry:
+ def __init__(self, ppart):
+ self.strpath = ppart.firstChild.nodeValue.encode('UTF-8')
+ self.action = ppart.getAttribute('action').encode('UTF-8')
+ if self.action == 'A':
+ self.copyfrom_path = ppart.getAttribute('copyfrom-path').encode('UTF-8')
+ if self.copyfrom_path:
+ self.copyfrom_rev = int(ppart.getAttribute('copyfrom-rev'))
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/svnwc.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/svnwc.py
new file mode 100644
index 00000000000..3138dd85da3
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_path/svnwc.py
@@ -0,0 +1,1240 @@
+"""
+svn-Command based Implementation of a Subversion WorkingCopy Path.
+
+ SvnWCCommandPath is the main class.
+
+"""
+
+import os, sys, time, re, calendar
+import py
+import subprocess
+from py._path import common
+
+#-----------------------------------------------------------
+# Caching latest repository revision and repo-paths
+# (getting them is slow with the current implementations)
+#
+# XXX make mt-safe
+#-----------------------------------------------------------
+
+class cache:
+ proplist = {}
+ info = {}
+ entries = {}
+ prop = {}
+
+class RepoEntry:
+ def __init__(self, url, rev, timestamp):
+ self.url = url
+ self.rev = rev
+ self.timestamp = timestamp
+
+ def __str__(self):
+ return "repo: %s;%s %s" %(self.url, self.rev, self.timestamp)
+
+class RepoCache:
+ """ The Repocache manages discovered repository paths
+ and their revisions. If inside a timeout the cache
+ will even return the revision of the root.
+ """
+ timeout = 20 # seconds after which we forget that we know the last revision
+
+ def __init__(self):
+ self.repos = []
+
+ def clear(self):
+ self.repos = []
+
+ def put(self, url, rev, timestamp=None):
+ if rev is None:
+ return
+ if timestamp is None:
+ timestamp = time.time()
+
+ for entry in self.repos:
+ if url == entry.url:
+ entry.timestamp = timestamp
+ entry.rev = rev
+ #print "set repo", entry
+ break
+ else:
+ entry = RepoEntry(url, rev, timestamp)
+ self.repos.append(entry)
+ #print "appended repo", entry
+
+ def get(self, url):
+ now = time.time()
+ for entry in self.repos:
+ if url.startswith(entry.url):
+ if now < entry.timestamp + self.timeout:
+ #print "returning immediate Etrny", entry
+ return entry.url, entry.rev
+ return entry.url, -1
+ return url, -1
+
+repositories = RepoCache()
+
+
+# svn support code
+
+ALLOWED_CHARS = "_ -/\\=$.~+%" #add characters as necessary when tested
+if sys.platform == "win32":
+ ALLOWED_CHARS += ":"
+ALLOWED_CHARS_HOST = ALLOWED_CHARS + '@:'
+
+def _getsvnversion(ver=[]):
+ try:
+ return ver[0]
+ except IndexError:
+ v = py.process.cmdexec("svn -q --version")
+ v.strip()
+ v = '.'.join(v.split('.')[:2])
+ ver.append(v)
+ return v
+
+def _escape_helper(text):
+ text = str(text)
+ if sys.platform != 'win32':
+ text = str(text).replace('$', '\\$')
+ return text
+
+def _check_for_bad_chars(text, allowed_chars=ALLOWED_CHARS):
+ for c in str(text):
+ if c.isalnum():
+ continue
+ if c in allowed_chars:
+ continue
+ return True
+ return False
+
+def checkbadchars(url):
+ # (hpk) not quite sure about the exact purpose, guido w.?
+ proto, uri = url.split("://", 1)
+ if proto != "file":
+ host, uripath = uri.split('/', 1)
+ # only check for bad chars in the non-protocol parts
+ if (_check_for_bad_chars(host, ALLOWED_CHARS_HOST) \
+ or _check_for_bad_chars(uripath, ALLOWED_CHARS)):
+ raise ValueError("bad char in %r" % (url, ))
+
+
+#_______________________________________________________________
+
+class SvnPathBase(common.PathBase):
+ """ Base implementation for SvnPath implementations. """
+ sep = '/'
+
+ def _geturl(self):
+ return self.strpath
+ url = property(_geturl, None, None, "url of this svn-path.")
+
+ def __str__(self):
+ """ return a string representation (including rev-number) """
+ return self.strpath
+
+ def __hash__(self):
+ return hash(self.strpath)
+
+ def new(self, **kw):
+ """ create a modified version of this path. A 'rev' argument
+ indicates a new revision.
+ the following keyword arguments modify various path parts::
+
+ http://host.com/repo/path/file.ext
+ |-----------------------| dirname
+ |------| basename
+ |--| purebasename
+ |--| ext
+ """
+ obj = object.__new__(self.__class__)
+ obj.rev = kw.get('rev', self.rev)
+ obj.auth = kw.get('auth', self.auth)
+ dirname, basename, purebasename, ext = self._getbyspec(
+ "dirname,basename,purebasename,ext")
+ if 'basename' in kw:
+ if 'purebasename' in kw or 'ext' in kw:
+ raise ValueError("invalid specification %r" % kw)
+ else:
+ pb = kw.setdefault('purebasename', purebasename)
+ ext = kw.setdefault('ext', ext)
+ if ext and not ext.startswith('.'):
+ ext = '.' + ext
+ kw['basename'] = pb + ext
+
+ kw.setdefault('dirname', dirname)
+ kw.setdefault('sep', self.sep)
+ if kw['basename']:
+ obj.strpath = "%(dirname)s%(sep)s%(basename)s" % kw
+ else:
+ obj.strpath = "%(dirname)s" % kw
+ return obj
+
+ def _getbyspec(self, spec):
+ """ get specified parts of the path. 'arg' is a string
+ with comma separated path parts. The parts are returned
+ in exactly the order of the specification.
+
+ you may specify the following parts:
+
+ http://host.com/repo/path/file.ext
+ |-----------------------| dirname
+ |------| basename
+ |--| purebasename
+ |--| ext
+ """
+ res = []
+ parts = self.strpath.split(self.sep)
+ for name in spec.split(','):
+ name = name.strip()
+ if name == 'dirname':
+ res.append(self.sep.join(parts[:-1]))
+ elif name == 'basename':
+ res.append(parts[-1])
+ else:
+ basename = parts[-1]
+ i = basename.rfind('.')
+ if i == -1:
+ purebasename, ext = basename, ''
+ else:
+ purebasename, ext = basename[:i], basename[i:]
+ if name == 'purebasename':
+ res.append(purebasename)
+ elif name == 'ext':
+ res.append(ext)
+ else:
+ raise NameError("Don't know part %r" % name)
+ return res
+
+ def __eq__(self, other):
+ """ return true if path and rev attributes each match """
+ return (str(self) == str(other) and
+ (self.rev == other.rev or self.rev == other.rev))
+
+ def __ne__(self, other):
+ return not self == other
+
+ def join(self, *args):
+ """ return a new Path (with the same revision) which is composed
+ of the self Path followed by 'args' path components.
+ """
+ if not args:
+ return self
+
+ args = tuple([arg.strip(self.sep) for arg in args])
+ parts = (self.strpath, ) + args
+ newpath = self.__class__(self.sep.join(parts), self.rev, self.auth)
+ return newpath
+
+ def propget(self, name):
+ """ return the content of the given property. """
+ value = self._propget(name)
+ return value
+
+ def proplist(self):
+ """ list all property names. """
+ content = self._proplist()
+ return content
+
+ def size(self):
+ """ Return the size of the file content of the Path. """
+ return self.info().size
+
+ def mtime(self):
+ """ Return the last modification time of the file. """
+ return self.info().mtime
+
+ # shared help methods
+
+ def _escape(self, cmd):
+ return _escape_helper(cmd)
+
+
+ #def _childmaxrev(self):
+ # """ return maximum revision number of childs (or self.rev if no childs) """
+ # rev = self.rev
+ # for name, info in self._listdir_nameinfo():
+ # rev = max(rev, info.created_rev)
+ # return rev
+
+ #def _getlatestrevision(self):
+ # """ return latest repo-revision for this path. """
+ # url = self.strpath
+ # path = self.__class__(url, None)
+ #
+ # # we need a long walk to find the root-repo and revision
+ # while 1:
+ # try:
+ # rev = max(rev, path._childmaxrev())
+ # previous = path
+ # path = path.dirpath()
+ # except (IOError, process.cmdexec.Error):
+ # break
+ # if rev is None:
+ # raise IOError, "could not determine newest repo revision for %s" % self
+ # return rev
+
+ class Checkers(common.Checkers):
+ def dir(self):
+ try:
+ return self.path.info().kind == 'dir'
+ except py.error.Error:
+ return self._listdirworks()
+
+ def _listdirworks(self):
+ try:
+ self.path.listdir()
+ except py.error.ENOENT:
+ return False
+ else:
+ return True
+
+ def file(self):
+ try:
+ return self.path.info().kind == 'file'
+ except py.error.ENOENT:
+ return False
+
+ def exists(self):
+ try:
+ return self.path.info()
+ except py.error.ENOENT:
+ return self._listdirworks()
+
+def parse_apr_time(timestr):
+ i = timestr.rfind('.')
+ if i == -1:
+ raise ValueError("could not parse %s" % timestr)
+ timestr = timestr[:i]
+ parsedtime = time.strptime(timestr, "%Y-%m-%dT%H:%M:%S")
+ return time.mktime(parsedtime)
+
+class PropListDict(dict):
+ """ a Dictionary which fetches values (InfoSvnCommand instances) lazily"""
+ def __init__(self, path, keynames):
+ dict.__init__(self, [(x, None) for x in keynames])
+ self.path = path
+
+ def __getitem__(self, key):
+ value = dict.__getitem__(self, key)
+ if value is None:
+ value = self.path.propget(key)
+ dict.__setitem__(self, key, value)
+ return value
+
+def fixlocale():
+ if sys.platform != 'win32':
+ return 'LC_ALL=C '
+ return ''
+
+# some nasty chunk of code to solve path and url conversion and quoting issues
+ILLEGAL_CHARS = '* | \\ / : < > ? \t \n \x0b \x0c \r'.split(' ')
+if os.sep in ILLEGAL_CHARS:
+ ILLEGAL_CHARS.remove(os.sep)
+ISWINDOWS = sys.platform == 'win32'
+_reg_allow_disk = re.compile(r'^([a-z]\:\\)?[^:]+$', re.I)
+def _check_path(path):
+ illegal = ILLEGAL_CHARS[:]
+ sp = path.strpath
+ if ISWINDOWS:
+ illegal.remove(':')
+ if not _reg_allow_disk.match(sp):
+ raise ValueError('path may not contain a colon (:)')
+ for char in sp:
+ if char not in string.printable or char in illegal:
+ raise ValueError('illegal character %r in path' % (char,))
+
+def path_to_fspath(path, addat=True):
+ _check_path(path)
+ sp = path.strpath
+ if addat and path.rev != -1:
+ sp = '%s@%s' % (sp, path.rev)
+ elif addat:
+ sp = '%s@HEAD' % (sp,)
+ return sp
+
+def url_from_path(path):
+ fspath = path_to_fspath(path, False)
+ from urllib import quote
+ if ISWINDOWS:
+ match = _reg_allow_disk.match(fspath)
+ fspath = fspath.replace('\\', '/')
+ if match.group(1):
+ fspath = '/%s%s' % (match.group(1).replace('\\', '/'),
+ quote(fspath[len(match.group(1)):]))
+ else:
+ fspath = quote(fspath)
+ else:
+ fspath = quote(fspath)
+ if path.rev != -1:
+ fspath = '%s@%s' % (fspath, path.rev)
+ else:
+ fspath = '%s@HEAD' % (fspath,)
+ return 'file://%s' % (fspath,)
+
+class SvnAuth(object):
+ """ container for auth information for Subversion """
+ def __init__(self, username, password, cache_auth=True, interactive=True):
+ self.username = username
+ self.password = password
+ self.cache_auth = cache_auth
+ self.interactive = interactive
+
+ def makecmdoptions(self):
+ uname = self.username.replace('"', '\\"')
+ passwd = self.password.replace('"', '\\"')
+ ret = []
+ if uname:
+ ret.append('--username="%s"' % (uname,))
+ if passwd:
+ ret.append('--password="%s"' % (passwd,))
+ if not self.cache_auth:
+ ret.append('--no-auth-cache')
+ if not self.interactive:
+ ret.append('--non-interactive')
+ return ' '.join(ret)
+
+ def __str__(self):
+ return "<SvnAuth username=%s ...>" %(self.username,)
+
+rex_blame = re.compile(r'\s*(\d+)\s*(\S+) (.*)')
+
+class SvnWCCommandPath(common.PathBase):
+ """ path implementation offering access/modification to svn working copies.
+ It has methods similar to the functions in os.path and similar to the
+ commands of the svn client.
+ """
+ sep = os.sep
+
+ def __new__(cls, wcpath=None, auth=None):
+ self = object.__new__(cls)
+ if isinstance(wcpath, cls):
+ if wcpath.__class__ == cls:
+ return wcpath
+ wcpath = wcpath.localpath
+ if _check_for_bad_chars(str(wcpath),
+ ALLOWED_CHARS):
+ raise ValueError("bad char in wcpath %s" % (wcpath, ))
+ self.localpath = py.path.local(wcpath)
+ self.auth = auth
+ return self
+
+ strpath = property(lambda x: str(x.localpath), None, None, "string path")
+ rev = property(lambda x: x.info(usecache=0).rev, None, None, "revision")
+
+ def __eq__(self, other):
+ return self.localpath == getattr(other, 'localpath', None)
+
+ def _geturl(self):
+ if getattr(self, '_url', None) is None:
+ info = self.info()
+ self._url = info.url #SvnPath(info.url, info.rev)
+ assert isinstance(self._url, py.builtin._basestring)
+ return self._url
+
+ url = property(_geturl, None, None, "url of this WC item")
+
+ def _escape(self, cmd):
+ return _escape_helper(cmd)
+
+ def dump(self, obj):
+ """ pickle object into path location"""
+ return self.localpath.dump(obj)
+
+ def svnurl(self):
+ """ return current SvnPath for this WC-item. """
+ info = self.info()
+ return py.path.svnurl(info.url)
+
+ def __repr__(self):
+ return "svnwc(%r)" % (self.strpath) # , self._url)
+
+ def __str__(self):
+ return str(self.localpath)
+
+ def _makeauthoptions(self):
+ if self.auth is None:
+ return ''
+ return self.auth.makecmdoptions()
+
+ def _authsvn(self, cmd, args=None):
+ args = args and list(args) or []
+ args.append(self._makeauthoptions())
+ return self._svn(cmd, *args)
+
+ def _svn(self, cmd, *args):
+ l = ['svn %s' % cmd]
+ args = [self._escape(item) for item in args]
+ l.extend(args)
+ l.append('"%s"' % self._escape(self.strpath))
+ # try fixing the locale because we can't otherwise parse
+ string = fixlocale() + " ".join(l)
+ try:
+ try:
+ key = 'LC_MESSAGES'
+ hold = os.environ.get(key)
+ os.environ[key] = 'C'
+ out = py.process.cmdexec(string)
+ finally:
+ if hold:
+ os.environ[key] = hold
+ else:
+ del os.environ[key]
+ except py.process.cmdexec.Error:
+ e = sys.exc_info()[1]
+ strerr = e.err.lower()
+ if strerr.find('not found') != -1:
+ raise py.error.ENOENT(self)
+ elif strerr.find("E200009:") != -1:
+ raise py.error.ENOENT(self)
+ if (strerr.find('file exists') != -1 or
+ strerr.find('file already exists') != -1 or
+ strerr.find('w150002:') != -1 or
+ strerr.find("can't create directory") != -1):
+ raise py.error.EEXIST(strerr) #self)
+ raise
+ return out
+
+ def switch(self, url):
+ """ switch to given URL. """
+ self._authsvn('switch', [url])
+
+ def checkout(self, url=None, rev=None):
+ """ checkout from url to local wcpath. """
+ args = []
+ if url is None:
+ url = self.url
+ if rev is None or rev == -1:
+ if (sys.platform != 'win32' and
+ _getsvnversion() == '1.3'):
+ url += "@HEAD"
+ else:
+ if _getsvnversion() == '1.3':
+ url += "@%d" % rev
+ else:
+ args.append('-r' + str(rev))
+ args.append(url)
+ self._authsvn('co', args)
+
+ def update(self, rev='HEAD', interactive=True):
+ """ update working copy item to given revision. (None -> HEAD). """
+ opts = ['-r', rev]
+ if not interactive:
+ opts.append("--non-interactive")
+ self._authsvn('up', opts)
+
+ def write(self, content, mode='w'):
+ """ write content into local filesystem wc. """
+ self.localpath.write(content, mode)
+
+ def dirpath(self, *args):
+ """ return the directory Path of the current Path. """
+ return self.__class__(self.localpath.dirpath(*args), auth=self.auth)
+
+ def _ensuredirs(self):
+ parent = self.dirpath()
+ if parent.check(dir=0):
+ parent._ensuredirs()
+ if self.check(dir=0):
+ self.mkdir()
+ return self
+
+ def ensure(self, *args, **kwargs):
+ """ ensure that an args-joined path exists (by default as
+ a file). if you specify a keyword argument 'directory=True'
+ then the path is forced to be a directory path.
+ """
+ p = self.join(*args)
+ if p.check():
+ if p.check(versioned=False):
+ p.add()
+ return p
+ if kwargs.get('dir', 0):
+ return p._ensuredirs()
+ parent = p.dirpath()
+ parent._ensuredirs()
+ p.write("")
+ p.add()
+ return p
+
+ def mkdir(self, *args):
+ """ create & return the directory joined with args. """
+ if args:
+ return self.join(*args).mkdir()
+ else:
+ self._svn('mkdir')
+ return self
+
+ def add(self):
+ """ add ourself to svn """
+ self._svn('add')
+
+ def remove(self, rec=1, force=1):
+ """ remove a file or a directory tree. 'rec'ursive is
+ ignored and considered always true (because of
+ underlying svn semantics.
+ """
+ assert rec, "svn cannot remove non-recursively"
+ if not self.check(versioned=True):
+ # not added to svn (anymore?), just remove
+ py.path.local(self).remove()
+ return
+ flags = []
+ if force:
+ flags.append('--force')
+ self._svn('remove', *flags)
+
+ def copy(self, target):
+ """ copy path to target."""
+ py.process.cmdexec("svn copy %s %s" %(str(self), str(target)))
+
+ def rename(self, target):
+ """ rename this path to target. """
+ py.process.cmdexec("svn move --force %s %s" %(str(self), str(target)))
+
+ def lock(self):
+ """ set a lock (exclusive) on the resource """
+ out = self._authsvn('lock').strip()
+ if not out:
+ # warning or error, raise exception
+ raise ValueError("unknown error in svn lock command")
+
+ def unlock(self):
+ """ unset a previously set lock """
+ out = self._authsvn('unlock').strip()
+ if out.startswith('svn:'):
+ # warning or error, raise exception
+ raise Exception(out[4:])
+
+ def cleanup(self):
+ """ remove any locks from the resource """
+ # XXX should be fixed properly!!!
+ try:
+ self.unlock()
+ except:
+ pass
+
+ def status(self, updates=0, rec=0, externals=0):
+ """ return (collective) Status object for this file. """
+ # http://svnbook.red-bean.com/book.html#svn-ch-3-sect-4.3.1
+ # 2201 2192 jum test
+ # XXX
+ if externals:
+ raise ValueError("XXX cannot perform status() "
+ "on external items yet")
+ else:
+ #1.2 supports: externals = '--ignore-externals'
+ externals = ''
+ if rec:
+ rec= ''
+ else:
+ rec = '--non-recursive'
+
+ # XXX does not work on all subversion versions
+ #if not externals:
+ # externals = '--ignore-externals'
+
+ if updates:
+ updates = '-u'
+ else:
+ updates = ''
+
+ try:
+ cmd = 'status -v --xml --no-ignore %s %s %s' % (
+ updates, rec, externals)
+ out = self._authsvn(cmd)
+ except py.process.cmdexec.Error:
+ cmd = 'status -v --no-ignore %s %s %s' % (
+ updates, rec, externals)
+ out = self._authsvn(cmd)
+ rootstatus = WCStatus(self).fromstring(out, self)
+ else:
+ rootstatus = XMLWCStatus(self).fromstring(out, self)
+ return rootstatus
+
+ def diff(self, rev=None):
+ """ return a diff of the current path against revision rev (defaulting
+ to the last one).
+ """
+ args = []
+ if rev is not None:
+ args.append("-r %d" % rev)
+ out = self._authsvn('diff', args)
+ return out
+
+ def blame(self):
+ """ return a list of tuples of three elements:
+ (revision, commiter, line)
+ """
+ out = self._svn('blame')
+ result = []
+ blamelines = out.splitlines()
+ reallines = py.path.svnurl(self.url).readlines()
+ for i, (blameline, line) in enumerate(
+ zip(blamelines, reallines)):
+ m = rex_blame.match(blameline)
+ if not m:
+ raise ValueError("output line %r of svn blame does not match "
+ "expected format" % (line, ))
+ rev, name, _ = m.groups()
+ result.append((int(rev), name, line))
+ return result
+
+ _rex_commit = re.compile(r'.*Committed revision (\d+)\.$', re.DOTALL)
+ def commit(self, msg='', rec=1):
+ """ commit with support for non-recursive commits """
+ # XXX i guess escaping should be done better here?!?
+ cmd = 'commit -m "%s" --force-log' % (msg.replace('"', '\\"'),)
+ if not rec:
+ cmd += ' -N'
+ out = self._authsvn(cmd)
+ try:
+ del cache.info[self]
+ except KeyError:
+ pass
+ if out:
+ m = self._rex_commit.match(out)
+ return int(m.group(1))
+
+ def propset(self, name, value, *args):
+ """ set property name to value on this path. """
+ d = py.path.local.mkdtemp()
+ try:
+ p = d.join('value')
+ p.write(value)
+ self._svn('propset', name, '--file', str(p), *args)
+ finally:
+ d.remove()
+
+ def propget(self, name):
+ """ get property name on this path. """
+ res = self._svn('propget', name)
+ return res[:-1] # strip trailing newline
+
+ def propdel(self, name):
+ """ delete property name on this path. """
+ res = self._svn('propdel', name)
+ return res[:-1] # strip trailing newline
+
+ def proplist(self, rec=0):
+ """ return a mapping of property names to property values.
+If rec is True, then return a dictionary mapping sub-paths to such mappings.
+"""
+ if rec:
+ res = self._svn('proplist -R')
+ return make_recursive_propdict(self, res)
+ else:
+ res = self._svn('proplist')
+ lines = res.split('\n')
+ lines = [x.strip() for x in lines[1:]]
+ return PropListDict(self, lines)
+
+ def revert(self, rec=0):
+ """ revert the local changes of this path. if rec is True, do so
+recursively. """
+ if rec:
+ result = self._svn('revert -R')
+ else:
+ result = self._svn('revert')
+ return result
+
+ def new(self, **kw):
+ """ create a modified version of this path. A 'rev' argument
+ indicates a new revision.
+ the following keyword arguments modify various path parts:
+
+ http://host.com/repo/path/file.ext
+ |-----------------------| dirname
+ |------| basename
+ |--| purebasename
+ |--| ext
+ """
+ if kw:
+ localpath = self.localpath.new(**kw)
+ else:
+ localpath = self.localpath
+ return self.__class__(localpath, auth=self.auth)
+
+ def join(self, *args, **kwargs):
+ """ return a new Path (with the same revision) which is composed
+ of the self Path followed by 'args' path components.
+ """
+ if not args:
+ return self
+ localpath = self.localpath.join(*args, **kwargs)
+ return self.__class__(localpath, auth=self.auth)
+
+ def info(self, usecache=1):
+ """ return an Info structure with svn-provided information. """
+ info = usecache and cache.info.get(self)
+ if not info:
+ try:
+ output = self._svn('info')
+ except py.process.cmdexec.Error:
+ e = sys.exc_info()[1]
+ if e.err.find('Path is not a working copy directory') != -1:
+ raise py.error.ENOENT(self, e.err)
+ elif e.err.find("is not under version control") != -1:
+ raise py.error.ENOENT(self, e.err)
+ raise
+ # XXX SVN 1.3 has output on stderr instead of stdout (while it does
+ # return 0!), so a bit nasty, but we assume no output is output
+ # to stderr...
+ if (output.strip() == '' or
+ output.lower().find('not a versioned resource') != -1):
+ raise py.error.ENOENT(self, output)
+ info = InfoSvnWCCommand(output)
+
+ # Can't reliably compare on Windows without access to win32api
+ if sys.platform != 'win32':
+ if info.path != self.localpath:
+ raise py.error.ENOENT(self, "not a versioned resource:" +
+ " %s != %s" % (info.path, self.localpath))
+ cache.info[self] = info
+ return info
+
+ def listdir(self, fil=None, sort=None):
+ """ return a sequence of Paths.
+
+ listdir will return either a tuple or a list of paths
+ depending on implementation choices.
+ """
+ if isinstance(fil, str):
+ fil = common.FNMatcher(fil)
+ # XXX unify argument naming with LocalPath.listdir
+ def notsvn(path):
+ return path.basename != '.svn'
+
+ paths = []
+ for localpath in self.localpath.listdir(notsvn):
+ p = self.__class__(localpath, auth=self.auth)
+ if notsvn(p) and (not fil or fil(p)):
+ paths.append(p)
+ self._sortlist(paths, sort)
+ return paths
+
+ def open(self, mode='r'):
+ """ return an opened file with the given mode. """
+ return open(self.strpath, mode)
+
+ def _getbyspec(self, spec):
+ return self.localpath._getbyspec(spec)
+
+ class Checkers(py.path.local.Checkers):
+ def __init__(self, path):
+ self.svnwcpath = path
+ self.path = path.localpath
+ def versioned(self):
+ try:
+ s = self.svnwcpath.info()
+ except (py.error.ENOENT, py.error.EEXIST):
+ return False
+ except py.process.cmdexec.Error:
+ e = sys.exc_info()[1]
+ if e.err.find('is not a working copy')!=-1:
+ return False
+ if e.err.lower().find('not a versioned resource') != -1:
+ return False
+ raise
+ else:
+ return True
+
+ def log(self, rev_start=None, rev_end=1, verbose=False):
+ """ return a list of LogEntry instances for this path.
+rev_start is the starting revision (defaulting to the first one).
+rev_end is the last revision (defaulting to HEAD).
+if verbose is True, then the LogEntry instances also know which files changed.
+"""
+ assert self.check() # make it simpler for the pipe
+ rev_start = rev_start is None and "HEAD" or rev_start
+ rev_end = rev_end is None and "HEAD" or rev_end
+ if rev_start == "HEAD" and rev_end == 1:
+ rev_opt = ""
+ else:
+ rev_opt = "-r %s:%s" % (rev_start, rev_end)
+ verbose_opt = verbose and "-v" or ""
+ locale_env = fixlocale()
+ # some blather on stderr
+ auth_opt = self._makeauthoptions()
+ #stdin, stdout, stderr = os.popen3(locale_env +
+ # 'svn log --xml %s %s %s "%s"' % (
+ # rev_opt, verbose_opt, auth_opt,
+ # self.strpath))
+ cmd = locale_env + 'svn log --xml %s %s %s "%s"' % (
+ rev_opt, verbose_opt, auth_opt, self.strpath)
+
+ popen = subprocess.Popen(cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ shell=True,
+ )
+ stdout, stderr = popen.communicate()
+ stdout = py.builtin._totext(stdout, sys.getdefaultencoding())
+ minidom,ExpatError = importxml()
+ try:
+ tree = minidom.parseString(stdout)
+ except ExpatError:
+ raise ValueError('no such revision')
+ result = []
+ for logentry in filter(None, tree.firstChild.childNodes):
+ if logentry.nodeType == logentry.ELEMENT_NODE:
+ result.append(LogEntry(logentry))
+ return result
+
+ def size(self):
+ """ Return the size of the file content of the Path. """
+ return self.info().size
+
+ def mtime(self):
+ """ Return the last modification time of the file. """
+ return self.info().mtime
+
+ def __hash__(self):
+ return hash((self.strpath, self.__class__, self.auth))
+
+
+class WCStatus:
+ attrnames = ('modified','added', 'conflict', 'unchanged', 'external',
+ 'deleted', 'prop_modified', 'unknown', 'update_available',
+ 'incomplete', 'kindmismatch', 'ignored', 'locked', 'replaced'
+ )
+
+ def __init__(self, wcpath, rev=None, modrev=None, author=None):
+ self.wcpath = wcpath
+ self.rev = rev
+ self.modrev = modrev
+ self.author = author
+
+ for name in self.attrnames:
+ setattr(self, name, [])
+
+ def allpath(self, sort=True, **kw):
+ d = {}
+ for name in self.attrnames:
+ if name not in kw or kw[name]:
+ for path in getattr(self, name):
+ d[path] = 1
+ l = d.keys()
+ if sort:
+ l.sort()
+ return l
+
+ # XXX a bit scary to assume there's always 2 spaces between username and
+ # path, however with win32 allowing spaces in user names there doesn't
+ # seem to be a more solid approach :(
+ _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)')
+
+ def fromstring(data, rootwcpath, rev=None, modrev=None, author=None):
+ """ return a new WCStatus object from data 's'
+ """
+ rootstatus = WCStatus(rootwcpath, rev, modrev, author)
+ update_rev = None
+ for line in data.split('\n'):
+ if not line.strip():
+ continue
+ #print "processing %r" % line
+ flags, rest = line[:8], line[8:]
+ # first column
+ c0,c1,c2,c3,c4,c5,x6,c7 = flags
+ #if '*' in line:
+ # print "flags", repr(flags), "rest", repr(rest)
+
+ if c0 in '?XI':
+ fn = line.split(None, 1)[1]
+ if c0 == '?':
+ wcpath = rootwcpath.join(fn, abs=1)
+ rootstatus.unknown.append(wcpath)
+ elif c0 == 'X':
+ wcpath = rootwcpath.__class__(
+ rootwcpath.localpath.join(fn, abs=1),
+ auth=rootwcpath.auth)
+ rootstatus.external.append(wcpath)
+ elif c0 == 'I':
+ wcpath = rootwcpath.join(fn, abs=1)
+ rootstatus.ignored.append(wcpath)
+
+ continue
+
+ #elif c0 in '~!' or c4 == 'S':
+ # raise NotImplementedError("received flag %r" % c0)
+
+ m = WCStatus._rex_status.match(rest)
+ if not m:
+ if c7 == '*':
+ fn = rest.strip()
+ wcpath = rootwcpath.join(fn, abs=1)
+ rootstatus.update_available.append(wcpath)
+ continue
+ if line.lower().find('against revision:')!=-1:
+ update_rev = int(rest.split(':')[1].strip())
+ continue
+ if line.lower().find('status on external') > -1:
+ # XXX not sure what to do here... perhaps we want to
+ # store some state instead of just continuing, as right
+ # now it makes the top-level external get added twice
+ # (once as external, once as 'normal' unchanged item)
+ # because of the way SVN presents external items
+ continue
+ # keep trying
+ raise ValueError("could not parse line %r" % line)
+ else:
+ rev, modrev, author, fn = m.groups()
+ wcpath = rootwcpath.join(fn, abs=1)
+ #assert wcpath.check()
+ if c0 == 'M':
+ assert wcpath.check(file=1), "didn't expect a directory with changed content here"
+ rootstatus.modified.append(wcpath)
+ elif c0 == 'A' or c3 == '+' :
+ rootstatus.added.append(wcpath)
+ elif c0 == 'D':
+ rootstatus.deleted.append(wcpath)
+ elif c0 == 'C':
+ rootstatus.conflict.append(wcpath)
+ elif c0 == '~':
+ rootstatus.kindmismatch.append(wcpath)
+ elif c0 == '!':
+ rootstatus.incomplete.append(wcpath)
+ elif c0 == 'R':
+ rootstatus.replaced.append(wcpath)
+ elif not c0.strip():
+ rootstatus.unchanged.append(wcpath)
+ else:
+ raise NotImplementedError("received flag %r" % c0)
+
+ if c1 == 'M':
+ rootstatus.prop_modified.append(wcpath)
+ # XXX do we cover all client versions here?
+ if c2 == 'L' or c5 == 'K':
+ rootstatus.locked.append(wcpath)
+ if c7 == '*':
+ rootstatus.update_available.append(wcpath)
+
+ if wcpath == rootwcpath:
+ rootstatus.rev = rev
+ rootstatus.modrev = modrev
+ rootstatus.author = author
+ if update_rev:
+ rootstatus.update_rev = update_rev
+ continue
+ return rootstatus
+ fromstring = staticmethod(fromstring)
+
+class XMLWCStatus(WCStatus):
+ def fromstring(data, rootwcpath, rev=None, modrev=None, author=None):
+ """ parse 'data' (XML string as outputted by svn st) into a status obj
+ """
+ # XXX for externals, the path is shown twice: once
+ # with external information, and once with full info as if
+ # the item was a normal non-external... the current way of
+ # dealing with this issue is by ignoring it - this does make
+ # externals appear as external items as well as 'normal',
+ # unchanged ones in the status object so this is far from ideal
+ rootstatus = WCStatus(rootwcpath, rev, modrev, author)
+ update_rev = None
+ minidom, ExpatError = importxml()
+ try:
+ doc = minidom.parseString(data)
+ except ExpatError:
+ e = sys.exc_info()[1]
+ raise ValueError(str(e))
+ urevels = doc.getElementsByTagName('against')
+ if urevels:
+ rootstatus.update_rev = urevels[-1].getAttribute('revision')
+ for entryel in doc.getElementsByTagName('entry'):
+ path = entryel.getAttribute('path')
+ statusel = entryel.getElementsByTagName('wc-status')[0]
+ itemstatus = statusel.getAttribute('item')
+
+ if itemstatus == 'unversioned':
+ wcpath = rootwcpath.join(path, abs=1)
+ rootstatus.unknown.append(wcpath)
+ continue
+ elif itemstatus == 'external':
+ wcpath = rootwcpath.__class__(
+ rootwcpath.localpath.join(path, abs=1),
+ auth=rootwcpath.auth)
+ rootstatus.external.append(wcpath)
+ continue
+ elif itemstatus == 'ignored':
+ wcpath = rootwcpath.join(path, abs=1)
+ rootstatus.ignored.append(wcpath)
+ continue
+ elif itemstatus == 'incomplete':
+ wcpath = rootwcpath.join(path, abs=1)
+ rootstatus.incomplete.append(wcpath)
+ continue
+
+ rev = statusel.getAttribute('revision')
+ if itemstatus == 'added' or itemstatus == 'none':
+ rev = '0'
+ modrev = '?'
+ author = '?'
+ date = ''
+ elif itemstatus == "replaced":
+ pass
+ else:
+ #print entryel.toxml()
+ commitel = entryel.getElementsByTagName('commit')[0]
+ if commitel:
+ modrev = commitel.getAttribute('revision')
+ author = ''
+ author_els = commitel.getElementsByTagName('author')
+ if author_els:
+ for c in author_els[0].childNodes:
+ author += c.nodeValue
+ date = ''
+ for c in commitel.getElementsByTagName('date')[0]\
+ .childNodes:
+ date += c.nodeValue
+
+ wcpath = rootwcpath.join(path, abs=1)
+
+ assert itemstatus != 'modified' or wcpath.check(file=1), (
+ 'did\'t expect a directory with changed content here')
+
+ itemattrname = {
+ 'normal': 'unchanged',
+ 'unversioned': 'unknown',
+ 'conflicted': 'conflict',
+ 'none': 'added',
+ }.get(itemstatus, itemstatus)
+
+ attr = getattr(rootstatus, itemattrname)
+ attr.append(wcpath)
+
+ propsstatus = statusel.getAttribute('props')
+ if propsstatus not in ('none', 'normal'):
+ rootstatus.prop_modified.append(wcpath)
+
+ if wcpath == rootwcpath:
+ rootstatus.rev = rev
+ rootstatus.modrev = modrev
+ rootstatus.author = author
+ rootstatus.date = date
+
+ # handle repos-status element (remote info)
+ rstatusels = entryel.getElementsByTagName('repos-status')
+ if rstatusels:
+ rstatusel = rstatusels[0]
+ ritemstatus = rstatusel.getAttribute('item')
+ if ritemstatus in ('added', 'modified'):
+ rootstatus.update_available.append(wcpath)
+
+ lockels = entryel.getElementsByTagName('lock')
+ if len(lockels):
+ rootstatus.locked.append(wcpath)
+
+ return rootstatus
+ fromstring = staticmethod(fromstring)
+
+class InfoSvnWCCommand:
+ def __init__(self, output):
+ # Path: test
+ # URL: http://codespeak.net/svn/std.path/trunk/dist/std.path/test
+ # Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada
+ # Revision: 2151
+ # Node Kind: directory
+ # Schedule: normal
+ # Last Changed Author: hpk
+ # Last Changed Rev: 2100
+ # Last Changed Date: 2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003)
+ # Properties Last Updated: 2003-11-03 14:47:48 +0100 (Mon, 03 Nov 2003)
+
+ d = {}
+ for line in output.split('\n'):
+ if not line.strip():
+ continue
+ key, value = line.split(':', 1)
+ key = key.lower().replace(' ', '')
+ value = value.strip()
+ d[key] = value
+ try:
+ self.url = d['url']
+ except KeyError:
+ raise ValueError("Not a versioned resource")
+ #raise ValueError, "Not a versioned resource %r" % path
+ self.kind = d['nodekind'] == 'directory' and 'dir' or d['nodekind']
+ try:
+ self.rev = int(d['revision'])
+ except KeyError:
+ self.rev = None
+
+ self.path = py.path.local(d['path'])
+ self.size = self.path.size()
+ if 'lastchangedrev' in d:
+ self.created_rev = int(d['lastchangedrev'])
+ if 'lastchangedauthor' in d:
+ self.last_author = d['lastchangedauthor']
+ if 'lastchangeddate' in d:
+ self.mtime = parse_wcinfotime(d['lastchangeddate'])
+ self.time = self.mtime * 1000000
+
+ def __eq__(self, other):
+ return self.__dict__ == other.__dict__
+
+def parse_wcinfotime(timestr):
+ """ Returns seconds since epoch, UTC. """
+ # example: 2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003)
+ m = re.match(r'(\d+-\d+-\d+ \d+:\d+:\d+) ([+-]\d+) .*', timestr)
+ if not m:
+ raise ValueError("timestring %r does not match" % timestr)
+ timestr, timezone = m.groups()
+ # do not handle timezone specially, return value should be UTC
+ parsedtime = time.strptime(timestr, "%Y-%m-%d %H:%M:%S")
+ return calendar.timegm(parsedtime)
+
+def make_recursive_propdict(wcroot,
+ output,
+ rex = re.compile("Properties on '(.*)':")):
+ """ Return a dictionary of path->PropListDict mappings. """
+ lines = [x for x in output.split('\n') if x]
+ pdict = {}
+ while lines:
+ line = lines.pop(0)
+ m = rex.match(line)
+ if not m:
+ raise ValueError("could not parse propget-line: %r" % line)
+ path = m.groups()[0]
+ wcpath = wcroot.join(path, abs=1)
+ propnames = []
+ while lines and lines[0].startswith(' '):
+ propname = lines.pop(0).strip()
+ propnames.append(propname)
+ assert propnames, "must have found properties!"
+ pdict[wcpath] = PropListDict(wcpath, propnames)
+ return pdict
+
+
+def importxml(cache=[]):
+ if cache:
+ return cache
+ from xml.dom import minidom
+ from xml.parsers.expat import ExpatError
+ cache.extend([minidom, ExpatError])
+ return cache
+
+class LogEntry:
+ def __init__(self, logentry):
+ self.rev = int(logentry.getAttribute('revision'))
+ for lpart in filter(None, logentry.childNodes):
+ if lpart.nodeType == lpart.ELEMENT_NODE:
+ if lpart.nodeName == 'author':
+ self.author = lpart.firstChild.nodeValue
+ elif lpart.nodeName == 'msg':
+ if lpart.firstChild:
+ self.msg = lpart.firstChild.nodeValue
+ else:
+ self.msg = ''
+ elif lpart.nodeName == 'date':
+ #2003-07-29T20:05:11.598637Z
+ timestr = lpart.firstChild.nodeValue
+ self.date = parse_apr_time(timestr)
+ elif lpart.nodeName == 'paths':
+ self.strpaths = []
+ for ppart in filter(None, lpart.childNodes):
+ if ppart.nodeType == ppart.ELEMENT_NODE:
+ self.strpaths.append(PathEntry(ppart))
+ def __repr__(self):
+ return '<Logentry rev=%d author=%s date=%s>' % (
+ self.rev, self.author, self.date)
+
+
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_process/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_process/__init__.py
index 86c714ad1ae..86c714ad1ae 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_process/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_process/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_process/cmdexec.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_process/cmdexec.py
index f83a2494029..f83a2494029 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_process/cmdexec.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_process/cmdexec.py
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_process/forkedfunc.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_process/forkedfunc.py
index 1c285306884..1c285306884 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_process/forkedfunc.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_process/forkedfunc.py
diff --git a/tests/wpt/web-platform-tests/tools/py/py/_process/killproc.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_process/killproc.py
index 18e8310b5f6..18e8310b5f6 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/_process/killproc.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_process/killproc.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_std.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_std.py
new file mode 100644
index 00000000000..74d43672654
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_std.py
@@ -0,0 +1,26 @@
+import sys
+import warnings
+
+
+class PyStdIsDeprecatedWarning(DeprecationWarning):
+ pass
+
+
+class Std(object):
+ """ makes top-level python modules available as an attribute,
+ importing them on first access.
+ """
+
+ def __init__(self):
+ self.__dict__ = sys.modules
+
+ def __getattr__(self, name):
+ warnings.warn("py.std is deprecated, plase import %s directly" % name,
+ category=PyStdIsDeprecatedWarning)
+ try:
+ m = __import__(name)
+ except ImportError:
+ raise AttributeError("py.std: could not import %s" % name)
+ return m
+
+std = Std()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/DESCRIPTION.rst b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/DESCRIPTION.rst
new file mode 100644
index 00000000000..548222007fd
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/DESCRIPTION.rst
@@ -0,0 +1,87 @@
+Welcome to apipkg!
+------------------------
+
+With apipkg you can control the exported namespace of a
+python package and greatly reduce the number of imports for your users.
+It is a `small pure python module`_ that works on virtually all Python
+versions, including CPython2.3 to Python3.1, Jython and PyPy. It co-operates
+well with Python's ``help()`` system, custom importers (PEP302) and common
+command line completion tools.
+
+Usage is very simple: you can require 'apipkg' as a dependency or you
+can copy paste the <200 Lines of code into your project.
+
+
+Tutorial example
+-------------------
+
+Here is a simple ``mypkg`` package that specifies one namespace
+and exports two objects imported from different modules::
+
+ # mypkg/__init__.py
+ import apipkg
+ apipkg.initpkg(__name__, {
+ 'path': {
+ 'Class1': "_mypkg.somemodule:Class1",
+ 'clsattr': "_mypkg.othermodule:Class2.attr",
+ }
+ }
+
+The package is initialized with a dictionary as namespace.
+
+You need to create a ``_mypkg`` package with a ``somemodule.py``
+and ``othermodule.py`` containing the respective classes.
+The ``_mypkg`` is not special - it's a completely
+regular python package.
+
+Namespace dictionaries contain ``name: value`` mappings
+where the value may be another namespace dictionary or
+a string specifying an import location. On accessing
+an namespace attribute an import will be performed::
+
+ >>> import mypkg
+ >>> mypkg.path
+ <ApiModule 'mypkg.path'>
+ >>> mypkg.path.Class1 # '_mypkg.somemodule' gets imported now
+ <class _mypkg.somemodule.Class1 at 0xb7d428fc>
+ >>> mypkg.path.clsattr # '_mypkg.othermodule' gets imported now
+ 4 # the value of _mypkg.othermodule.Class2.attr
+
+The ``mypkg.path`` namespace and its two entries are
+loaded when they are accessed. This means:
+
+* lazy loading - only what is actually needed is ever loaded
+
+* only the root "mypkg" ever needs to be imported to get
+ access to the complete functionality.
+
+* the underlying modules are also accessible, for example::
+
+ from mypkg.sub import Class1
+
+
+Including apipkg in your package
+--------------------------------------
+
+If you don't want to add an ``apipkg`` dependency to your package you
+can copy the `apipkg.py`_ file somewhere to your own package,
+for example ``_mypkg/apipkg.py`` in the above example. You
+then import the ``initpkg`` function from that new place and
+are good to go.
+
+.. _`small pure python module`:
+.. _`apipkg.py`: http://bitbucket.org/hpk42/apipkg/src/tip/apipkg.py
+
+Feedback?
+-----------------------
+
+If you have questions you are welcome to
+
+* join the #pylib channel on irc.freenode.net
+* subscribe to the http://codespeak.net/mailman/listinfo/py-dev list.
+* create an issue on http://bitbucket.org/hpk42/apipkg/issues
+
+have fun,
+holger krekel
+
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/INSTALLER b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/INSTALLER
new file mode 100644
index 00000000000..a1b589e38a3
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/METADATA b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/METADATA
new file mode 100644
index 00000000000..eb7e60acffa
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/METADATA
@@ -0,0 +1,109 @@
+Metadata-Version: 2.0
+Name: apipkg
+Version: 1.4
+Summary: apipkg: namespace control and lazy-import mechanism
+Home-page: http://bitbucket.org/hpk42/apipkg
+Author: holger krekel
+Author-email: holger at merlinux.eu
+License: MIT License
+Platform: unix
+Platform: linux
+Platform: osx
+Platform: cygwin
+Platform: win32
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Programming Language :: Python
+
+Welcome to apipkg!
+------------------------
+
+With apipkg you can control the exported namespace of a
+python package and greatly reduce the number of imports for your users.
+It is a `small pure python module`_ that works on virtually all Python
+versions, including CPython2.3 to Python3.1, Jython and PyPy. It co-operates
+well with Python's ``help()`` system, custom importers (PEP302) and common
+command line completion tools.
+
+Usage is very simple: you can require 'apipkg' as a dependency or you
+can copy paste the <200 Lines of code into your project.
+
+
+Tutorial example
+-------------------
+
+Here is a simple ``mypkg`` package that specifies one namespace
+and exports two objects imported from different modules::
+
+ # mypkg/__init__.py
+ import apipkg
+ apipkg.initpkg(__name__, {
+ 'path': {
+ 'Class1': "_mypkg.somemodule:Class1",
+ 'clsattr': "_mypkg.othermodule:Class2.attr",
+ }
+ }
+
+The package is initialized with a dictionary as namespace.
+
+You need to create a ``_mypkg`` package with a ``somemodule.py``
+and ``othermodule.py`` containing the respective classes.
+The ``_mypkg`` is not special - it's a completely
+regular python package.
+
+Namespace dictionaries contain ``name: value`` mappings
+where the value may be another namespace dictionary or
+a string specifying an import location. On accessing
+an namespace attribute an import will be performed::
+
+ >>> import mypkg
+ >>> mypkg.path
+ <ApiModule 'mypkg.path'>
+ >>> mypkg.path.Class1 # '_mypkg.somemodule' gets imported now
+ <class _mypkg.somemodule.Class1 at 0xb7d428fc>
+ >>> mypkg.path.clsattr # '_mypkg.othermodule' gets imported now
+ 4 # the value of _mypkg.othermodule.Class2.attr
+
+The ``mypkg.path`` namespace and its two entries are
+loaded when they are accessed. This means:
+
+* lazy loading - only what is actually needed is ever loaded
+
+* only the root "mypkg" ever needs to be imported to get
+ access to the complete functionality.
+
+* the underlying modules are also accessible, for example::
+
+ from mypkg.sub import Class1
+
+
+Including apipkg in your package
+--------------------------------------
+
+If you don't want to add an ``apipkg`` dependency to your package you
+can copy the `apipkg.py`_ file somewhere to your own package,
+for example ``_mypkg/apipkg.py`` in the above example. You
+then import the ``initpkg`` function from that new place and
+are good to go.
+
+.. _`small pure python module`:
+.. _`apipkg.py`: http://bitbucket.org/hpk42/apipkg/src/tip/apipkg.py
+
+Feedback?
+-----------------------
+
+If you have questions you are welcome to
+
+* join the #pylib channel on irc.freenode.net
+* subscribe to the http://codespeak.net/mailman/listinfo/py-dev list.
+* create an issue on http://bitbucket.org/hpk42/apipkg/issues
+
+have fun,
+holger krekel
+
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/RECORD b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/RECORD
new file mode 100644
index 00000000000..dc72959dfe0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/RECORD
@@ -0,0 +1,9 @@
+apipkg.py,sha256=BNnv_qvq8zZvku-uudoqgp3XTNFbwsNUmtzOKrVI7X0,6420
+apipkg-1.4.dist-info/top_level.txt,sha256=3TGS6nmN7kjxhUK4LpPCB3QkQI34QYGrT0ZQGWajoZ8,7
+apipkg-1.4.dist-info/METADATA,sha256=Fk_8BrHyXE--kvB3_ZBKgwvPaKusAZUjchH-kpB63Hs,3491
+apipkg-1.4.dist-info/DESCRIPTION.rst,sha256=RkMQqk5ljhGy0DiZkR_nbpjqvwCIhuIEHsyvkn3O96k,2803
+apipkg-1.4.dist-info/metadata.json,sha256=GdshYrA_7gAII3E3EQMH-31BHzU-klTZ6bPQzlDmuy4,779
+apipkg-1.4.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110
+apipkg-1.4.dist-info/RECORD,,
+apipkg-1.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+__pycache__/apipkg.cpython-35.pyc,,
diff --git a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/WHEEL b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/WHEEL
index 9dff69d8610..9dff69d8610 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/WHEEL
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/WHEEL
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/metadata.json b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/metadata.json
new file mode 100644
index 00000000000..05609b99373
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/metadata.json
@@ -0,0 +1 @@
+{"license": "MIT License", "name": "apipkg", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "apipkg: namespace control and lazy-import mechanism", "platform": "unix", "version": "1.4", "extensions": {"python.details": {"project_urls": {"Home": "http://bitbucket.org/hpk42/apipkg"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "holger at merlinux.eu", "name": "holger krekel"}]}}, "classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Topic :: Software Development :: Libraries", "Programming Language :: Python"]} \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/top_level.txt b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/top_level.txt
new file mode 100644
index 00000000000..e2221c8f9e9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/top_level.txt
@@ -0,0 +1 @@
+apipkg
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg.py
new file mode 100644
index 00000000000..9d56e0bcbae
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/apipkg.py
@@ -0,0 +1,205 @@
+"""
+apipkg: control the exported namespace of a python package.
+
+see http://pypi.python.org/pypi/apipkg
+
+(c) holger krekel, 2009 - MIT license
+"""
+import os
+import sys
+from types import ModuleType
+
+
+__version__ = '1.4'
+
+
+def _py_abspath(path):
+ """
+ special version of abspath
+ that will leave paths from jython jars alone
+ """
+ if path.startswith('__pyclasspath__'):
+
+ return path
+ else:
+ return os.path.abspath(path)
+
+
+def distribution_version(name):
+ """try to get the version of the named distribution,
+ returs None on failure"""
+ from pkg_resources import get_distribution, DistributionNotFound
+ try:
+ dist = get_distribution(name)
+ except DistributionNotFound:
+ pass
+ else:
+ return dist.version
+
+
+def initpkg(pkgname, exportdefs, attr=dict(), eager=False):
+ """ initialize given package from the export definitions. """
+ oldmod = sys.modules.get(pkgname)
+ d = {}
+ f = getattr(oldmod, '__file__', None)
+ if f:
+ f = _py_abspath(f)
+ d['__file__'] = f
+ if hasattr(oldmod, '__version__'):
+ d['__version__'] = oldmod.__version__
+ if hasattr(oldmod, '__loader__'):
+ d['__loader__'] = oldmod.__loader__
+ if hasattr(oldmod, '__path__'):
+ d['__path__'] = [_py_abspath(p) for p in oldmod.__path__]
+ if '__doc__' not in exportdefs and getattr(oldmod, '__doc__', None):
+ d['__doc__'] = oldmod.__doc__
+ d.update(attr)
+ if hasattr(oldmod, "__dict__"):
+ oldmod.__dict__.update(d)
+ mod = ApiModule(pkgname, exportdefs, implprefix=pkgname, attr=d)
+ sys.modules[pkgname] = mod
+ # eagerload in bypthon to avoid their monkeypatching breaking packages
+ if 'bpython' in sys.modules or eager:
+ for module in sys.modules.values():
+ if isinstance(module, ApiModule):
+ module.__dict__
+
+
+def importobj(modpath, attrname):
+ module = __import__(modpath, None, None, ['__doc__'])
+ if not attrname:
+ return module
+
+ retval = module
+ names = attrname.split(".")
+ for x in names:
+ retval = getattr(retval, x)
+ return retval
+
+
+class ApiModule(ModuleType):
+ def __docget(self):
+ try:
+ return self.__doc
+ except AttributeError:
+ if '__doc__' in self.__map__:
+ return self.__makeattr('__doc__')
+
+ def __docset(self, value):
+ self.__doc = value
+ __doc__ = property(__docget, __docset)
+
+ def __init__(self, name, importspec, implprefix=None, attr=None):
+ self.__name__ = name
+ self.__all__ = [x for x in importspec if x != '__onfirstaccess__']
+ self.__map__ = {}
+ self.__implprefix__ = implprefix or name
+ if attr:
+ for name, val in attr.items():
+ # print "setting", self.__name__, name, val
+ setattr(self, name, val)
+ for name, importspec in importspec.items():
+ if isinstance(importspec, dict):
+ subname = '%s.%s' % (self.__name__, name)
+ apimod = ApiModule(subname, importspec, implprefix)
+ sys.modules[subname] = apimod
+ setattr(self, name, apimod)
+ else:
+ parts = importspec.split(':')
+ modpath = parts.pop(0)
+ attrname = parts and parts[0] or ""
+ if modpath[0] == '.':
+ modpath = implprefix + modpath
+
+ if not attrname:
+ subname = '%s.%s' % (self.__name__, name)
+ apimod = AliasModule(subname, modpath)
+ sys.modules[subname] = apimod
+ if '.' not in name:
+ setattr(self, name, apimod)
+ else:
+ self.__map__[name] = (modpath, attrname)
+
+ def __repr__(self):
+ l = []
+ if hasattr(self, '__version__'):
+ l.append("version=" + repr(self.__version__))
+ if hasattr(self, '__file__'):
+ l.append('from ' + repr(self.__file__))
+ if l:
+ return '<ApiModule %r %s>' % (self.__name__, " ".join(l))
+ return '<ApiModule %r>' % (self.__name__,)
+
+ def __makeattr(self, name):
+ """lazily compute value for name or raise AttributeError if unknown."""
+ # print "makeattr", self.__name__, name
+ target = None
+ if '__onfirstaccess__' in self.__map__:
+ target = self.__map__.pop('__onfirstaccess__')
+ importobj(*target)()
+ try:
+ modpath, attrname = self.__map__[name]
+ except KeyError:
+ if target is not None and name != '__onfirstaccess__':
+ # retry, onfirstaccess might have set attrs
+ return getattr(self, name)
+ raise AttributeError(name)
+ else:
+ result = importobj(modpath, attrname)
+ setattr(self, name, result)
+ try:
+ del self.__map__[name]
+ except KeyError:
+ pass # in a recursive-import situation a double-del can happen
+ return result
+
+ __getattr__ = __makeattr
+
+ @property
+ def __dict__(self):
+ # force all the content of the module
+ # to be loaded when __dict__ is read
+ dictdescr = ModuleType.__dict__['__dict__']
+ dict = dictdescr.__get__(self)
+ if dict is not None:
+ hasattr(self, 'some')
+ for name in self.__all__:
+ try:
+ self.__makeattr(name)
+ except AttributeError:
+ pass
+ return dict
+
+
+def AliasModule(modname, modpath, attrname=None):
+ mod = []
+
+ def getmod():
+ if not mod:
+ x = importobj(modpath, None)
+ if attrname is not None:
+ x = getattr(x, attrname)
+ mod.append(x)
+ return mod[0]
+
+ class AliasModule(ModuleType):
+
+ def __repr__(self):
+ x = modpath
+ if attrname:
+ x += "." + attrname
+ return '<AliasModule %r for %r>' % (modname, x)
+
+ def __getattribute__(self, name):
+ try:
+ return getattr(getmod(), name)
+ except ImportError:
+ return None
+
+ def __setattr__(self, name, value):
+ setattr(getmod(), name, value)
+
+ def __delattr__(self, name):
+ delattr(getmod(), name)
+
+ return AliasModule(str(modname))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/DESCRIPTION.rst b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/DESCRIPTION.rst
new file mode 100644
index 00000000000..6d59bc222cc
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/DESCRIPTION.rst
@@ -0,0 +1,53 @@
+iniconfig: brain-dead simple parsing of ini files
+=======================================================
+
+iniconfig is a small and simple INI-file parser module
+having a unique set of features:
+
+* tested against Python2.4 across to Python3.2, Jython, PyPy
+* maintains order of sections and entries
+* supports multi-line values with or without line-continuations
+* supports "#" comments everywhere
+* raises errors with proper line-numbers
+* no bells and whistles like automatic substitutions
+* iniconfig raises an Error if two sections have the same name.
+
+If you encounter issues or have feature wishes please report them to:
+
+ http://github.org/RonnyPfannschmidt/iniconfig/issues
+
+Basic Example
+===================================
+
+If you have an ini file like this::
+
+ # content of example.ini
+ [section1] # comment
+ name1=value1 # comment
+ name1b=value1,value2 # comment
+
+ [section2]
+ name2=
+ line1
+ line2
+
+then you can do::
+
+ >>> import iniconfig
+ >>> ini = iniconfig.IniConfig("example.ini")
+ >>> ini['section1']['name1'] # raises KeyError if not exists
+ 'value1'
+ >>> ini.get('section1', 'name1b', [], lambda x: x.split(","))
+ ['value1', 'value2']
+ >>> ini.get('section1', 'notexist', [], lambda x: x.split(","))
+ []
+ >>> [x.name for x in list(ini)]
+ ['section1', 'section2']
+ >>> list(list(ini)[0].items())
+ [('name1', 'value1'), ('name1b', 'value1,value2')]
+ >>> 'section1' in ini
+ True
+ >>> 'inexistendsection' in ini
+ False
+
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/INSTALLER b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/INSTALLER
new file mode 100644
index 00000000000..a1b589e38a3
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/METADATA b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/METADATA
new file mode 100644
index 00000000000..79ea62dc34c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/METADATA
@@ -0,0 +1,78 @@
+Metadata-Version: 2.0
+Name: iniconfig
+Version: 1.0.0
+Summary: iniconfig: brain-dead simple config-ini parsing
+Home-page: http://github.com/RonnyPfannschmidt/iniconfig
+Author: Ronny Pfannschmidt, Holger Krekel
+Author-email: opensource@ronnypfannschmidt.de, holger.krekel@gmail.com
+License: MIT License
+Platform: unix
+Platform: linux
+Platform: osx
+Platform: cygwin
+Platform: win32
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Utilities
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+
+iniconfig: brain-dead simple parsing of ini files
+=======================================================
+
+iniconfig is a small and simple INI-file parser module
+having a unique set of features:
+
+* tested against Python2.4 across to Python3.2, Jython, PyPy
+* maintains order of sections and entries
+* supports multi-line values with or without line-continuations
+* supports "#" comments everywhere
+* raises errors with proper line-numbers
+* no bells and whistles like automatic substitutions
+* iniconfig raises an Error if two sections have the same name.
+
+If you encounter issues or have feature wishes please report them to:
+
+ http://github.org/RonnyPfannschmidt/iniconfig/issues
+
+Basic Example
+===================================
+
+If you have an ini file like this::
+
+ # content of example.ini
+ [section1] # comment
+ name1=value1 # comment
+ name1b=value1,value2 # comment
+
+ [section2]
+ name2=
+ line1
+ line2
+
+then you can do::
+
+ >>> import iniconfig
+ >>> ini = iniconfig.IniConfig("example.ini")
+ >>> ini['section1']['name1'] # raises KeyError if not exists
+ 'value1'
+ >>> ini.get('section1', 'name1b', [], lambda x: x.split(","))
+ ['value1', 'value2']
+ >>> ini.get('section1', 'notexist', [], lambda x: x.split(","))
+ []
+ >>> [x.name for x in list(ini)]
+ ['section1', 'section2']
+ >>> list(list(ini)[0].items())
+ [('name1', 'value1'), ('name1b', 'value1,value2')]
+ >>> 'section1' in ini
+ True
+ >>> 'inexistendsection' in ini
+ False
+
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/RECORD b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/RECORD
new file mode 100644
index 00000000000..ec2f5e17487
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/RECORD
@@ -0,0 +1,9 @@
+iniconfig.py,sha256=-pBe5AF_6aAwo1CxJQ8i_zJq6ejc6IxHta7qk2tNJhY,5208
+iniconfig-1.0.0.dist-info/DESCRIPTION.rst,sha256=BDLMwWqfjpwZ5yqXRvz1x6bf8Dnt_pZhElekAwtL19o,1522
+iniconfig-1.0.0.dist-info/METADATA,sha256=bb2T8WUSDXXiUVxZ4WXhbffq6stikMTlB1jyrPbLfyU,2405
+iniconfig-1.0.0.dist-info/RECORD,,
+iniconfig-1.0.0.dist-info/WHEEL,sha256=3XK1Z4AI42GuJXciCpiHMOkbehxRV8QDBW8IU41k3ZU,96
+iniconfig-1.0.0.dist-info/metadata.json,sha256=UYYwW0p815nU4qz8Iq1gGqIYaAcsCyGju3jXvTOyXSI,950
+iniconfig-1.0.0.dist-info/top_level.txt,sha256=7KfM0fugdlToj9UW7enKXk2HYALQD8qHiyKtjhSzgN8,10
+iniconfig-1.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+__pycache__/iniconfig.cpython-35.pyc,,
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/WHEEL b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/WHEEL
new file mode 100644
index 00000000000..15b96c99cac
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.30.0.a0)
+Root-Is-Purelib: true
+Tag: cp35-none-any
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/metadata.json b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/metadata.json
new file mode 100644
index 00000000000..084daa6c06b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Topic :: Software Development :: Libraries", "Topic :: Utilities", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3"], "extensions": {"python.details": {"contacts": [{"email": "opensource@ronnypfannschmidt.de, holger.krekel@gmail.com", "name": "Ronny Pfannschmidt, Holger Krekel", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/RonnyPfannschmidt/iniconfig"}}}, "generator": "bdist_wheel (0.30.0.a0)", "license": "MIT License", "metadata_version": "2.0", "name": "iniconfig", "platform": "unix", "summary": "iniconfig: brain-dead simple config-ini parsing", "version": "1.0.0"} \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/top_level.txt b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/top_level.txt
new file mode 100644
index 00000000000..9dda53692d2
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig-1.0.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+iniconfig
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig.py
new file mode 100644
index 00000000000..6ad9eaf868b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_vendored_packages/iniconfig.py
@@ -0,0 +1,165 @@
+""" brain-dead simple parser for ini-style files.
+(C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed
+"""
+__all__ = ['IniConfig', 'ParseError']
+
+COMMENTCHARS = "#;"
+
+
+class ParseError(Exception):
+ def __init__(self, path, lineno, msg):
+ Exception.__init__(self, path, lineno, msg)
+ self.path = path
+ self.lineno = lineno
+ self.msg = msg
+
+ def __str__(self):
+ return "%s:%s: %s" % (self.path, self.lineno+1, self.msg)
+
+
+class SectionWrapper(object):
+ def __init__(self, config, name):
+ self.config = config
+ self.name = name
+
+ def lineof(self, name):
+ return self.config.lineof(self.name, name)
+
+ def get(self, key, default=None, convert=str):
+ return self.config.get(self.name, key,
+ convert=convert, default=default)
+
+ def __getitem__(self, key):
+ return self.config.sections[self.name][key]
+
+ def __iter__(self):
+ section = self.config.sections.get(self.name, [])
+
+ def lineof(key):
+ return self.config.lineof(self.name, key)
+ for name in sorted(section, key=lineof):
+ yield name
+
+ def items(self):
+ for name in self:
+ yield name, self[name]
+
+
+class IniConfig(object):
+ def __init__(self, path, data=None):
+ self.path = str(path) # convenience
+ if data is None:
+ f = open(self.path)
+ try:
+ tokens = self._parse(iter(f))
+ finally:
+ f.close()
+ else:
+ tokens = self._parse(data.splitlines(True))
+
+ self._sources = {}
+ self.sections = {}
+
+ for lineno, section, name, value in tokens:
+ if section is None:
+ self._raise(lineno, 'no section header defined')
+ self._sources[section, name] = lineno
+ if name is None:
+ if section in self.sections:
+ self._raise(lineno, 'duplicate section %r' % (section, ))
+ self.sections[section] = {}
+ else:
+ if name in self.sections[section]:
+ self._raise(lineno, 'duplicate name %r' % (name, ))
+ self.sections[section][name] = value
+
+ def _raise(self, lineno, msg):
+ raise ParseError(self.path, lineno, msg)
+
+ def _parse(self, line_iter):
+ result = []
+ section = None
+ for lineno, line in enumerate(line_iter):
+ name, data = self._parseline(line, lineno)
+ # new value
+ if name is not None and data is not None:
+ result.append((lineno, section, name, data))
+ # new section
+ elif name is not None and data is None:
+ if not name:
+ self._raise(lineno, 'empty section name')
+ section = name
+ result.append((lineno, section, None, None))
+ # continuation
+ elif name is None and data is not None:
+ if not result:
+ self._raise(lineno, 'unexpected value continuation')
+ last = result.pop()
+ last_name, last_data = last[-2:]
+ if last_name is None:
+ self._raise(lineno, 'unexpected value continuation')
+
+ if last_data:
+ data = '%s\n%s' % (last_data, data)
+ result.append(last[:-1] + (data,))
+ return result
+
+ def _parseline(self, line, lineno):
+ # blank lines
+ if iscommentline(line):
+ line = ""
+ else:
+ line = line.rstrip()
+ if not line:
+ return None, None
+ # section
+ if line[0] == '[':
+ realline = line
+ for c in COMMENTCHARS:
+ line = line.split(c)[0].rstrip()
+ if line[-1] == "]":
+ return line[1:-1], None
+ return None, realline.strip()
+ # value
+ elif not line[0].isspace():
+ try:
+ name, value = line.split('=', 1)
+ if ":" in name:
+ raise ValueError()
+ except ValueError:
+ try:
+ name, value = line.split(":", 1)
+ except ValueError:
+ self._raise(lineno, 'unexpected line: %r' % line)
+ return name.strip(), value.strip()
+ # continuation
+ else:
+ return None, line.strip()
+
+ def lineof(self, section, name=None):
+ lineno = self._sources.get((section, name))
+ if lineno is not None:
+ return lineno + 1
+
+ def get(self, section, name, default=None, convert=str):
+ try:
+ return convert(self.sections[section][name])
+ except KeyError:
+ return default
+
+ def __getitem__(self, name):
+ if name not in self.sections:
+ raise KeyError(name)
+ return SectionWrapper(self, name)
+
+ def __iter__(self):
+ for name in sorted(self.sections, key=self.lineof):
+ yield SectionWrapper(self, name)
+
+ def __contains__(self, arg):
+ return arg in self.sections
+
+
+def iscommentline(line):
+ c = line.lstrip()[:1]
+ return c in COMMENTCHARS
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/py/_xmlgen.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/_xmlgen.py
new file mode 100644
index 00000000000..1c835458843
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/_xmlgen.py
@@ -0,0 +1,255 @@
+"""
+module for generating and serializing xml and html structures
+by using simple python objects.
+
+(c) holger krekel, holger at merlinux eu. 2009
+"""
+import sys, re
+
+if sys.version_info >= (3,0):
+ def u(s):
+ return s
+ def unicode(x, errors=None):
+ if hasattr(x, '__unicode__'):
+ return x.__unicode__()
+ return str(x)
+else:
+ def u(s):
+ return unicode(s)
+ unicode = unicode
+
+
+class NamespaceMetaclass(type):
+ def __getattr__(self, name):
+ if name[:1] == '_':
+ raise AttributeError(name)
+ if self == Namespace:
+ raise ValueError("Namespace class is abstract")
+ tagspec = self.__tagspec__
+ if tagspec is not None and name not in tagspec:
+ raise AttributeError(name)
+ classattr = {}
+ if self.__stickyname__:
+ classattr['xmlname'] = name
+ cls = type(name, (self.__tagclass__,), classattr)
+ setattr(self, name, cls)
+ return cls
+
+class Tag(list):
+ class Attr(object):
+ def __init__(self, **kwargs):
+ self.__dict__.update(kwargs)
+
+ def __init__(self, *args, **kwargs):
+ super(Tag, self).__init__(args)
+ self.attr = self.Attr(**kwargs)
+
+ def __unicode__(self):
+ return self.unicode(indent=0)
+ __str__ = __unicode__
+
+ def unicode(self, indent=2):
+ l = []
+ SimpleUnicodeVisitor(l.append, indent).visit(self)
+ return u("").join(l)
+
+ def __repr__(self):
+ name = self.__class__.__name__
+ return "<%r tag object %d>" % (name, id(self))
+
+Namespace = NamespaceMetaclass('Namespace', (object, ), {
+ '__tagspec__': None,
+ '__tagclass__': Tag,
+ '__stickyname__': False,
+})
+
+class HtmlTag(Tag):
+ def unicode(self, indent=2):
+ l = []
+ HtmlVisitor(l.append, indent, shortempty=False).visit(self)
+ return u("").join(l)
+
+# exported plain html namespace
+class html(Namespace):
+ __tagclass__ = HtmlTag
+ __stickyname__ = True
+ __tagspec__ = dict([(x,1) for x in (
+ 'a,abbr,acronym,address,applet,area,article,aside,audio,b,'
+ 'base,basefont,bdi,bdo,big,blink,blockquote,body,br,button,'
+ 'canvas,caption,center,cite,code,col,colgroup,command,comment,'
+ 'datalist,dd,del,details,dfn,dir,div,dl,dt,em,embed,'
+ 'fieldset,figcaption,figure,footer,font,form,frame,frameset,h1,'
+ 'h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input,'
+ 'ins,isindex,kbd,keygen,label,legend,li,link,listing,map,mark,'
+ 'marquee,menu,meta,meter,multicol,nav,nobr,noembed,noframes,'
+ 'noscript,object,ol,optgroup,option,output,p,param,pre,progress,'
+ 'q,rp,rt,ruby,s,samp,script,section,select,small,source,span,'
+ 'strike,strong,style,sub,summary,sup,table,tbody,td,textarea,'
+ 'tfoot,th,thead,time,title,tr,track,tt,u,ul,xmp,var,video,wbr'
+ ).split(',') if x])
+
+ class Style(object):
+ def __init__(self, **kw):
+ for x, y in kw.items():
+ x = x.replace('_', '-')
+ setattr(self, x, y)
+
+
+class raw(object):
+ """just a box that can contain a unicode string that will be
+ included directly in the output"""
+ def __init__(self, uniobj):
+ self.uniobj = uniobj
+
+class SimpleUnicodeVisitor(object):
+ """ recursive visitor to write unicode. """
+ def __init__(self, write, indent=0, curindent=0, shortempty=True):
+ self.write = write
+ self.cache = {}
+ self.visited = {} # for detection of recursion
+ self.indent = indent
+ self.curindent = curindent
+ self.parents = []
+ self.shortempty = shortempty # short empty tags or not
+
+ def visit(self, node):
+ """ dispatcher on node's class/bases name. """
+ cls = node.__class__
+ try:
+ visitmethod = self.cache[cls]
+ except KeyError:
+ for subclass in cls.__mro__:
+ visitmethod = getattr(self, subclass.__name__, None)
+ if visitmethod is not None:
+ break
+ else:
+ visitmethod = self.__object
+ self.cache[cls] = visitmethod
+ visitmethod(node)
+
+ # the default fallback handler is marked private
+ # to avoid clashes with the tag name object
+ def __object(self, obj):
+ #self.write(obj)
+ self.write(escape(unicode(obj)))
+
+ def raw(self, obj):
+ self.write(obj.uniobj)
+
+ def list(self, obj):
+ assert id(obj) not in self.visited
+ self.visited[id(obj)] = 1
+ for elem in obj:
+ self.visit(elem)
+
+ def Tag(self, tag):
+ assert id(tag) not in self.visited
+ try:
+ tag.parent = self.parents[-1]
+ except IndexError:
+ tag.parent = None
+ self.visited[id(tag)] = 1
+ tagname = getattr(tag, 'xmlname', tag.__class__.__name__)
+ if self.curindent and not self._isinline(tagname):
+ self.write("\n" + u(' ') * self.curindent)
+ if tag:
+ self.curindent += self.indent
+ self.write(u('<%s%s>') % (tagname, self.attributes(tag)))
+ self.parents.append(tag)
+ for x in tag:
+ self.visit(x)
+ self.parents.pop()
+ self.write(u('</%s>') % tagname)
+ self.curindent -= self.indent
+ else:
+ nameattr = tagname+self.attributes(tag)
+ if self._issingleton(tagname):
+ self.write(u('<%s/>') % (nameattr,))
+ else:
+ self.write(u('<%s></%s>') % (nameattr, tagname))
+
+ def attributes(self, tag):
+ # serialize attributes
+ attrlist = dir(tag.attr)
+ attrlist.sort()
+ l = []
+ for name in attrlist:
+ res = self.repr_attribute(tag.attr, name)
+ if res is not None:
+ l.append(res)
+ l.extend(self.getstyle(tag))
+ return u("").join(l)
+
+ def repr_attribute(self, attrs, name):
+ if name[:2] != '__':
+ value = getattr(attrs, name)
+ if name.endswith('_'):
+ name = name[:-1]
+ if isinstance(value, raw):
+ insert = value.uniobj
+ else:
+ insert = escape(unicode(value))
+ return ' %s="%s"' % (name, insert)
+
+ def getstyle(self, tag):
+ """ return attribute list suitable for styling. """
+ try:
+ styledict = tag.style.__dict__
+ except AttributeError:
+ return []
+ else:
+ stylelist = [x+': ' + y for x,y in styledict.items()]
+ return [u(' style="%s"') % u('; ').join(stylelist)]
+
+ def _issingleton(self, tagname):
+ """can (and will) be overridden in subclasses"""
+ return self.shortempty
+
+ def _isinline(self, tagname):
+ """can (and will) be overridden in subclasses"""
+ return False
+
+class HtmlVisitor(SimpleUnicodeVisitor):
+
+ single = dict([(x, 1) for x in
+ ('br,img,area,param,col,hr,meta,link,base,'
+ 'input,frame').split(',')])
+ inline = dict([(x, 1) for x in
+ ('a abbr acronym b basefont bdo big br cite code dfn em font '
+ 'i img input kbd label q s samp select small span strike '
+ 'strong sub sup textarea tt u var'.split(' '))])
+
+ def repr_attribute(self, attrs, name):
+ if name == 'class_':
+ value = getattr(attrs, name)
+ if value is None:
+ return
+ return super(HtmlVisitor, self).repr_attribute(attrs, name)
+
+ def _issingleton(self, tagname):
+ return tagname in self.single
+
+ def _isinline(self, tagname):
+ return tagname in self.inline
+
+
+class _escape:
+ def __init__(self):
+ self.escape = {
+ u('"') : u('&quot;'), u('<') : u('&lt;'), u('>') : u('&gt;'),
+ u('&') : u('&amp;'), u("'") : u('&apos;'),
+ }
+ self.charef_rex = re.compile(u("|").join(self.escape.keys()))
+
+ def _replacer(self, match):
+ return self.escape[match.group(0)]
+
+ def __call__(self, ustring):
+ """ xml-escape the given unicode string. """
+ try:
+ ustring = unicode(ustring)
+ except UnicodeDecodeError:
+ ustring = unicode(ustring, 'utf-8', errors='replace')
+ return self.charef_rex.sub(self._replacer, ustring)
+
+escape = _escape()
diff --git a/tests/wpt/web-platform-tests/tools/py/py/test.py b/tests/wpt/web-platform-tests/tools/third_party/py/py/test.py
index aa5beb1789f..aa5beb1789f 100644
--- a/tests/wpt/web-platform-tests/tools/py/py/test.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/py/test.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/setup.cfg b/tests/wpt/web-platform-tests/tools/third_party/py/setup.cfg
new file mode 100644
index 00000000000..5f25c2febfd
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/setup.cfg
@@ -0,0 +1,8 @@
+[wheel]
+universal = 1
+
+[metadata]
+license_file = LICENSE
+
+[devpi:upload]
+formats=sdist.tgz,bdist_wheel
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/setup.py b/tests/wpt/web-platform-tests/tools/third_party/py/setup.py
new file mode 100644
index 00000000000..959323b0c75
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/setup.py
@@ -0,0 +1,53 @@
+import os
+import sys
+
+from setuptools import setup, find_packages
+
+
+def get_version():
+ p = os.path.join(os.path.dirname(
+ os.path.abspath(__file__)), "py", "__init__.py")
+ with open(p) as f:
+ for line in f.readlines():
+ if "__version__" in line:
+ return line.strip().split("=")[-1].strip(" '")
+ raise ValueError("could not read version")
+
+
+def main():
+ setup(
+ name='py',
+ description='library with cross-python path, ini-parsing, io, code, log facilities',
+ long_description=open('README.rst').read(),
+ version=get_version(),
+ url='http://py.readthedocs.io/',
+ license='MIT license',
+ platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
+ python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
+ author='holger krekel, Ronny Pfannschmidt, Benjamin Peterson and others',
+ author_email='pytest-dev@python.org',
+ classifiers=['Development Status :: 6 - Mature',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: MIT License',
+ 'Operating System :: POSIX',
+ 'Operating System :: Microsoft :: Windows',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Topic :: Software Development :: Testing',
+ 'Topic :: Software Development :: Libraries',
+ 'Topic :: Utilities',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: Implementation :: CPython',
+ 'Programming Language :: Python :: Implementation :: PyPy',
+ ],
+ packages=find_packages(exclude=['tasks', 'testing']),
+ zip_safe=False,
+ )
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/tasks/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/tasks/__init__.py
new file mode 100644
index 00000000000..5d74b649e11
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/tasks/__init__.py
@@ -0,0 +1,12 @@
+"""
+Invoke tasks to help with pytest development and release process.
+"""
+
+import invoke
+
+from . import vendoring
+
+
+ns = invoke.Collection(
+ vendoring
+)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/tasks/vendoring.py b/tests/wpt/web-platform-tests/tools/third_party/py/tasks/vendoring.py
new file mode 100644
index 00000000000..fbc171bc3e0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/tasks/vendoring.py
@@ -0,0 +1,23 @@
+from __future__ import absolute_import, print_function
+import py
+import invoke
+
+VENDOR_TARGET = py.path.local("py/_vendored_packages")
+GOOD_FILES = 'README.md', '__init__.py'
+
+@invoke.task()
+def remove_libs(ctx):
+ print("removing vendored libs")
+ for path in VENDOR_TARGET.listdir():
+ if path.basename not in GOOD_FILES:
+ print(" ", path)
+ path.remove()
+
+@invoke.task(pre=[remove_libs])
+def update_libs(ctx):
+ print("installing libs")
+ ctx.run("pip install -t {target} apipkg iniconfig".format(target=VENDOR_TARGET))
+ ctx.run("git add {target}".format(target=VENDOR_TARGET))
+ print("Please commit to finish the update after running the tests:")
+ print()
+ print(' git commit -am "Updated vendored libs"')
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_assertion.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_assertion.py
new file mode 100644
index 00000000000..e2a7f903998
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_assertion.py
@@ -0,0 +1,305 @@
+import pytest, py
+import re
+
+def exvalue():
+ import sys
+ return sys.exc_info()[1]
+
+def f():
+ return 2
+
+def test_assert():
+ try:
+ assert f() == 3
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert s.startswith('assert 2 == 3\n')
+
+
+def test_assert_within_finally():
+ excinfo = py.test.raises(ZeroDivisionError, """
+ try:
+ 1/0
+ finally:
+ i = 42
+ """)
+ s = excinfo.exconly()
+ assert re.search("ZeroDivisionError:.*division", s) is not None
+
+
+def test_assert_multiline_1():
+ try:
+ assert (f() ==
+ 3)
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert s.startswith('assert 2 == 3\n')
+
+def test_assert_multiline_2():
+ try:
+ assert (f() == (4,
+ 3)[-1])
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert s.startswith('assert 2 ==')
+
+def test_in():
+ try:
+ assert "hi" in [1, 2]
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert s.startswith("assert 'hi' in")
+
+def test_is():
+ try:
+ assert 1 is 2
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert s.startswith("assert 1 is 2")
+
+
+def test_attrib():
+ class Foo(object):
+ b = 1
+ i = Foo()
+ try:
+ assert i.b == 2
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert s.startswith("assert 1 == 2")
+
+def test_attrib_inst():
+ class Foo(object):
+ b = 1
+ try:
+ assert Foo().b == 2
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert s.startswith("assert 1 == 2")
+
+def test_len():
+ l = list(range(42))
+ try:
+ assert len(l) == 100
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert s.startswith("assert 42 == 100")
+ assert "where 42 = len([" in s
+
+
+def test_assert_keyword_arg():
+ def f(x=3):
+ return False
+ try:
+ assert f(x=5)
+ except AssertionError:
+ e = exvalue()
+ assert "x=5" in str(e)
+
+# These tests should both fail, but should fail nicely...
+class WeirdRepr:
+ def __repr__(self):
+ return '<WeirdRepr\nsecond line>'
+
+def bug_test_assert_repr():
+ v = WeirdRepr()
+ try:
+ assert v == 1
+ except AssertionError:
+ e = exvalue()
+ assert str(e).find('WeirdRepr') != -1
+ assert str(e).find('second line') != -1
+ assert 0
+
+def test_assert_non_string():
+ try:
+ assert 0, ['list']
+ except AssertionError:
+ e = exvalue()
+ assert str(e).find("list") != -1
+
+def test_assert_implicit_multiline():
+ try:
+ x = [1,2,3]
+ assert x != [1,
+ 2, 3]
+ except AssertionError:
+ e = exvalue()
+ assert str(e).find('assert [1, 2, 3] !=') != -1
+
+@py.test.mark.xfail(py.test.__version__[0] != "2",
+ reason="broken on modern pytest",
+ run=False
+)
+def test_assert_with_brokenrepr_arg():
+ class BrokenRepr:
+ def __repr__(self): 0 / 0
+ e = AssertionError(BrokenRepr())
+ if e.msg.find("broken __repr__") == -1:
+ py.test.fail("broken __repr__ not handle correctly")
+
+def test_multiple_statements_per_line():
+ try:
+ a = 1; assert a == 2
+ except AssertionError:
+ e = exvalue()
+ assert "assert 1 == 2" in str(e)
+
+def test_power():
+ try:
+ assert 2**3 == 7
+ except AssertionError:
+ e = exvalue()
+ assert "assert (2 ** 3) == 7" in str(e)
+
+
+class TestView:
+
+ def setup_class(cls):
+ cls.View = py.test.importorskip("py._code._assertionold").View
+
+ def test_class_dispatch(self):
+ ### Use a custom class hierarchy with existing instances
+
+ class Picklable(self.View):
+ pass
+
+ class Simple(Picklable):
+ __view__ = object
+ def pickle(self):
+ return repr(self.__obj__)
+
+ class Seq(Picklable):
+ __view__ = list, tuple, dict
+ def pickle(self):
+ return ';'.join(
+ [Picklable(item).pickle() for item in self.__obj__])
+
+ class Dict(Seq):
+ __view__ = dict
+ def pickle(self):
+ return Seq.pickle(self) + '!' + Seq(self.values()).pickle()
+
+ assert Picklable(123).pickle() == '123'
+ assert Picklable([1,[2,3],4]).pickle() == '1;2;3;4'
+ assert Picklable({1:2}).pickle() == '1!2'
+
+ def test_viewtype_class_hierarchy(self):
+ # Use a custom class hierarchy based on attributes of existing instances
+ class Operation:
+ "Existing class that I don't want to change."
+ def __init__(self, opname, *args):
+ self.opname = opname
+ self.args = args
+
+ existing = [Operation('+', 4, 5),
+ Operation('getitem', '', 'join'),
+ Operation('setattr', 'x', 'y', 3),
+ Operation('-', 12, 1)]
+
+ class PyOp(self.View):
+ def __viewkey__(self):
+ return self.opname
+ def generate(self):
+ return '%s(%s)' % (self.opname, ', '.join(map(repr, self.args)))
+
+ class PyBinaryOp(PyOp):
+ __view__ = ('+', '-', '*', '/')
+ def generate(self):
+ return '%s %s %s' % (self.args[0], self.opname, self.args[1])
+
+ codelines = [PyOp(op).generate() for op in existing]
+ assert codelines == ["4 + 5", "getitem('', 'join')",
+ "setattr('x', 'y', 3)", "12 - 1"]
+
+def test_underscore_api():
+ py.code._AssertionError
+ py.code._reinterpret_old # used by pypy
+ py.code._reinterpret
+
+def test_assert_customizable_reprcompare(monkeypatch):
+ util = pytest.importorskip("_pytest.assertion.util")
+ monkeypatch.setattr(util, '_reprcompare', lambda *args: 'hello')
+ try:
+ assert 3 == 4
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert "hello" in s
+
+def test_assert_long_source_1():
+ try:
+ assert len == [
+ (None, ['somet text', 'more text']),
+ ]
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert 're-run' not in s
+ assert 'somet text' in s
+
+def test_assert_long_source_2():
+ try:
+ assert(len == [
+ (None, ['somet text', 'more text']),
+ ])
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert 're-run' not in s
+ assert 'somet text' in s
+
+def test_assert_raise_alias(testdir):
+ testdir.makepyfile("""
+ import sys
+ EX = AssertionError
+ def test_hello():
+ raise EX("hello"
+ "multi"
+ "line")
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*def test_hello*",
+ "*raise EX*",
+ "*1 failed*",
+ ])
+
+@py.test.mark.xfail(py.test.__version__[0] != "2",
+ reason="broken on modern pytest",
+ run=False)
+def test_assert_raise_subclass():
+ class SomeEx(AssertionError):
+ def __init__(self, *args):
+ super(SomeEx, self).__init__()
+ try:
+ raise SomeEx("hello")
+ except AssertionError as e:
+ s = str(e)
+ assert 're-run' not in s
+ assert 'could not determine' in s
+
+def test_assert_raises_in_nonzero_of_object_pytest_issue10():
+ class A(object):
+ def __nonzero__(self):
+ raise ValueError(42)
+ def __lt__(self, other):
+ return A()
+ def __repr__(self):
+ return "<MY42 object>"
+ def myany(x):
+ return True
+ try:
+ assert not(myany(A() < 0))
+ except AssertionError:
+ e = exvalue()
+ s = str(e)
+ assert "<MY42 object> < 0" in s
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/code/test_code.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_code.py
index 28ec628b00d..28ec628b00d 100644
--- a/tests/wpt/web-platform-tests/tools/py/testing/code/test_code.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_code.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_excinfo.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_excinfo.py
new file mode 100644
index 00000000000..c148ab8cfbd
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_excinfo.py
@@ -0,0 +1,956 @@
+# -*- coding: utf-8 -*-
+
+import py
+import pytest
+import sys
+from test_source import astonly
+
+from py._code.code import FormattedExcinfo, ReprExceptionInfo
+queue = py.builtin._tryimport('queue', 'Queue')
+
+failsonjython = py.test.mark.xfail("sys.platform.startswith('java')")
+
+try:
+ import importlib
+except ImportError:
+ invalidate_import_caches = None
+else:
+ invalidate_import_caches = getattr(importlib, "invalidate_caches", None)
+
+
+pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
+
+broken_on_modern_pytest = pytest.mark.xfail(
+ pytest_version_info[0] != 2,
+ reason="this test hasn't been fixed after moving py.code into pytest",
+ run=False
+ )
+
+
+class TWMock:
+ def __init__(self):
+ self.lines = []
+
+ def sep(self, sep, line=None):
+ self.lines.append((sep, line))
+
+ def line(self, line, **kw):
+ self.lines.append(line)
+
+ def markup(self, text, **kw):
+ return text
+
+ fullwidth = 80
+
+
+def test_excinfo_simple():
+ try:
+ raise ValueError
+ except ValueError:
+ info = py.code.ExceptionInfo()
+ assert info.type == ValueError
+
+
+def test_excinfo_getstatement():
+ def g():
+ raise ValueError
+
+ def f():
+ g()
+ try:
+ f()
+ except ValueError:
+ excinfo = py.code.ExceptionInfo()
+ linenumbers = [
+ py.code.getrawcode(f).co_firstlineno-1+3,
+ py.code.getrawcode(f).co_firstlineno-1+1,
+ py.code.getrawcode(g).co_firstlineno-1+1,
+ ]
+ l = list(excinfo.traceback)
+ foundlinenumbers = [x.lineno for x in l]
+ assert foundlinenumbers == linenumbers
+ #for x in info:
+ # print "%s:%d %s" %(x.path.relto(root), x.lineno, x.statement)
+ #xxx
+
+# testchain for getentries test below
+def f():
+ #
+ raise ValueError
+ #
+def g():
+ #
+ __tracebackhide__ = True
+ f()
+ #
+def h():
+ #
+ g()
+ #
+
+class TestTraceback_f_g_h:
+ def setup_method(self, method):
+ try:
+ h()
+ except ValueError:
+ self.excinfo = py.code.ExceptionInfo()
+
+ def test_traceback_entries(self):
+ tb = self.excinfo.traceback
+ entries = list(tb)
+ assert len(tb) == 4 # maybe fragile test
+ assert len(entries) == 4 # maybe fragile test
+ names = ['f', 'g', 'h']
+ for entry in entries:
+ try:
+ names.remove(entry.frame.code.name)
+ except ValueError:
+ pass
+ assert not names
+
+ def test_traceback_entry_getsource(self):
+ tb = self.excinfo.traceback
+ s = str(tb[-1].getsource())
+ assert s.startswith("def f():")
+ assert s.endswith("raise ValueError")
+
+ @astonly
+ @failsonjython
+ def test_traceback_entry_getsource_in_construct(self):
+ source = py.code.Source("""\
+ def xyz():
+ try:
+ raise ValueError
+ except somenoname:
+ pass
+ xyz()
+ """)
+ try:
+ exec (source.compile())
+ except NameError:
+ tb = py.code.ExceptionInfo().traceback
+ print (tb[-1].getsource())
+ s = str(tb[-1].getsource())
+ assert s.startswith("def xyz():\n try:")
+ assert s.strip().endswith("except somenoname:")
+
+ def test_traceback_cut(self):
+ co = py.code.Code(f)
+ path, firstlineno = co.path, co.firstlineno
+ traceback = self.excinfo.traceback
+ newtraceback = traceback.cut(path=path, firstlineno=firstlineno)
+ assert len(newtraceback) == 1
+ newtraceback = traceback.cut(path=path, lineno=firstlineno+2)
+ assert len(newtraceback) == 1
+
+ def test_traceback_cut_excludepath(self, testdir):
+ p = testdir.makepyfile("def f(): raise ValueError")
+ excinfo = py.test.raises(ValueError, "p.pyimport().f()")
+ basedir = py.path.local(py.test.__file__).dirpath()
+ newtraceback = excinfo.traceback.cut(excludepath=basedir)
+ for x in newtraceback:
+ if hasattr(x, 'path'):
+ assert not py.path.local(x.path).relto(basedir)
+ assert newtraceback[-1].frame.code.path == p
+
+ def test_traceback_filter(self):
+ traceback = self.excinfo.traceback
+ ntraceback = traceback.filter()
+ assert len(ntraceback) == len(traceback) - 1
+
+ def test_traceback_recursion_index(self):
+ def f(n):
+ if n < 10:
+ n += 1
+ f(n)
+ excinfo = py.test.raises(RuntimeError, f, 8)
+ traceback = excinfo.traceback
+ recindex = traceback.recursionindex()
+ assert recindex == 3
+
+ def test_traceback_only_specific_recursion_errors(self, monkeypatch):
+ def f(n):
+ if n == 0:
+ raise RuntimeError("hello")
+ f(n-1)
+
+ excinfo = pytest.raises(RuntimeError, f, 100)
+ monkeypatch.delattr(excinfo.traceback.__class__, "recursionindex")
+ repr = excinfo.getrepr()
+ assert "RuntimeError: hello" in str(repr.reprcrash)
+
+ def test_traceback_no_recursion_index(self):
+ def do_stuff():
+ raise RuntimeError
+
+ def reraise_me():
+ import sys
+ exc, val, tb = sys.exc_info()
+ py.builtin._reraise(exc, val, tb)
+
+ def f(n):
+ try:
+ do_stuff()
+ except:
+ reraise_me()
+ excinfo = py.test.raises(RuntimeError, f, 8)
+ traceback = excinfo.traceback
+ recindex = traceback.recursionindex()
+ assert recindex is None
+
+ def test_traceback_messy_recursion(self):
+ # XXX: simplified locally testable version
+ decorator = py.test.importorskip('decorator').decorator
+
+ def log(f, *k, **kw):
+ print('%s %s' % (k, kw))
+ f(*k, **kw)
+ log = decorator(log)
+
+ def fail():
+ raise ValueError('')
+
+ fail = log(log(fail))
+
+ excinfo = py.test.raises(ValueError, fail)
+ assert excinfo.traceback.recursionindex() is None
+
+ def test_traceback_getcrashentry(self):
+ def i():
+ __tracebackhide__ = True
+ raise ValueError
+
+ def h():
+ i()
+
+ def g():
+ __tracebackhide__ = True
+ h()
+
+ def f():
+ g()
+
+ excinfo = py.test.raises(ValueError, f)
+ tb = excinfo.traceback
+ entry = tb.getcrashentry()
+ co = py.code.Code(h)
+ assert entry.frame.code.path == co.path
+ assert entry.lineno == co.firstlineno + 1
+ assert entry.frame.code.name == 'h'
+
+ def test_traceback_getcrashentry_empty(self):
+ def g():
+ __tracebackhide__ = True
+ raise ValueError
+
+ def f():
+ __tracebackhide__ = True
+ g()
+
+ excinfo = py.test.raises(ValueError, f)
+ tb = excinfo.traceback
+ entry = tb.getcrashentry()
+ co = py.code.Code(g)
+ assert entry.frame.code.path == co.path
+ assert entry.lineno == co.firstlineno + 2
+ assert entry.frame.code.name == 'g'
+
+
+def hello(x):
+ x + 5
+
+
+def test_tbentry_reinterpret():
+ try:
+ hello("hello")
+ except TypeError:
+ excinfo = py.code.ExceptionInfo()
+ tbentry = excinfo.traceback[-1]
+ msg = tbentry.reinterpret()
+ assert msg.startswith("TypeError: ('hello' + 5)")
+
+
+def test_excinfo_exconly():
+ excinfo = py.test.raises(ValueError, h)
+ assert excinfo.exconly().startswith('ValueError')
+ excinfo = py.test.raises(ValueError,
+ "raise ValueError('hello\\nworld')")
+ msg = excinfo.exconly(tryshort=True)
+ assert msg.startswith('ValueError')
+ assert msg.endswith("world")
+
+
+def test_excinfo_repr():
+ excinfo = py.test.raises(ValueError, h)
+ s = repr(excinfo)
+ assert s == "<ExceptionInfo ValueError tblen=4>"
+
+
+def test_excinfo_str():
+ excinfo = py.test.raises(ValueError, h)
+ s = str(excinfo)
+ assert s.startswith(__file__[:-9]) # pyc file and $py.class
+ assert s.endswith("ValueError")
+ assert len(s.split(":")) >= 3 # on windows it's 4
+
+
+def test_excinfo_errisinstance():
+ excinfo = py.test.raises(ValueError, h)
+ assert excinfo.errisinstance(ValueError)
+
+
+def test_excinfo_no_sourcecode():
+ try:
+ exec ("raise ValueError()")
+ except ValueError:
+ excinfo = py.code.ExceptionInfo()
+ s = str(excinfo.traceback[-1])
+ assert s == " File '<string>':1 in <module>\n ???\n"
+
+
+def test_excinfo_no_python_sourcecode(tmpdir):
+ #XXX: simplified locally testable version
+ tmpdir.join('test.txt').write("{{ h()}}:")
+
+ jinja2 = py.test.importorskip('jinja2')
+ loader = jinja2.FileSystemLoader(str(tmpdir))
+ env = jinja2.Environment(loader=loader)
+ template = env.get_template('test.txt')
+ excinfo = py.test.raises(ValueError,
+ template.render, h=h)
+ for item in excinfo.traceback:
+ print(item) # XXX: for some reason jinja.Template.render is printed in full
+ item.source # shouldnt fail
+ if item.path.basename == 'test.txt':
+ assert str(item.source) == '{{ h()}}:'
+
+
+def test_entrysource_Queue_example():
+ try:
+ queue.Queue().get(timeout=0.001)
+ except queue.Empty:
+ excinfo = py.code.ExceptionInfo()
+ entry = excinfo.traceback[-1]
+ source = entry.getsource()
+ assert source is not None
+ s = str(source).strip()
+ assert s.startswith("def get")
+
+
+def test_codepath_Queue_example():
+ try:
+ queue.Queue().get(timeout=0.001)
+ except queue.Empty:
+ excinfo = py.code.ExceptionInfo()
+ entry = excinfo.traceback[-1]
+ path = entry.path
+ assert isinstance(path, py.path.local)
+ assert path.basename.lower() == "queue.py"
+ assert path.check()
+
+
+class TestFormattedExcinfo:
+ def pytest_funcarg__importasmod(self, request):
+ def importasmod(source):
+ source = py.code.Source(source)
+ tmpdir = request.getfuncargvalue("tmpdir")
+ modpath = tmpdir.join("mod.py")
+ tmpdir.ensure("__init__.py")
+ modpath.write(source)
+ if invalidate_import_caches is not None:
+ invalidate_import_caches()
+ return modpath.pyimport()
+ return importasmod
+
+ def excinfo_from_exec(self, source):
+ source = py.code.Source(source).strip()
+ try:
+ exec (source.compile())
+ except KeyboardInterrupt:
+ raise
+ except:
+ return py.code.ExceptionInfo()
+ assert 0, "did not raise"
+
+ def test_repr_source(self):
+ pr = FormattedExcinfo()
+ source = py.code.Source("""
+ def f(x):
+ pass
+ """).strip()
+ pr.flow_marker = "|"
+ lines = pr.get_source(source, 0)
+ assert len(lines) == 2
+ assert lines[0] == "| def f(x):"
+ assert lines[1] == " pass"
+
+ @broken_on_modern_pytest
+ def test_repr_source_excinfo(self):
+ """ check if indentation is right """
+ pr = FormattedExcinfo()
+ excinfo = self.excinfo_from_exec("""
+ def f():
+ assert 0
+ f()
+ """)
+ pr = FormattedExcinfo()
+ source = pr._getentrysource(excinfo.traceback[-1])
+ lines = pr.get_source(source, 1, excinfo)
+ assert lines == [
+ ' def f():',
+ '> assert 0',
+ 'E assert 0'
+ ]
+
+ def test_repr_source_not_existing(self):
+ pr = FormattedExcinfo()
+ co = compile("raise ValueError()", "", "exec")
+ try:
+ exec (co)
+ except ValueError:
+ excinfo = py.code.ExceptionInfo()
+ repr = pr.repr_excinfo(excinfo)
+ assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
+
+ def test_repr_many_line_source_not_existing(self):
+ pr = FormattedExcinfo()
+ co = compile("""
+a = 1
+raise ValueError()
+""", "", "exec")
+ try:
+ exec (co)
+ except ValueError:
+ excinfo = py.code.ExceptionInfo()
+ repr = pr.repr_excinfo(excinfo)
+ assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
+
+ def test_repr_source_failing_fullsource(self):
+ pr = FormattedExcinfo()
+
+ class FakeCode(object):
+ class raw:
+ co_filename = '?'
+ path = '?'
+ firstlineno = 5
+
+ def fullsource(self):
+ return None
+ fullsource = property(fullsource)
+
+ class FakeFrame(object):
+ code = FakeCode()
+ f_locals = {}
+ f_globals = {}
+
+ class FakeTracebackEntry(py.code.Traceback.Entry):
+ def __init__(self, tb):
+ self.lineno = 5+3
+
+ @property
+ def frame(self):
+ return FakeFrame()
+
+ class Traceback(py.code.Traceback):
+ Entry = FakeTracebackEntry
+
+ class FakeExcinfo(py.code.ExceptionInfo):
+ typename = "Foo"
+ def __init__(self):
+ pass
+
+ def exconly(self, tryshort):
+ return "EXC"
+ def errisinstance(self, cls):
+ return False
+
+ excinfo = FakeExcinfo()
+ class FakeRawTB(object):
+ tb_next = None
+ tb = FakeRawTB()
+ excinfo.traceback = Traceback(tb)
+
+ fail = IOError()
+ repr = pr.repr_excinfo(excinfo)
+ assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
+
+ fail = py.error.ENOENT
+ repr = pr.repr_excinfo(excinfo)
+ assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
+
+
+ def test_repr_local(self):
+ p = FormattedExcinfo(showlocals=True)
+ loc = {'y': 5, 'z': 7, 'x': 3, '@x': 2, '__builtins__': {}}
+ reprlocals = p.repr_locals(loc)
+ assert reprlocals.lines
+ assert reprlocals.lines[0] == '__builtins__ = <builtins>'
+ assert reprlocals.lines[1] == 'x = 3'
+ assert reprlocals.lines[2] == 'y = 5'
+ assert reprlocals.lines[3] == 'z = 7'
+
+ def test_repr_tracebackentry_lines(self, importasmod):
+ mod = importasmod("""
+ def func1():
+ raise ValueError("hello\\nworld")
+ """)
+ excinfo = py.test.raises(ValueError, mod.func1)
+ excinfo.traceback = excinfo.traceback.filter()
+ p = FormattedExcinfo()
+ reprtb = p.repr_traceback_entry(excinfo.traceback[-1])
+
+ # test as intermittent entry
+ lines = reprtb.lines
+ assert lines[0] == ' def func1():'
+ assert lines[1] == '> raise ValueError("hello\\nworld")'
+
+ # test as last entry
+ p = FormattedExcinfo(showlocals=True)
+ repr_entry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
+ lines = repr_entry.lines
+ assert lines[0] == ' def func1():'
+ assert lines[1] == '> raise ValueError("hello\\nworld")'
+ assert lines[2] == 'E ValueError: hello'
+ assert lines[3] == 'E world'
+ assert not lines[4:]
+
+ loc = repr_entry.reprlocals is not None
+ loc = repr_entry.reprfileloc
+ assert loc.path == mod.__file__
+ assert loc.lineno == 3
+ #assert loc.message == "ValueError: hello"
+
+ def test_repr_tracebackentry_lines(self, importasmod):
+ mod = importasmod("""
+ def func1(m, x, y, z):
+ raise ValueError("hello\\nworld")
+ """)
+ excinfo = py.test.raises(ValueError, mod.func1, "m"*90, 5, 13, "z"*120)
+ excinfo.traceback = excinfo.traceback.filter()
+ entry = excinfo.traceback[-1]
+ p = FormattedExcinfo(funcargs=True)
+ reprfuncargs = p.repr_args(entry)
+ assert reprfuncargs.args[0] == ('m', repr("m"*90))
+ assert reprfuncargs.args[1] == ('x', '5')
+ assert reprfuncargs.args[2] == ('y', '13')
+ assert reprfuncargs.args[3] == ('z', repr("z" * 120))
+
+ p = FormattedExcinfo(funcargs=True)
+ repr_entry = p.repr_traceback_entry(entry)
+ assert repr_entry.reprfuncargs.args == reprfuncargs.args
+ tw = TWMock()
+ repr_entry.toterminal(tw)
+ assert tw.lines[0] == "m = " + repr('m' * 90)
+ assert tw.lines[1] == "x = 5, y = 13"
+ assert tw.lines[2] == "z = " + repr('z' * 120)
+
+ def test_repr_tracebackentry_lines_var_kw_args(self, importasmod):
+ mod = importasmod("""
+ def func1(x, *y, **z):
+ raise ValueError("hello\\nworld")
+ """)
+ excinfo = py.test.raises(ValueError, mod.func1, 'a', 'b', c='d')
+ excinfo.traceback = excinfo.traceback.filter()
+ entry = excinfo.traceback[-1]
+ p = FormattedExcinfo(funcargs=True)
+ reprfuncargs = p.repr_args(entry)
+ assert reprfuncargs.args[0] == ('x', repr('a'))
+ assert reprfuncargs.args[1] == ('y', repr(('b',)))
+ assert reprfuncargs.args[2] == ('z', repr({'c': 'd'}))
+
+ p = FormattedExcinfo(funcargs=True)
+ repr_entry = p.repr_traceback_entry(entry)
+ assert repr_entry.reprfuncargs.args == reprfuncargs.args
+ tw = TWMock()
+ repr_entry.toterminal(tw)
+ assert tw.lines[0] == "x = 'a', y = ('b',), z = {'c': 'd'}"
+
+ def test_repr_tracebackentry_short(self, importasmod):
+ mod = importasmod("""
+ def func1():
+ raise ValueError("hello")
+ def entry():
+ func1()
+ """)
+ excinfo = py.test.raises(ValueError, mod.entry)
+ p = FormattedExcinfo(style="short")
+ reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
+ lines = reprtb.lines
+ basename = py.path.local(mod.__file__).basename
+ assert lines[0] == ' func1()'
+ assert basename in str(reprtb.reprfileloc.path)
+ assert reprtb.reprfileloc.lineno == 5
+
+ # test last entry
+ p = FormattedExcinfo(style="short")
+ reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
+ lines = reprtb.lines
+ assert lines[0] == ' raise ValueError("hello")'
+ assert lines[1] == 'E ValueError: hello'
+ assert basename in str(reprtb.reprfileloc.path)
+ assert reprtb.reprfileloc.lineno == 3
+
+ def test_repr_tracebackentry_no(self, importasmod):
+ mod = importasmod("""
+ def func1():
+ raise ValueError("hello")
+ def entry():
+ func1()
+ """)
+ excinfo = py.test.raises(ValueError, mod.entry)
+ p = FormattedExcinfo(style="no")
+ p.repr_traceback_entry(excinfo.traceback[-2])
+
+ p = FormattedExcinfo(style="no")
+ reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
+ lines = reprentry.lines
+ assert lines[0] == 'E ValueError: hello'
+ assert not lines[1:]
+
+ def test_repr_traceback_tbfilter(self, importasmod):
+ mod = importasmod("""
+ def f(x):
+ raise ValueError(x)
+ def entry():
+ f(0)
+ """)
+ excinfo = py.test.raises(ValueError, mod.entry)
+ p = FormattedExcinfo(tbfilter=True)
+ reprtb = p.repr_traceback(excinfo)
+ assert len(reprtb.reprentries) == 2
+ p = FormattedExcinfo(tbfilter=False)
+ reprtb = p.repr_traceback(excinfo)
+ assert len(reprtb.reprentries) == 3
+
+ def test_traceback_short_no_source(self, importasmod, monkeypatch):
+ mod = importasmod("""
+ def func1():
+ raise ValueError("hello")
+ def entry():
+ func1()
+ """)
+ try:
+ mod.entry()
+ except ValueError:
+ excinfo = py.code.ExceptionInfo()
+ from py._code.code import Code
+ monkeypatch.setattr(Code, 'path', 'bogus')
+ excinfo.traceback[0].frame.code.path = "bogus"
+ p = FormattedExcinfo(style="short")
+ reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
+ lines = reprtb.lines
+ last_p = FormattedExcinfo(style="short")
+ last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
+ last_lines = last_reprtb.lines
+ monkeypatch.undo()
+ basename = py.path.local(mod.__file__).basename
+ assert lines[0] == ' func1()'
+
+ assert last_lines[0] == ' raise ValueError("hello")'
+ assert last_lines[1] == 'E ValueError: hello'
+
+ def test_repr_traceback_and_excinfo(self, importasmod):
+ mod = importasmod("""
+ def f(x):
+ raise ValueError(x)
+ def entry():
+ f(0)
+ """)
+ excinfo = py.test.raises(ValueError, mod.entry)
+
+ for style in ("long", "short"):
+ p = FormattedExcinfo(style=style)
+ reprtb = p.repr_traceback(excinfo)
+ assert len(reprtb.reprentries) == 2
+ assert reprtb.style == style
+ assert not reprtb.extraline
+ repr = p.repr_excinfo(excinfo)
+ assert repr.reprtraceback
+ assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries)
+ assert repr.reprcrash.path.endswith("mod.py")
+ assert repr.reprcrash.message == "ValueError: 0"
+
+ def test_repr_traceback_with_invalid_cwd(self, importasmod, monkeypatch):
+ mod = importasmod("""
+ def f(x):
+ raise ValueError(x)
+ def entry():
+ f(0)
+ """)
+ excinfo = py.test.raises(ValueError, mod.entry)
+
+ p = FormattedExcinfo()
+ def raiseos():
+ raise OSError(2)
+ monkeypatch.setattr('os.getcwd', raiseos)
+ assert p._makepath(__file__) == __file__
+ reprtb = p.repr_traceback(excinfo)
+
+ @broken_on_modern_pytest
+ def test_repr_excinfo_addouterr(self, importasmod):
+ mod = importasmod("""
+ def entry():
+ raise ValueError()
+ """)
+ excinfo = py.test.raises(ValueError, mod.entry)
+ repr = excinfo.getrepr()
+ repr.addsection("title", "content")
+ twmock = TWMock()
+ repr.toterminal(twmock)
+ assert twmock.lines[-1] == "content"
+ assert twmock.lines[-2] == ("-", "title")
+
+ def test_repr_excinfo_reprcrash(self, importasmod):
+ mod = importasmod("""
+ def entry():
+ raise ValueError()
+ """)
+ excinfo = py.test.raises(ValueError, mod.entry)
+ repr = excinfo.getrepr()
+ assert repr.reprcrash.path.endswith("mod.py")
+ assert repr.reprcrash.lineno == 3
+ assert repr.reprcrash.message == "ValueError"
+ assert str(repr.reprcrash).endswith("mod.py:3: ValueError")
+
+ def test_repr_traceback_recursion(self, importasmod):
+ mod = importasmod("""
+ def rec2(x):
+ return rec1(x+1)
+ def rec1(x):
+ return rec2(x-1)
+ def entry():
+ rec1(42)
+ """)
+ excinfo = py.test.raises(RuntimeError, mod.entry)
+
+ for style in ("short", "long", "no"):
+ p = FormattedExcinfo(style="short")
+ reprtb = p.repr_traceback(excinfo)
+ assert reprtb.extraline == "!!! Recursion detected (same locals & position)"
+ assert str(reprtb)
+
+ @broken_on_modern_pytest
+ def test_tb_entry_AssertionError(self, importasmod):
+ # probably this test is a bit redundant
+ # as py/magic/testing/test_assertion.py
+ # already tests correctness of
+ # assertion-reinterpretation logic
+ mod = importasmod("""
+ def somefunc():
+ x = 1
+ assert x == 2
+ """)
+ excinfo = py.test.raises(AssertionError, mod.somefunc)
+
+ p = FormattedExcinfo()
+ reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
+ lines = reprentry.lines
+ assert lines[-1] == "E assert 1 == 2"
+
+ def test_reprexcinfo_getrepr(self, importasmod):
+ mod = importasmod("""
+ def f(x):
+ raise ValueError(x)
+ def entry():
+ f(0)
+ """)
+ try:
+ mod.entry()
+ except ValueError:
+ excinfo = py.code.ExceptionInfo()
+
+ for style in ("short", "long", "no"):
+ for showlocals in (True, False):
+ repr = excinfo.getrepr(style=style, showlocals=showlocals)
+ assert isinstance(repr, ReprExceptionInfo)
+ assert repr.reprtraceback.style == style
+
+ def test_reprexcinfo_unicode(self):
+ from py._code.code import TerminalRepr
+ class MyRepr(TerminalRepr):
+ def toterminal(self, tw):
+ tw.line(py.builtin._totext("я", "utf-8"))
+ x = py.builtin._totext(MyRepr())
+ assert x == py.builtin._totext("я", "utf-8")
+
+ @broken_on_modern_pytest
+ def test_toterminal_long(self, importasmod):
+ mod = importasmod("""
+ def g(x):
+ raise ValueError(x)
+ def f():
+ g(3)
+ """)
+ excinfo = py.test.raises(ValueError, mod.f)
+ excinfo.traceback = excinfo.traceback.filter()
+ repr = excinfo.getrepr()
+ tw = TWMock()
+ repr.toterminal(tw)
+ assert tw.lines[0] == ""
+ tw.lines.pop(0)
+ assert tw.lines[0] == " def f():"
+ assert tw.lines[1] == "> g(3)"
+ assert tw.lines[2] == ""
+ assert tw.lines[3].endswith("mod.py:5: ")
+ assert tw.lines[4] == ("_ ", None)
+ assert tw.lines[5] == ""
+ assert tw.lines[6] == " def g(x):"
+ assert tw.lines[7] == "> raise ValueError(x)"
+ assert tw.lines[8] == "E ValueError: 3"
+ assert tw.lines[9] == ""
+ assert tw.lines[10].endswith("mod.py:3: ValueError")
+
+ @broken_on_modern_pytest
+ def test_toterminal_long_missing_source(self, importasmod, tmpdir):
+ mod = importasmod("""
+ def g(x):
+ raise ValueError(x)
+ def f():
+ g(3)
+ """)
+ excinfo = py.test.raises(ValueError, mod.f)
+ tmpdir.join('mod.py').remove()
+ excinfo.traceback = excinfo.traceback.filter()
+ repr = excinfo.getrepr()
+ tw = TWMock()
+ repr.toterminal(tw)
+ assert tw.lines[0] == ""
+ tw.lines.pop(0)
+ assert tw.lines[0] == "> ???"
+ assert tw.lines[1] == ""
+ assert tw.lines[2].endswith("mod.py:5: ")
+ assert tw.lines[3] == ("_ ", None)
+ assert tw.lines[4] == ""
+ assert tw.lines[5] == "> ???"
+ assert tw.lines[6] == "E ValueError: 3"
+ assert tw.lines[7] == ""
+ assert tw.lines[8].endswith("mod.py:3: ValueError")
+
+ @broken_on_modern_pytest
+ def test_toterminal_long_incomplete_source(self, importasmod, tmpdir):
+ mod = importasmod("""
+ def g(x):
+ raise ValueError(x)
+ def f():
+ g(3)
+ """)
+ excinfo = py.test.raises(ValueError, mod.f)
+ tmpdir.join('mod.py').write('asdf')
+ excinfo.traceback = excinfo.traceback.filter()
+ repr = excinfo.getrepr()
+ tw = TWMock()
+ repr.toterminal(tw)
+ assert tw.lines[0] == ""
+ tw.lines.pop(0)
+ assert tw.lines[0] == "> ???"
+ assert tw.lines[1] == ""
+ assert tw.lines[2].endswith("mod.py:5: ")
+ assert tw.lines[3] == ("_ ", None)
+ assert tw.lines[4] == ""
+ assert tw.lines[5] == "> ???"
+ assert tw.lines[6] == "E ValueError: 3"
+ assert tw.lines[7] == ""
+ assert tw.lines[8].endswith("mod.py:3: ValueError")
+
+ @broken_on_modern_pytest
+ def test_toterminal_long_filenames(self, importasmod):
+ mod = importasmod("""
+ def f():
+ raise ValueError()
+ """)
+ excinfo = py.test.raises(ValueError, mod.f)
+ tw = TWMock()
+ path = py.path.local(mod.__file__)
+ old = path.dirpath().chdir()
+ try:
+ repr = excinfo.getrepr(abspath=False)
+ repr.toterminal(tw)
+ line = tw.lines[-1]
+ x = py.path.local().bestrelpath(path)
+ if len(x) < len(str(path)):
+ assert line == "mod.py:3: ValueError"
+
+ repr = excinfo.getrepr(abspath=True)
+ repr.toterminal(tw)
+ line = tw.lines[-1]
+ assert line == "%s:3: ValueError" %(path,)
+ finally:
+ old.chdir()
+
+ @pytest.mark.parametrize('style', ("long", "short", "no"))
+ @pytest.mark.parametrize('showlocals', (True, False),
+ ids=['locals', 'nolocals'])
+ @pytest.mark.parametrize('tbfilter', (True, False),
+ ids=['tbfilter', 'nofilter'])
+ @pytest.mark.parametrize('funcargs', (True, False),
+ ids=['funcargs', 'nofuncargs'])
+ def test_format_excinfo(self, importasmod,
+ style, showlocals, tbfilter, funcargs):
+
+ mod = importasmod("""
+ def g(x):
+ raise ValueError(x)
+ def f():
+ g(3)
+ """)
+ excinfo = py.test.raises(ValueError, mod.f)
+ tw = py.io.TerminalWriter(stringio=True)
+ repr = excinfo.getrepr(
+ style=style,
+ showlocals=showlocals,
+ funcargs=funcargs,
+ tbfilter=tbfilter
+ )
+ repr.toterminal(tw)
+ assert tw.stringio.getvalue()
+
+ @broken_on_modern_pytest
+ def test_native_style(self):
+ excinfo = self.excinfo_from_exec("""
+ assert 0
+ """)
+ repr = excinfo.getrepr(style='native')
+ assert "assert 0" in str(repr.reprcrash)
+ s = str(repr)
+ assert s.startswith('Traceback (most recent call last):\n File')
+ assert s.endswith('\nAssertionError: assert 0')
+ assert 'exec (source.compile())' in s
+ assert s.count('assert 0') == 2
+
+ @broken_on_modern_pytest
+ def test_traceback_repr_style(self, importasmod):
+ mod = importasmod("""
+ def f():
+ g()
+ def g():
+ h()
+ def h():
+ i()
+ def i():
+ raise ValueError()
+ """)
+ excinfo = py.test.raises(ValueError, mod.f)
+ excinfo.traceback = excinfo.traceback.filter()
+ excinfo.traceback[1].set_repr_style("short")
+ excinfo.traceback[2].set_repr_style("short")
+ r = excinfo.getrepr(style="long")
+ tw = TWMock()
+ r.toterminal(tw)
+ for line in tw.lines: print (line)
+ assert tw.lines[0] == ""
+ assert tw.lines[1] == " def f():"
+ assert tw.lines[2] == "> g()"
+ assert tw.lines[3] == ""
+ assert tw.lines[4].endswith("mod.py:3: ")
+ assert tw.lines[5] == ("_ ", None)
+ assert tw.lines[6].endswith("in g")
+ assert tw.lines[7] == " h()"
+ assert tw.lines[8].endswith("in h")
+ assert tw.lines[9] == " i()"
+ assert tw.lines[10] == ("_ ", None)
+ assert tw.lines[11] == ""
+ assert tw.lines[12] == " def i():"
+ assert tw.lines[13] == "> raise ValueError()"
+ assert tw.lines[14] == "E ValueError"
+ assert tw.lines[15] == ""
+ assert tw.lines[16].endswith("mod.py:9: ValueError")
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_source.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_source.py
new file mode 100644
index 00000000000..3492761a4e9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/code/test_source.py
@@ -0,0 +1,648 @@
+from py.code import Source
+import py
+import sys
+import inspect
+
+from py._code.source import _ast
+if _ast is not None:
+ astonly = py.test.mark.nothing
+else:
+ astonly = py.test.mark.xfail("True", reason="only works with AST-compile")
+
+failsonjython = py.test.mark.xfail("sys.platform.startswith('java')")
+
+def test_source_str_function():
+ x = Source("3")
+ assert str(x) == "3"
+
+ x = Source(" 3")
+ assert str(x) == "3"
+
+ x = Source("""
+ 3
+ """, rstrip=False)
+ assert str(x) == "\n3\n "
+
+ x = Source("""
+ 3
+ """, rstrip=True)
+ assert str(x) == "\n3"
+
+def test_unicode():
+ try:
+ unicode
+ except NameError:
+ return
+ x = Source(unicode("4"))
+ assert str(x) == "4"
+ co = py.code.compile(unicode('u"\xc3\xa5"', 'utf8'), mode='eval')
+ val = eval(co)
+ assert isinstance(val, unicode)
+
+def test_source_from_function():
+ source = py.code.Source(test_source_str_function)
+ assert str(source).startswith('def test_source_str_function():')
+
+def test_source_from_method():
+ class TestClass:
+ def test_method(self):
+ pass
+ source = py.code.Source(TestClass().test_method)
+ assert source.lines == ["def test_method(self):",
+ " pass"]
+
+def test_source_from_lines():
+ lines = ["a \n", "b\n", "c"]
+ source = py.code.Source(lines)
+ assert source.lines == ['a ', 'b', 'c']
+
+def test_source_from_inner_function():
+ def f():
+ pass
+ source = py.code.Source(f, deindent=False)
+ assert str(source).startswith(' def f():')
+ source = py.code.Source(f)
+ assert str(source).startswith('def f():')
+
+def test_source_putaround_simple():
+ source = Source("raise ValueError")
+ source = source.putaround(
+ "try:", """\
+ except ValueError:
+ x = 42
+ else:
+ x = 23""")
+ assert str(source)=="""\
+try:
+ raise ValueError
+except ValueError:
+ x = 42
+else:
+ x = 23"""
+
+def test_source_putaround():
+ source = Source()
+ source = source.putaround("""
+ if 1:
+ x=1
+ """)
+ assert str(source).strip() == "if 1:\n x=1"
+
+def test_source_strips():
+ source = Source("")
+ assert source == Source()
+ assert str(source) == ''
+ assert source.strip() == source
+
+def test_source_strip_multiline():
+ source = Source()
+ source.lines = ["", " hello", " "]
+ source2 = source.strip()
+ assert source2.lines == [" hello"]
+
+def test_syntaxerror_rerepresentation():
+ ex = py.test.raises(SyntaxError, py.code.compile, 'xyz xyz')
+ assert ex.value.lineno == 1
+ assert ex.value.offset in (4,7) # XXX pypy/jython versus cpython?
+ assert ex.value.text.strip(), 'x x'
+
+def test_isparseable():
+ assert Source("hello").isparseable()
+ assert Source("if 1:\n pass").isparseable()
+ assert Source(" \nif 1:\n pass").isparseable()
+ assert not Source("if 1:\n").isparseable()
+ assert not Source(" \nif 1:\npass").isparseable()
+ assert not Source(chr(0)).isparseable()
+
+class TestAccesses:
+ source = Source("""\
+ def f(x):
+ pass
+ def g(x):
+ pass
+ """)
+ def test_getrange(self):
+ x = self.source[0:2]
+ assert x.isparseable()
+ assert len(x.lines) == 2
+ assert str(x) == "def f(x):\n pass"
+
+ def test_getline(self):
+ x = self.source[0]
+ assert x == "def f(x):"
+
+ def test_len(self):
+ assert len(self.source) == 4
+
+ def test_iter(self):
+ l = [x for x in self.source]
+ assert len(l) == 4
+
+class TestSourceParsingAndCompiling:
+ source = Source("""\
+ def f(x):
+ assert (x ==
+ 3 +
+ 4)
+ """).strip()
+
+ def test_compile(self):
+ co = py.code.compile("x=3")
+ d = {}
+ exec (co, d)
+ assert d['x'] == 3
+
+ def test_compile_and_getsource_simple(self):
+ co = py.code.compile("x=3")
+ exec (co)
+ source = py.code.Source(co)
+ assert str(source) == "x=3"
+
+ def test_compile_and_getsource_through_same_function(self):
+ def gensource(source):
+ return py.code.compile(source)
+ co1 = gensource("""
+ def f():
+ raise KeyError()
+ """)
+ co2 = gensource("""
+ def f():
+ raise ValueError()
+ """)
+ source1 = inspect.getsource(co1)
+ assert 'KeyError' in source1
+ source2 = inspect.getsource(co2)
+ assert 'ValueError' in source2
+
+ def test_getstatement(self):
+ #print str(self.source)
+ ass = str(self.source[1:])
+ for i in range(1, 4):
+ #print "trying start in line %r" % self.source[i]
+ s = self.source.getstatement(i)
+ #x = s.deindent()
+ assert str(s) == ass
+
+ def test_getstatementrange_triple_quoted(self):
+ #print str(self.source)
+ source = Source("""hello('''
+ ''')""")
+ s = source.getstatement(0)
+ assert s == str(source)
+ s = source.getstatement(1)
+ assert s == str(source)
+
+ @astonly
+ def test_getstatementrange_within_constructs(self):
+ source = Source("""\
+ try:
+ try:
+ raise ValueError
+ except SomeThing:
+ pass
+ finally:
+ 42
+ """)
+ assert len(source) == 7
+ # check all lineno's that could occur in a traceback
+ #assert source.getstatementrange(0) == (0, 7)
+ #assert source.getstatementrange(1) == (1, 5)
+ assert source.getstatementrange(2) == (2, 3)
+ assert source.getstatementrange(3) == (3, 4)
+ assert source.getstatementrange(4) == (4, 5)
+ #assert source.getstatementrange(5) == (0, 7)
+ assert source.getstatementrange(6) == (6, 7)
+
+ def test_getstatementrange_bug(self):
+ source = Source("""\
+ try:
+ x = (
+ y +
+ z)
+ except:
+ pass
+ """)
+ assert len(source) == 6
+ assert source.getstatementrange(2) == (1, 4)
+
+ def test_getstatementrange_bug2(self):
+ source = Source("""\
+ assert (
+ 33
+ ==
+ [
+ X(3,
+ b=1, c=2
+ ),
+ ]
+ )
+ """)
+ assert len(source) == 9
+ assert source.getstatementrange(5) == (0, 9)
+
+ def test_getstatementrange_ast_issue58(self):
+ source = Source("""\
+
+ def test_some():
+ for a in [a for a in
+ CAUSE_ERROR]: pass
+
+ x = 3
+ """)
+ assert getstatement(2, source).lines == source.lines[2:3]
+ assert getstatement(3, source).lines == source.lines[3:4]
+
+ def test_getstatementrange_out_of_bounds_py3(self):
+ source = Source("if xxx:\n from .collections import something")
+ r = source.getstatementrange(1)
+ assert r == (1,2)
+
+ def test_getstatementrange_with_syntaxerror_issue7(self):
+ source = Source(":")
+ py.test.raises(SyntaxError, lambda: source.getstatementrange(0))
+
+ def test_compile_to_ast(self):
+ import ast
+ source = Source("x = 4")
+ mod = source.compile(flag=ast.PyCF_ONLY_AST)
+ assert isinstance(mod, ast.Module)
+ compile(mod, "<filename>", "exec")
+
+ def test_compile_and_getsource(self):
+ co = self.source.compile()
+ py.builtin.exec_(co, globals())
+ f(7)
+ excinfo = py.test.raises(AssertionError, "f(6)")
+ frame = excinfo.traceback[-1].frame
+ stmt = frame.code.fullsource.getstatement(frame.lineno)
+ #print "block", str(block)
+ assert str(stmt).strip().startswith('assert')
+
+ def test_compilefuncs_and_path_sanity(self):
+ def check(comp, name):
+ co = comp(self.source, name)
+ if not name:
+ expected = "codegen %s:%d>" %(mypath, mylineno+2+1)
+ else:
+ expected = "codegen %r %s:%d>" % (name, mypath, mylineno+2+1)
+ fn = co.co_filename
+ assert fn.endswith(expected)
+
+ mycode = py.code.Code(self.test_compilefuncs_and_path_sanity)
+ mylineno = mycode.firstlineno
+ mypath = mycode.path
+
+ for comp in py.code.compile, py.code.Source.compile:
+ for name in '', None, 'my':
+ yield check, comp, name
+
+ def test_offsetless_synerr(self):
+ py.test.raises(SyntaxError, py.code.compile, "lambda a,a: 0", mode='eval')
+
+def test_getstartingblock_singleline():
+ class A:
+ def __init__(self, *args):
+ frame = sys._getframe(1)
+ self.source = py.code.Frame(frame).statement
+
+ x = A('x', 'y')
+
+ l = [i for i in x.source.lines if i.strip()]
+ assert len(l) == 1
+
+def test_getstartingblock_multiline():
+ class A:
+ def __init__(self, *args):
+ frame = sys._getframe(1)
+ self.source = py.code.Frame(frame).statement
+
+ x = A('x',
+ 'y' \
+ ,
+ 'z')
+
+ l = [i for i in x.source.lines if i.strip()]
+ assert len(l) == 4
+
+def test_getline_finally():
+ def c(): pass
+ excinfo = py.test.raises(TypeError, """
+ teardown = None
+ try:
+ c(1)
+ finally:
+ if teardown:
+ teardown()
+ """)
+ source = excinfo.traceback[-1].statement
+ assert str(source).strip() == 'c(1)'
+
+def test_getfuncsource_dynamic():
+ source = """
+ def f():
+ raise ValueError
+
+ def g(): pass
+ """
+ co = py.code.compile(source)
+ py.builtin.exec_(co, globals())
+ assert str(py.code.Source(f)).strip() == 'def f():\n raise ValueError'
+ assert str(py.code.Source(g)).strip() == 'def g(): pass'
+
+
+def test_getfuncsource_with_multine_string():
+ def f():
+ c = '''while True:
+ pass
+'''
+ assert str(py.code.Source(f)).strip() == "def f():\n c = '''while True:\n pass\n'''"
+
+
+def test_deindent():
+ from py._code.source import deindent as deindent
+ assert deindent(['\tfoo', '\tbar', ]) == ['foo', 'bar']
+
+ def f():
+ c = '''while True:
+ pass
+'''
+ import inspect
+ lines = deindent(inspect.getsource(f).splitlines())
+ assert lines == ["def f():", " c = '''while True:", " pass", "'''"]
+
+ source = """
+ def f():
+ def g():
+ pass
+ """
+ lines = deindent(source.splitlines())
+ assert lines == ['', 'def f():', ' def g():', ' pass', ' ']
+
+def test_source_of_class_at_eof_without_newline(tmpdir):
+ # this test fails because the implicit inspect.getsource(A) below
+ # does not return the "x = 1" last line.
+ source = py.code.Source('''
+ class A(object):
+ def method(self):
+ x = 1
+ ''')
+ path = tmpdir.join("a.py")
+ path.write(source)
+ s2 = py.code.Source(tmpdir.join("a.py").pyimport().A)
+ assert str(source).strip() == str(s2).strip()
+
+if True:
+ def x():
+ pass
+
+def test_getsource_fallback():
+ from py._code.source import getsource
+ expected = """def x():
+ pass"""
+ src = getsource(x)
+ assert src == expected
+
+def test_idem_compile_and_getsource():
+ from py._code.source import getsource
+ expected = "def x(): pass"
+ co = py.code.compile(expected)
+ src = getsource(co)
+ assert src == expected
+
+def test_findsource_fallback():
+ from py._code.source import findsource
+ src, lineno = findsource(x)
+ assert 'test_findsource_simple' in str(src)
+ assert src[lineno] == ' def x():'
+
+def test_findsource():
+ from py._code.source import findsource
+ co = py.code.compile("""if 1:
+ def x():
+ pass
+""")
+
+ src, lineno = findsource(co)
+ assert 'if 1:' in str(src)
+
+ d = {}
+ eval(co, d)
+ src, lineno = findsource(d['x'])
+ assert 'if 1:' in str(src)
+ assert src[lineno] == " def x():"
+
+
+def test_getfslineno():
+ from py.code import getfslineno
+
+ def f(x):
+ pass
+
+ fspath, lineno = getfslineno(f)
+
+ assert fspath.basename == "test_source.py"
+ assert lineno == py.code.getrawcode(f).co_firstlineno-1 # see findsource
+
+ class A(object):
+ pass
+
+ fspath, lineno = getfslineno(A)
+
+ _, A_lineno = inspect.findsource(A)
+ assert fspath.basename == "test_source.py"
+ assert lineno == A_lineno
+
+ assert getfslineno(3) == ("", -1)
+ class B:
+ pass
+ B.__name__ = "B2"
+ assert getfslineno(B)[1] == -1
+
+def test_code_of_object_instance_with_call():
+ class A:
+ pass
+ py.test.raises(TypeError, lambda: py.code.Source(A()))
+ class WithCall:
+ def __call__(self):
+ pass
+
+ code = py.code.Code(WithCall())
+ assert 'pass' in str(code.source())
+
+ class Hello(object):
+ def __call__(self):
+ pass
+ py.test.raises(TypeError, lambda: py.code.Code(Hello))
+
+
+def getstatement(lineno, source):
+ from py._code.source import getstatementrange_ast
+ source = py.code.Source(source, deindent=False)
+ ast, start, end = getstatementrange_ast(lineno, source)
+ return source[start:end]
+
+def test_oneline():
+ source = getstatement(0, "raise ValueError")
+ assert str(source) == "raise ValueError"
+
+def test_comment_and_no_newline_at_end():
+ from py._code.source import getstatementrange_ast
+ source = Source(['def test_basic_complex():',
+ ' assert 1 == 2',
+ '# vim: filetype=pyopencl:fdm=marker'])
+ ast, start, end = getstatementrange_ast(1, source)
+ assert end == 2
+
+def test_oneline_and_comment():
+ source = getstatement(0, "raise ValueError\n#hello")
+ assert str(source) == "raise ValueError"
+
+def test_comments():
+ source = '''def test():
+ "comment 1"
+ x = 1
+ # comment 2
+ # comment 3
+
+ assert False
+
+"""
+comment 4
+"""
+'''
+ for line in range(2,6):
+ assert str(getstatement(line, source)) == ' x = 1'
+ for line in range(6,10):
+ assert str(getstatement(line, source)) == ' assert False'
+ assert str(getstatement(10, source)) == '"""'
+
+def test_comment_in_statement():
+ source = '''test(foo=1,
+ # comment 1
+ bar=2)
+'''
+ for line in range(1,3):
+ assert str(getstatement(line, source)) == \
+ 'test(foo=1,\n # comment 1\n bar=2)'
+
+def test_single_line_else():
+ source = getstatement(1, "if False: 2\nelse: 3")
+ assert str(source) == "else: 3"
+
+def test_single_line_finally():
+ source = getstatement(1, "try: 1\nfinally: 3")
+ assert str(source) == "finally: 3"
+
+def test_issue55():
+ source = ('def round_trip(dinp):\n assert 1 == dinp\n'
+ 'def test_rt():\n round_trip("""\n""")\n')
+ s = getstatement(3, source)
+ assert str(s) == ' round_trip("""\n""")'
+
+
+def XXXtest_multiline():
+ source = getstatement(0, """\
+raise ValueError(
+ 23
+)
+x = 3
+""")
+ assert str(source) == "raise ValueError(\n 23\n)"
+
+class TestTry:
+ pytestmark = astonly
+ source = """\
+try:
+ raise ValueError
+except Something:
+ raise IndexError(1)
+else:
+ raise KeyError()
+"""
+
+ def test_body(self):
+ source = getstatement(1, self.source)
+ assert str(source) == " raise ValueError"
+
+ def test_except_line(self):
+ source = getstatement(2, self.source)
+ assert str(source) == "except Something:"
+
+ def test_except_body(self):
+ source = getstatement(3, self.source)
+ assert str(source) == " raise IndexError(1)"
+
+ def test_else(self):
+ source = getstatement(5, self.source)
+ assert str(source) == " raise KeyError()"
+
+class TestTryFinally:
+ source = """\
+try:
+ raise ValueError
+finally:
+ raise IndexError(1)
+"""
+
+ def test_body(self):
+ source = getstatement(1, self.source)
+ assert str(source) == " raise ValueError"
+
+ def test_finally(self):
+ source = getstatement(3, self.source)
+ assert str(source) == " raise IndexError(1)"
+
+
+
+class TestIf:
+ pytestmark = astonly
+ source = """\
+if 1:
+ y = 3
+elif False:
+ y = 5
+else:
+ y = 7
+"""
+
+ def test_body(self):
+ source = getstatement(1, self.source)
+ assert str(source) == " y = 3"
+
+ def test_elif_clause(self):
+ source = getstatement(2, self.source)
+ assert str(source) == "elif False:"
+
+ def test_elif(self):
+ source = getstatement(3, self.source)
+ assert str(source) == " y = 5"
+
+ def test_else(self):
+ source = getstatement(5, self.source)
+ assert str(source) == " y = 7"
+
+def test_semicolon():
+ s = """\
+hello ; pytest.skip()
+"""
+ source = getstatement(0, s)
+ assert str(source) == s.strip()
+
+def test_def_online():
+ s = """\
+def func(): raise ValueError(42)
+
+def something():
+ pass
+"""
+ source = getstatement(0, s)
+ assert str(source) == "def func(): raise ValueError(42)"
+
+def XXX_test_expression_multiline():
+ source = """\
+something
+'''
+'''"""
+ result = getstatement(1, source)
+ assert str(result) == "'''\n'''"
+
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/conftest.py
index 0f956b3dd25..0f956b3dd25 100644
--- a/tests/wpt/web-platform-tests/tools/py/testing/conftest.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/conftest.py
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/io_/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/__init__.py
index 792d6005489..792d6005489 100644
--- a/tests/wpt/web-platform-tests/tools/py/testing/io_/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_capture.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_capture.py
new file mode 100644
index 00000000000..b5fedd0abc6
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_capture.py
@@ -0,0 +1,501 @@
+from __future__ import with_statement
+
+import os, sys
+import py
+
+needsdup = py.test.mark.skipif("not hasattr(os, 'dup')")
+
+from py.builtin import print_
+
+if sys.version_info >= (3,0):
+ def tobytes(obj):
+ if isinstance(obj, str):
+ obj = obj.encode('UTF-8')
+ assert isinstance(obj, bytes)
+ return obj
+ def totext(obj):
+ if isinstance(obj, bytes):
+ obj = str(obj, 'UTF-8')
+ assert isinstance(obj, str)
+ return obj
+else:
+ def tobytes(obj):
+ if isinstance(obj, unicode):
+ obj = obj.encode('UTF-8')
+ assert isinstance(obj, str)
+ return obj
+ def totext(obj):
+ if isinstance(obj, str):
+ obj = unicode(obj, 'UTF-8')
+ assert isinstance(obj, unicode)
+ return obj
+
+def oswritebytes(fd, obj):
+ os.write(fd, tobytes(obj))
+
+class TestTextIO:
+ def test_text(self):
+ f = py.io.TextIO()
+ f.write("hello")
+ s = f.getvalue()
+ assert s == "hello"
+ f.close()
+
+ def test_unicode_and_str_mixture(self):
+ f = py.io.TextIO()
+ if sys.version_info >= (3,0):
+ f.write("\u00f6")
+ py.test.raises(TypeError, "f.write(bytes('hello', 'UTF-8'))")
+ else:
+ f.write(unicode("\u00f6", 'UTF-8'))
+ f.write("hello") # bytes
+ s = f.getvalue()
+ f.close()
+ assert isinstance(s, unicode)
+
+def test_bytes_io():
+ f = py.io.BytesIO()
+ f.write(tobytes("hello"))
+ py.test.raises(TypeError, "f.write(totext('hello'))")
+ s = f.getvalue()
+ assert s == tobytes("hello")
+
+def test_dontreadfrominput():
+ from py._io.capture import DontReadFromInput
+ f = DontReadFromInput()
+ assert not f.isatty()
+ py.test.raises(IOError, f.read)
+ py.test.raises(IOError, f.readlines)
+ py.test.raises(IOError, iter, f)
+ py.test.raises(ValueError, f.fileno)
+ f.close() # just for completeness
+
+def pytest_funcarg__tmpfile(request):
+ testdir = request.getfuncargvalue("testdir")
+ f = testdir.makepyfile("").open('wb+')
+ request.addfinalizer(f.close)
+ return f
+
+@needsdup
+def test_dupfile(tmpfile):
+ flist = []
+ for i in range(5):
+ nf = py.io.dupfile(tmpfile, encoding="utf-8")
+ assert nf != tmpfile
+ assert nf.fileno() != tmpfile.fileno()
+ assert nf not in flist
+ print_(i, end="", file=nf)
+ flist.append(nf)
+ for i in range(5):
+ f = flist[i]
+ f.close()
+ tmpfile.seek(0)
+ s = tmpfile.read()
+ assert "01234" in repr(s)
+ tmpfile.close()
+
+def test_dupfile_no_mode():
+ """
+ dupfile should trap an AttributeError and return f if no mode is supplied.
+ """
+ class SomeFileWrapper(object):
+ "An object with a fileno method but no mode attribute"
+ def fileno(self):
+ return 1
+ tmpfile = SomeFileWrapper()
+ assert py.io.dupfile(tmpfile) is tmpfile
+ with py.test.raises(AttributeError):
+ py.io.dupfile(tmpfile, raising=True)
+
+def lsof_check(func):
+ pid = os.getpid()
+ try:
+ out = py.process.cmdexec("lsof -p %d" % pid)
+ except py.process.cmdexec.Error:
+ py.test.skip("could not run 'lsof'")
+ func()
+ out2 = py.process.cmdexec("lsof -p %d" % pid)
+ len1 = len([x for x in out.split("\n") if "REG" in x])
+ len2 = len([x for x in out2.split("\n") if "REG" in x])
+ assert len2 < len1 + 3, out2
+
+class TestFDCapture:
+ pytestmark = needsdup
+
+ def test_not_now(self, tmpfile):
+ fd = tmpfile.fileno()
+ cap = py.io.FDCapture(fd, now=False)
+ data = tobytes("hello")
+ os.write(fd, data)
+ f = cap.done()
+ s = f.read()
+ assert not s
+ cap = py.io.FDCapture(fd, now=False)
+ cap.start()
+ os.write(fd, data)
+ f = cap.done()
+ s = f.read()
+ assert s == "hello"
+
+ def test_simple(self, tmpfile):
+ fd = tmpfile.fileno()
+ cap = py.io.FDCapture(fd)
+ data = tobytes("hello")
+ os.write(fd, data)
+ f = cap.done()
+ s = f.read()
+ assert s == "hello"
+ f.close()
+
+ def test_simple_many(self, tmpfile):
+ for i in range(10):
+ self.test_simple(tmpfile)
+
+ def test_simple_many_check_open_files(self, tmpfile):
+ lsof_check(lambda: self.test_simple_many(tmpfile))
+
+ def test_simple_fail_second_start(self, tmpfile):
+ fd = tmpfile.fileno()
+ cap = py.io.FDCapture(fd)
+ f = cap.done()
+ py.test.raises(ValueError, cap.start)
+ f.close()
+
+ def test_stderr(self):
+ cap = py.io.FDCapture(2, patchsys=True)
+ print_("hello", file=sys.stderr)
+ f = cap.done()
+ s = f.read()
+ assert s == "hello\n"
+
+ def test_stdin(self, tmpfile):
+ tmpfile.write(tobytes("3"))
+ tmpfile.seek(0)
+ cap = py.io.FDCapture(0, tmpfile=tmpfile)
+ # check with os.read() directly instead of raw_input(), because
+ # sys.stdin itself may be redirected (as py.test now does by default)
+ x = os.read(0, 100).strip()
+ f = cap.done()
+ assert x == tobytes("3")
+
+ def test_writeorg(self, tmpfile):
+ data1, data2 = tobytes("foo"), tobytes("bar")
+ try:
+ cap = py.io.FDCapture(tmpfile.fileno())
+ tmpfile.write(data1)
+ cap.writeorg(data2)
+ finally:
+ tmpfile.close()
+ f = cap.done()
+ scap = f.read()
+ assert scap == totext(data1)
+ stmp = open(tmpfile.name, 'rb').read()
+ assert stmp == data2
+
+
+class TestStdCapture:
+ def getcapture(self, **kw):
+ return py.io.StdCapture(**kw)
+
+ def test_capturing_done_simple(self):
+ cap = self.getcapture()
+ sys.stdout.write("hello")
+ sys.stderr.write("world")
+ outfile, errfile = cap.done()
+ s = outfile.read()
+ assert s == "hello"
+ s = errfile.read()
+ assert s == "world"
+
+ def test_capturing_reset_simple(self):
+ cap = self.getcapture()
+ print("hello world")
+ sys.stderr.write("hello error\n")
+ out, err = cap.reset()
+ assert out == "hello world\n"
+ assert err == "hello error\n"
+
+ def test_capturing_readouterr(self):
+ cap = self.getcapture()
+ try:
+ print ("hello world")
+ sys.stderr.write("hello error\n")
+ out, err = cap.readouterr()
+ assert out == "hello world\n"
+ assert err == "hello error\n"
+ sys.stderr.write("error2")
+ finally:
+ out, err = cap.reset()
+ assert err == "error2"
+
+ def test_capturing_readouterr_unicode(self):
+ cap = self.getcapture()
+ print ("hx\xc4\x85\xc4\x87")
+ out, err = cap.readouterr()
+ assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8")
+
+ @py.test.mark.skipif('sys.version_info >= (3,)',
+ reason='text output different for bytes on python3')
+ def test_capturing_readouterr_decode_error_handling(self):
+ cap = self.getcapture()
+ # triggered a internal error in pytest
+ print('\xa6')
+ out, err = cap.readouterr()
+ assert out == py.builtin._totext('\ufffd\n', 'unicode-escape')
+
+ def test_capturing_mixed(self):
+ cap = self.getcapture(mixed=True)
+ sys.stdout.write("hello ")
+ sys.stderr.write("world")
+ sys.stdout.write(".")
+ out, err = cap.reset()
+ assert out.strip() == "hello world."
+ assert not err
+
+ def test_reset_twice_error(self):
+ cap = self.getcapture()
+ print ("hello")
+ out, err = cap.reset()
+ py.test.raises(ValueError, cap.reset)
+ assert out == "hello\n"
+ assert not err
+
+ def test_capturing_modify_sysouterr_in_between(self):
+ oldout = sys.stdout
+ olderr = sys.stderr
+ cap = self.getcapture()
+ sys.stdout.write("hello")
+ sys.stderr.write("world")
+ sys.stdout = py.io.TextIO()
+ sys.stderr = py.io.TextIO()
+ print ("not seen")
+ sys.stderr.write("not seen\n")
+ out, err = cap.reset()
+ assert out == "hello"
+ assert err == "world"
+ assert sys.stdout == oldout
+ assert sys.stderr == olderr
+
+ def test_capturing_error_recursive(self):
+ cap1 = self.getcapture()
+ print ("cap1")
+ cap2 = self.getcapture()
+ print ("cap2")
+ out2, err2 = cap2.reset()
+ out1, err1 = cap1.reset()
+ assert out1 == "cap1\n"
+ assert out2 == "cap2\n"
+
+ def test_just_out_capture(self):
+ cap = self.getcapture(out=True, err=False)
+ sys.stdout.write("hello")
+ sys.stderr.write("world")
+ out, err = cap.reset()
+ assert out == "hello"
+ assert not err
+
+ def test_just_err_capture(self):
+ cap = self.getcapture(out=False, err=True)
+ sys.stdout.write("hello")
+ sys.stderr.write("world")
+ out, err = cap.reset()
+ assert err == "world"
+ assert not out
+
+ def test_stdin_restored(self):
+ old = sys.stdin
+ cap = self.getcapture(in_=True)
+ newstdin = sys.stdin
+ out, err = cap.reset()
+ assert newstdin != sys.stdin
+ assert sys.stdin is old
+
+ def test_stdin_nulled_by_default(self):
+ print ("XXX this test may well hang instead of crashing")
+ print ("XXX which indicates an error in the underlying capturing")
+ print ("XXX mechanisms")
+ cap = self.getcapture()
+ py.test.raises(IOError, "sys.stdin.read()")
+ out, err = cap.reset()
+
+ def test_suspend_resume(self):
+ cap = self.getcapture(out=True, err=False, in_=False)
+ try:
+ print ("hello")
+ sys.stderr.write("error\n")
+ out, err = cap.suspend()
+ assert out == "hello\n"
+ assert not err
+ print ("in between")
+ sys.stderr.write("in between\n")
+ cap.resume()
+ print ("after")
+ sys.stderr.write("error_after\n")
+ finally:
+ out, err = cap.reset()
+ assert out == "after\n"
+ assert not err
+
+class TestStdCaptureNotNow(TestStdCapture):
+ def getcapture(self, **kw):
+ kw['now'] = False
+ cap = py.io.StdCapture(**kw)
+ cap.startall()
+ return cap
+
+class TestStdCaptureFD(TestStdCapture):
+ pytestmark = needsdup
+
+ def getcapture(self, **kw):
+ return py.io.StdCaptureFD(**kw)
+
+ def test_intermingling(self):
+ cap = self.getcapture()
+ oswritebytes(1, "1")
+ sys.stdout.write(str(2))
+ sys.stdout.flush()
+ oswritebytes(1, "3")
+ oswritebytes(2, "a")
+ sys.stderr.write("b")
+ sys.stderr.flush()
+ oswritebytes(2, "c")
+ out, err = cap.reset()
+ assert out == "123"
+ assert err == "abc"
+
+ def test_callcapture(self):
+ def func(x, y):
+ print (x)
+ sys.stderr.write(str(y))
+ return 42
+
+ res, out, err = py.io.StdCaptureFD.call(func, 3, y=4)
+ assert res == 42
+ assert out.startswith("3")
+ assert err.startswith("4")
+
+ def test_many(self, capfd):
+ def f():
+ for i in range(10):
+ cap = py.io.StdCaptureFD()
+ cap.reset()
+ lsof_check(f)
+
+class TestStdCaptureFDNotNow(TestStdCaptureFD):
+ pytestmark = needsdup
+
+ def getcapture(self, **kw):
+ kw['now'] = False
+ cap = py.io.StdCaptureFD(**kw)
+ cap.startall()
+ return cap
+
+@needsdup
+def test_stdcapture_fd_tmpfile(tmpfile):
+ capfd = py.io.StdCaptureFD(out=tmpfile)
+ os.write(1, "hello".encode("ascii"))
+ os.write(2, "world".encode("ascii"))
+ outf, errf = capfd.done()
+ assert outf == tmpfile
+
+class TestStdCaptureFDinvalidFD:
+ pytestmark = needsdup
+ def test_stdcapture_fd_invalid_fd(self, testdir):
+ testdir.makepyfile("""
+ import py, os
+ def test_stdout():
+ os.close(1)
+ cap = py.io.StdCaptureFD(out=True, err=False, in_=False)
+ cap.done()
+ def test_stderr():
+ os.close(2)
+ cap = py.io.StdCaptureFD(out=False, err=True, in_=False)
+ cap.done()
+ def test_stdin():
+ os.close(0)
+ cap = py.io.StdCaptureFD(out=False, err=False, in_=True)
+ cap.done()
+ """)
+ result = testdir.runpytest("--capture=fd")
+ assert result.ret == 0
+ assert result.parseoutcomes()['passed'] == 3
+
+def test_capture_not_started_but_reset():
+ capsys = py.io.StdCapture(now=False)
+ capsys.done()
+ capsys.done()
+ capsys.reset()
+
+@needsdup
+def test_capture_no_sys():
+ capsys = py.io.StdCapture()
+ try:
+ cap = py.io.StdCaptureFD(patchsys=False)
+ sys.stdout.write("hello")
+ sys.stderr.write("world")
+ oswritebytes(1, "1")
+ oswritebytes(2, "2")
+ out, err = cap.reset()
+ assert out == "1"
+ assert err == "2"
+ finally:
+ capsys.reset()
+
+@needsdup
+def test_callcapture_nofd():
+ def func(x, y):
+ oswritebytes(1, "hello")
+ oswritebytes(2, "hello")
+ print (x)
+ sys.stderr.write(str(y))
+ return 42
+
+ capfd = py.io.StdCaptureFD(patchsys=False)
+ try:
+ res, out, err = py.io.StdCapture.call(func, 3, y=4)
+ finally:
+ capfd.reset()
+ assert res == 42
+ assert out.startswith("3")
+ assert err.startswith("4")
+
+@needsdup
+@py.test.mark.parametrize('use', [True, False])
+def test_fdcapture_tmpfile_remains_the_same(tmpfile, use):
+ if not use:
+ tmpfile = True
+ cap = py.io.StdCaptureFD(out=False, err=tmpfile, now=False)
+ cap.startall()
+ capfile = cap.err.tmpfile
+ cap.suspend()
+ cap.resume()
+ capfile2 = cap.err.tmpfile
+ assert capfile2 == capfile
+
+@py.test.mark.parametrize('method', ['StdCapture', 'StdCaptureFD'])
+def test_capturing_and_logging_fundamentals(testdir, method):
+ if method == "StdCaptureFD" and not hasattr(os, 'dup'):
+ py.test.skip("need os.dup")
+ # here we check a fundamental feature
+ p = testdir.makepyfile("""
+ import sys, os
+ import py, logging
+ cap = py.io.%s(out=False, in_=False)
+
+ logging.warn("hello1")
+ outerr = cap.suspend()
+ print ("suspend, captured %%s" %%(outerr,))
+ logging.warn("hello2")
+
+ cap.resume()
+ logging.warn("hello3")
+
+ outerr = cap.suspend()
+ print ("suspend2, captured %%s" %% (outerr,))
+ """ % (method,))
+ result = testdir.runpython(p)
+ result.stdout.fnmatch_lines([
+ "suspend, captured*hello1*",
+ "suspend2, captured*hello2*WARNING:root:hello3*",
+ ])
+ assert "atexit" not in result.stderr.str()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_saferepr.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_saferepr.py
new file mode 100644
index 00000000000..97be1416fec
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_saferepr.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import generators
+import py
+import sys
+
+saferepr = py.io.saferepr
+
+class TestSafeRepr:
+ def test_simple_repr(self):
+ assert saferepr(1) == '1'
+ assert saferepr(None) == 'None'
+
+ def test_maxsize(self):
+ s = saferepr('x'*50, maxsize=25)
+ assert len(s) == 25
+ expected = repr('x'*10 + '...' + 'x'*10)
+ assert s == expected
+
+ def test_maxsize_error_on_instance(self):
+ class A:
+ def __repr__(self):
+ raise ValueError('...')
+
+ s = saferepr(('*'*50, A()), maxsize=25)
+ assert len(s) == 25
+ assert s[0] == '(' and s[-1] == ')'
+
+ def test_exceptions(self):
+ class BrokenRepr:
+ def __init__(self, ex):
+ self.ex = ex
+ foo = 0
+ def __repr__(self):
+ raise self.ex
+ class BrokenReprException(Exception):
+ __str__ = None
+ __repr__ = None
+ assert 'Exception' in saferepr(BrokenRepr(Exception("broken")))
+ s = saferepr(BrokenReprException("really broken"))
+ assert 'TypeError' in s
+ assert 'TypeError' in saferepr(BrokenRepr("string"))
+
+ s2 = saferepr(BrokenRepr(BrokenReprException('omg even worse')))
+ assert 'NameError' not in s2
+ assert 'unknown' in s2
+
+ def test_big_repr(self):
+ from py._io.saferepr import SafeRepr
+ assert len(saferepr(range(1000))) <= \
+ len('[' + SafeRepr().maxlist * "1000" + ']')
+
+ def test_repr_on_newstyle(self):
+ class Function(object):
+ def __repr__(self):
+ return "<%s>" %(self.name)
+ try:
+ s = saferepr(Function())
+ except Exception:
+ py.test.fail("saferepr failed for newstyle class")
+
+ def test_unicode(self):
+ val = py.builtin._totext('£€', 'utf-8')
+ reprval = py.builtin._totext("'£€'", 'utf-8')
+ assert saferepr(val) == reprval
+
+def test_unicode_handling():
+ value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
+ def f():
+ raise Exception(value)
+ excinfo = py.test.raises(Exception, f)
+ s = str(excinfo)
+ if sys.version_info[0] < 3:
+ u = unicode(excinfo)
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_terminalwriter.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_terminalwriter.py
new file mode 100644
index 00000000000..7e9ebf409ec
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/io_/test_terminalwriter.py
@@ -0,0 +1,292 @@
+
+import py
+import os, sys
+from py._io import terminalwriter
+import codecs
+import pytest
+
+def test_get_terminal_width():
+ x = py.io.get_terminal_width
+ assert x == terminalwriter.get_terminal_width
+
+def test_getdimensions(monkeypatch):
+ fcntl = py.test.importorskip("fcntl")
+ import struct
+ l = []
+ monkeypatch.setattr(fcntl, 'ioctl', lambda *args: l.append(args))
+ try:
+ terminalwriter._getdimensions()
+ except (TypeError, struct.error):
+ pass
+ assert len(l) == 1
+ assert l[0][0] == 1
+
+def test_terminal_width_COLUMNS(monkeypatch):
+ """ Dummy test for get_terminal_width
+ """
+ fcntl = py.test.importorskip("fcntl")
+ monkeypatch.setattr(fcntl, 'ioctl', lambda *args: int('x'))
+ monkeypatch.setenv('COLUMNS', '42')
+ assert terminalwriter.get_terminal_width() == 42
+ monkeypatch.delenv('COLUMNS', raising=False)
+
+def test_terminalwriter_defaultwidth_80(monkeypatch):
+ monkeypatch.setattr(terminalwriter, '_getdimensions', lambda: 0/0)
+ monkeypatch.delenv('COLUMNS', raising=False)
+ tw = py.io.TerminalWriter()
+ assert tw.fullwidth == 80
+
+def test_terminalwriter_getdimensions_bogus(monkeypatch):
+ monkeypatch.setattr(terminalwriter, '_getdimensions', lambda: (10,10))
+ monkeypatch.delenv('COLUMNS', raising=False)
+ tw = py.io.TerminalWriter()
+ assert tw.fullwidth == 80
+
+def test_terminalwriter_getdimensions_emacs(monkeypatch):
+ # emacs terminal returns (0,0) but set COLUMNS properly
+ monkeypatch.setattr(terminalwriter, '_getdimensions', lambda: (0,0))
+ monkeypatch.setenv('COLUMNS', '42')
+ tw = py.io.TerminalWriter()
+ assert tw.fullwidth == 42
+
+def test_terminalwriter_computes_width(monkeypatch):
+ monkeypatch.setattr(terminalwriter, 'get_terminal_width', lambda: 42)
+ tw = py.io.TerminalWriter()
+ assert tw.fullwidth == 42
+
+def test_terminalwriter_default_instantiation():
+ tw = py.io.TerminalWriter(stringio=True)
+ assert hasattr(tw, 'stringio')
+
+def test_terminalwriter_dumb_term_no_markup(monkeypatch):
+ monkeypatch.setattr(os, 'environ', {'TERM': 'dumb', 'PATH': ''})
+ class MyFile:
+ closed = False
+ def isatty(self):
+ return True
+ monkeypatch.setattr(sys, 'stdout', MyFile())
+ try:
+ assert sys.stdout.isatty()
+ tw = py.io.TerminalWriter()
+ assert not tw.hasmarkup
+ finally:
+ monkeypatch.undo()
+
+def test_terminalwriter_file_unicode(tmpdir):
+ f = codecs.open(str(tmpdir.join("xyz")), "wb", "utf8")
+ tw = py.io.TerminalWriter(file=f)
+ assert tw.encoding == "utf8"
+
+def test_unicode_encoding():
+ msg = py.builtin._totext('b\u00f6y', 'utf8')
+ for encoding in 'utf8', 'latin1':
+ l = []
+ tw = py.io.TerminalWriter(l.append, encoding=encoding)
+ tw.line(msg)
+ assert l[0].strip() == msg.encode(encoding)
+
+@pytest.mark.parametrize("encoding", ["ascii"])
+def test_unicode_on_file_with_ascii_encoding(tmpdir, monkeypatch, encoding):
+ msg = py.builtin._totext('hell\xf6', "latin1")
+ #pytest.raises(UnicodeEncodeError, lambda: bytes(msg))
+ f = codecs.open(str(tmpdir.join("x")), "w", encoding)
+ tw = py.io.TerminalWriter(f)
+ tw.line(msg)
+ f.close()
+ s = tmpdir.join("x").open("rb").read().strip()
+ assert encoding == "ascii"
+ assert s == msg.encode("unicode-escape")
+
+
+win32 = int(sys.platform == "win32")
+class TestTerminalWriter:
+ def pytest_generate_tests(self, metafunc):
+ if "tw" in metafunc.funcargnames:
+ metafunc.addcall(id="path", param="path")
+ metafunc.addcall(id="stringio", param="stringio")
+ metafunc.addcall(id="callable", param="callable")
+ def pytest_funcarg__tw(self, request):
+ if request.param == "path":
+ tmpdir = request.getfuncargvalue("tmpdir")
+ p = tmpdir.join("tmpfile")
+ f = codecs.open(str(p), 'w+', encoding='utf8')
+ tw = py.io.TerminalWriter(f)
+ def getlines():
+ tw._file.flush()
+ return codecs.open(str(p), 'r',
+ encoding='utf8').readlines()
+ elif request.param == "stringio":
+ tw = py.io.TerminalWriter(stringio=True)
+ def getlines():
+ tw.stringio.seek(0)
+ return tw.stringio.readlines()
+ elif request.param == "callable":
+ writes = []
+ tw = py.io.TerminalWriter(writes.append)
+ def getlines():
+ io = py.io.TextIO()
+ io.write("".join(writes))
+ io.seek(0)
+ return io.readlines()
+ tw.getlines = getlines
+ tw.getvalue = lambda: "".join(getlines())
+ return tw
+
+ def test_line(self, tw):
+ tw.line("hello")
+ l = tw.getlines()
+ assert len(l) == 1
+ assert l[0] == "hello\n"
+
+ def test_line_unicode(self, tw):
+ for encoding in 'utf8', 'latin1':
+ tw._encoding = encoding
+ msg = py.builtin._totext('b\u00f6y', 'utf8')
+ tw.line(msg)
+ l = tw.getlines()
+ assert l[0] == msg + "\n"
+
+ def test_sep_no_title(self, tw):
+ tw.sep("-", fullwidth=60)
+ l = tw.getlines()
+ assert len(l) == 1
+ assert l[0] == "-" * (60-win32) + "\n"
+
+ def test_sep_with_title(self, tw):
+ tw.sep("-", "hello", fullwidth=60)
+ l = tw.getlines()
+ assert len(l) == 1
+ assert l[0] == "-" * 26 + " hello " + "-" * (27-win32) + "\n"
+
+ @py.test.mark.skipif("sys.platform == 'win32'")
+ def test__escaped(self, tw):
+ text2 = tw._escaped("hello", (31))
+ assert text2.find("hello") != -1
+
+ @py.test.mark.skipif("sys.platform == 'win32'")
+ def test_markup(self, tw):
+ for bold in (True, False):
+ for color in ("red", "green"):
+ text2 = tw.markup("hello", **{color: True, 'bold': bold})
+ assert text2.find("hello") != -1
+ py.test.raises(ValueError, "tw.markup('x', wronkw=3)")
+ py.test.raises(ValueError, "tw.markup('x', wronkw=0)")
+
+ def test_line_write_markup(self, tw):
+ tw.hasmarkup = True
+ tw.line("x", bold=True)
+ tw.write("x\n", red=True)
+ l = tw.getlines()
+ if sys.platform != "win32":
+ assert len(l[0]) >= 2, l
+ assert len(l[1]) >= 2, l
+
+ def test_attr_fullwidth(self, tw):
+ tw.sep("-", "hello", fullwidth=70)
+ tw.fullwidth = 70
+ tw.sep("-", "hello")
+ l = tw.getlines()
+ assert len(l[0]) == len(l[1])
+
+ def test_reline(self, tw):
+ tw.line("hello")
+ tw.hasmarkup = False
+ pytest.raises(ValueError, lambda: tw.reline("x"))
+ tw.hasmarkup = True
+ tw.reline("0 1 2")
+ tw.getlines()
+ l = tw.getvalue().split("\n")
+ assert len(l) == 2
+ tw.reline("0 1 3")
+ l = tw.getvalue().split("\n")
+ assert len(l) == 2
+ assert l[1].endswith("0 1 3\r")
+ tw.line("so")
+ l = tw.getvalue().split("\n")
+ assert len(l) == 3
+ assert l[-1] == ""
+ assert l[1] == ("0 1 2\r0 1 3\rso ")
+ assert l[0] == "hello"
+
+
+def test_terminal_with_callable_write_and_flush():
+ l = set()
+ class fil:
+ flush = lambda self: l.add("1")
+ write = lambda self, x: l.add("1")
+ __call__ = lambda self, x: l.add("2")
+
+ tw = py.io.TerminalWriter(fil())
+ tw.line("hello")
+ assert l == set(["1"])
+ del fil.flush
+ l.clear()
+ tw = py.io.TerminalWriter(fil())
+ tw.line("hello")
+ assert l == set(["2"])
+
+
+def test_chars_on_current_line():
+ tw = py.io.TerminalWriter(stringio=True)
+
+ written = []
+
+ def write_and_check(s, expected):
+ tw.write(s, bold=True)
+ written.append(s)
+ assert tw.chars_on_current_line == expected
+ assert tw.stringio.getvalue() == ''.join(written)
+
+ write_and_check('foo', 3)
+ write_and_check('bar', 6)
+ write_and_check('\n', 0)
+ write_and_check('\n', 0)
+ write_and_check('\n\n\n', 0)
+ write_and_check('\nfoo', 3)
+ write_and_check('\nfbar\nhello', 5)
+ write_and_check('10', 7)
+
+
+@pytest.mark.skipif(sys.platform == "win32", reason="win32 has no native ansi")
+def test_attr_hasmarkup():
+ tw = py.io.TerminalWriter(stringio=True)
+ assert not tw.hasmarkup
+ tw.hasmarkup = True
+ tw.line("hello", bold=True)
+ s = tw.stringio.getvalue()
+ assert len(s) > len("hello\n")
+ assert '\x1b[1m' in s
+ assert '\x1b[0m' in s
+
+@pytest.mark.skipif(sys.platform == "win32", reason="win32 has no native ansi")
+def test_ansi_print():
+ # we have no easy way to construct a file that
+ # represents a terminal
+ f = py.io.TextIO()
+ f.isatty = lambda: True
+ py.io.ansi_print("hello", 0x32, file=f)
+ text2 = f.getvalue()
+ assert text2.find("hello") != -1
+ assert len(text2) >= len("hello\n")
+ assert '\x1b[50m' in text2
+ assert '\x1b[0m' in text2
+
+def test_should_do_markup_PY_COLORS_eq_1(monkeypatch):
+ monkeypatch.setitem(os.environ, 'PY_COLORS', '1')
+ tw = py.io.TerminalWriter(stringio=True)
+ assert tw.hasmarkup
+ tw.line("hello", bold=True)
+ s = tw.stringio.getvalue()
+ assert len(s) > len("hello\n")
+ assert '\x1b[1m' in s
+ assert '\x1b[0m' in s
+
+def test_should_do_markup_PY_COLORS_eq_0(monkeypatch):
+ monkeypatch.setitem(os.environ, 'PY_COLORS', '0')
+ f = py.io.TextIO()
+ f.isatty = lambda: True
+ tw = py.io.TerminalWriter(file=f)
+ assert not tw.hasmarkup
+ tw.line("hello", bold=True)
+ s = f.getvalue()
+ assert s == "hello\n"
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/log/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/log/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/log/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/log/test_log.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/log/test_log.py
new file mode 100644
index 00000000000..5c706d9b6ad
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/log/test_log.py
@@ -0,0 +1,191 @@
+import py
+
+from py._log.log import default_keywordmapper
+
+callcapture = py.io.StdCapture.call
+
+
+def setup_module(mod):
+ mod._oldstate = default_keywordmapper.getstate()
+
+def teardown_module(mod):
+ default_keywordmapper.setstate(mod._oldstate)
+
+class TestLogProducer:
+ def setup_method(self, meth):
+ from py._log.log import default_keywordmapper
+ default_keywordmapper.setstate(_oldstate)
+
+ def test_getstate_setstate(self):
+ state = py.log._getstate()
+ py.log.setconsumer("hello", [].append)
+ state2 = py.log._getstate()
+ assert state2 != state
+ py.log._setstate(state)
+ state3 = py.log._getstate()
+ assert state3 == state
+
+ def test_producer_repr(self):
+ d = py.log.Producer("default")
+ assert repr(d).find('default') != -1
+
+ def test_produce_one_keyword(self):
+ l = []
+ py.log.setconsumer('s1', l.append)
+ py.log.Producer('s1')("hello world")
+ assert len(l) == 1
+ msg = l[0]
+ assert msg.content().startswith('hello world')
+ assert msg.prefix() == '[s1] '
+ assert str(msg) == "[s1] hello world"
+
+ def test_producer_class(self):
+ p = py.log.Producer('x1')
+ l = []
+ py.log.setconsumer(p._keywords, l.append)
+ p("hello")
+ assert len(l) == 1
+ assert len(l[0].keywords) == 1
+ assert 'x1' == l[0].keywords[0]
+
+ def test_producer_caching(self):
+ p = py.log.Producer('x1')
+ x2 = p.x2
+ assert x2 is p.x2
+
+class TestLogConsumer:
+ def setup_method(self, meth):
+ default_keywordmapper.setstate(_oldstate)
+ def test_log_none(self):
+ log = py.log.Producer("XXX")
+ l = []
+ py.log.setconsumer('XXX', l.append)
+ log("1")
+ assert l
+ l[:] = []
+ py.log.setconsumer('XXX', None)
+ log("2")
+ assert not l
+
+ def test_log_default_stderr(self):
+ res, out, err = callcapture(py.log.Producer("default"), "hello")
+ assert err.strip() == "[default] hello"
+
+ def test_simple_consumer_match(self):
+ l = []
+ py.log.setconsumer("x1", l.append)
+ p = py.log.Producer("x1 x2")
+ p("hello")
+ assert l
+ assert l[0].content() == "hello"
+
+ def test_simple_consumer_match_2(self):
+ l = []
+ p = py.log.Producer("x1 x2")
+ py.log.setconsumer(p._keywords, l.append)
+ p("42")
+ assert l
+ assert l[0].content() == "42"
+
+ def test_no_auto_producer(self):
+ p = py.log.Producer('x')
+ py.test.raises(AttributeError, "p._x")
+ py.test.raises(AttributeError, "p.x_y")
+
+ def test_setconsumer_with_producer(self):
+ l = []
+ p = py.log.Producer("hello")
+ py.log.setconsumer(p, l.append)
+ p("world")
+ assert str(l[0]) == "[hello] world"
+
+ def test_multi_consumer(self):
+ l = []
+ py.log.setconsumer("x1", l.append)
+ py.log.setconsumer("x1 x2", None)
+ p = py.log.Producer("x1 x2")
+ p("hello")
+ assert not l
+ py.log.Producer("x1")("hello")
+ assert l
+ assert l[0].content() == "hello"
+
+ def test_log_stderr(self):
+ py.log.setconsumer("xyz", py.log.STDOUT)
+ res, out, err = callcapture(py.log.Producer("xyz"), "hello")
+ assert not err
+ assert out.strip() == '[xyz] hello'
+
+ def test_log_file(self, tmpdir):
+ customlog = tmpdir.join('log.out')
+ py.log.setconsumer("default", open(str(customlog), 'w', 1))
+ py.log.Producer("default")("hello world #1")
+ assert customlog.readlines() == ['[default] hello world #1\n']
+
+ py.log.setconsumer("default", py.log.Path(customlog, buffering=False))
+ py.log.Producer("default")("hello world #2")
+ res = customlog.readlines()
+ assert res == ['[default] hello world #2\n'] # no append by default!
+
+ def test_log_file_append_mode(self, tmpdir):
+ logfilefn = tmpdir.join('log_append.out')
+
+ # The append mode is on by default, so we don't need to specify it for File
+ py.log.setconsumer("default", py.log.Path(logfilefn, append=True,
+ buffering=0))
+ assert logfilefn.check()
+ py.log.Producer("default")("hello world #1")
+ lines = logfilefn.readlines()
+ assert lines == ['[default] hello world #1\n']
+ py.log.setconsumer("default", py.log.Path(logfilefn, append=True,
+ buffering=0))
+ py.log.Producer("default")("hello world #1")
+ lines = logfilefn.readlines()
+ assert lines == ['[default] hello world #1\n',
+ '[default] hello world #1\n']
+
+ def test_log_file_delayed_create(self, tmpdir):
+ logfilefn = tmpdir.join('log_create.out')
+
+ py.log.setconsumer("default", py.log.Path(logfilefn,
+ delayed_create=True, buffering=0))
+ assert not logfilefn.check()
+ py.log.Producer("default")("hello world #1")
+ lines = logfilefn.readlines()
+ assert lines == ['[default] hello world #1\n']
+
+ def test_keyword_based_log_files(self, tmpdir):
+ logfiles = []
+ keywords = 'k1 k2 k3'.split()
+ for key in keywords:
+ path = tmpdir.join(key)
+ py.log.setconsumer(key, py.log.Path(path, buffering=0))
+
+ py.log.Producer('k1')('1')
+ py.log.Producer('k2')('2')
+ py.log.Producer('k3')('3')
+
+ for key in keywords:
+ path = tmpdir.join(key)
+ assert path.read().strip() == '[%s] %s' % (key, key[-1])
+
+ # disabled for now; the syslog log file can usually be read only by root
+ # I manually inspected /var/log/messages and the entries were there
+ def no_test_log_syslog(self):
+ py.log.setconsumer("default", py.log.Syslog())
+ py.log.default("hello world #1")
+
+ # disabled for now until I figure out how to read entries in the
+ # Event Logs on Windows
+ # I manually inspected the Application Log and the entries were there
+ def no_test_log_winevent(self):
+ py.log.setconsumer("default", py.log.WinEvent())
+ py.log.default("hello world #1")
+
+ # disabled for now until I figure out how to properly pass the parameters
+ def no_test_log_email(self):
+ py.log.setconsumer("default", py.log.Email(mailhost="gheorghiu.net",
+ fromaddr="grig",
+ toaddrs="grig",
+ subject = "py.log email"))
+ py.log.default("hello world #1")
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/log/test_warning.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/log/test_warning.py
new file mode 100644
index 00000000000..a460c319e87
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/log/test_warning.py
@@ -0,0 +1,86 @@
+import sys
+from distutils.version import LooseVersion
+
+import pytest
+
+import py
+
+mypath = py.path.local(__file__).new(ext=".py")
+
+
+win = sys.platform.startswith('win')
+pytestmark = pytest.mark.skipif(win and LooseVersion(pytest.__version__) >= LooseVersion('3.1'),
+ reason='apiwarn is not compatible with pytest >= 3.1 (#162)')
+
+
+@pytest.mark.xfail
+def test_forwarding_to_warnings_module():
+ pytest.deprecated_call(py.log._apiwarn, "1.3", "..")
+
+def test_apiwarn_functional(recwarn):
+ capture = py.io.StdCapture()
+ py.log._apiwarn("x.y.z", "something", stacklevel=1)
+ out, err = capture.reset()
+ py.builtin.print_("out", out)
+ py.builtin.print_("err", err)
+ assert err.find("x.y.z") != -1
+ lno = py.code.getrawcode(test_apiwarn_functional).co_firstlineno + 2
+ exp = "%s:%s" % (mypath, lno)
+ assert err.find(exp) != -1
+
+def test_stacklevel(recwarn):
+ def f():
+ py.log._apiwarn("x", "some", stacklevel=2)
+ # 3
+ # 4
+ capture = py.io.StdCapture()
+ f()
+ out, err = capture.reset()
+ lno = py.code.getrawcode(test_stacklevel).co_firstlineno + 6
+ warning = str(err)
+ assert warning.find(":%s" % lno) != -1
+
+def test_stacklevel_initpkg_with_resolve(testdir, recwarn):
+ testdir.makepyfile(modabc="""
+ import py
+ def f():
+ py.log._apiwarn("x", "some", stacklevel="apipkg123")
+ """)
+ testdir.makepyfile(apipkg123="""
+ def __getattr__():
+ import modabc
+ modabc.f()
+ """)
+ p = testdir.makepyfile("""
+ import apipkg123
+ apipkg123.__getattr__()
+ """)
+ capture = py.io.StdCapture()
+ p.pyimport()
+ out, err = capture.reset()
+ warning = str(err)
+ loc = 'test_stacklevel_initpkg_with_resolve.py:2'
+ assert warning.find(loc) != -1
+
+def test_stacklevel_initpkg_no_resolve(recwarn):
+ def f():
+ py.log._apiwarn("x", "some", stacklevel="apipkg")
+ capture = py.io.StdCapture()
+ f()
+ out, err = capture.reset()
+ lno = py.code.getrawcode(test_stacklevel_initpkg_no_resolve).co_firstlineno + 2
+ warning = str(err)
+ assert warning.find(":%s" % lno) != -1
+
+
+def test_function(recwarn):
+ capture = py.io.StdCapture()
+ py.log._apiwarn("x.y.z", "something", function=test_function)
+ out, err = capture.reset()
+ py.builtin.print_("out", out)
+ py.builtin.print_("err", err)
+ assert err.find("x.y.z") != -1
+ lno = py.code.getrawcode(test_function).co_firstlineno
+ exp = "%s:%s" % (mypath, lno)
+ assert err.find(exp) != -1
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/common.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/common.py
new file mode 100644
index 00000000000..d69a1c39d09
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/common.py
@@ -0,0 +1,492 @@
+import py
+import sys
+
+import pytest
+
+class CommonFSTests(object):
+ def test_constructor_equality(self, path1):
+ p = path1.__class__(path1)
+ assert p == path1
+
+ def test_eq_nonstring(self, path1):
+ p1 = path1.join('sampledir')
+ p2 = path1.join('sampledir')
+ assert p1 == p2
+
+ def test_new_identical(self, path1):
+ assert path1 == path1.new()
+
+ def test_join(self, path1):
+ p = path1.join('sampledir')
+ strp = str(p)
+ assert strp.endswith('sampledir')
+ assert strp.startswith(str(path1))
+
+ def test_join_normalized(self, path1):
+ newpath = path1.join(path1.sep+'sampledir')
+ strp = str(newpath)
+ assert strp.endswith('sampledir')
+ assert strp.startswith(str(path1))
+ newpath = path1.join((path1.sep*2) + 'sampledir')
+ strp = str(newpath)
+ assert strp.endswith('sampledir')
+ assert strp.startswith(str(path1))
+
+ def test_join_noargs(self, path1):
+ newpath = path1.join()
+ assert path1 == newpath
+
+ def test_add_something(self, path1):
+ p = path1.join('sample')
+ p = p + 'dir'
+ assert p.check()
+ assert p.exists()
+ assert p.isdir()
+ assert not p.isfile()
+
+ def test_parts(self, path1):
+ newpath = path1.join('sampledir', 'otherfile')
+ par = newpath.parts()[-3:]
+ assert par == [path1, path1.join('sampledir'), newpath]
+
+ revpar = newpath.parts(reverse=True)[:3]
+ assert revpar == [newpath, path1.join('sampledir'), path1]
+
+ def test_common(self, path1):
+ other = path1.join('sampledir')
+ x = other.common(path1)
+ assert x == path1
+
+ #def test_parents_nonexisting_file(self, path1):
+ # newpath = path1 / 'dirnoexist' / 'nonexisting file'
+ # par = list(newpath.parents())
+ # assert par[:2] == [path1 / 'dirnoexist', path1]
+
+ def test_basename_checks(self, path1):
+ newpath = path1.join('sampledir')
+ assert newpath.check(basename='sampledir')
+ assert newpath.check(notbasename='xyz')
+ assert newpath.basename == 'sampledir'
+
+ def test_basename(self, path1):
+ newpath = path1.join('sampledir')
+ assert newpath.check(basename='sampledir')
+ assert newpath.basename, 'sampledir'
+
+ def test_dirname(self, path1):
+ newpath = path1.join('sampledir')
+ assert newpath.dirname == str(path1)
+
+ def test_dirpath(self, path1):
+ newpath = path1.join('sampledir')
+ assert newpath.dirpath() == path1
+
+ def test_dirpath_with_args(self, path1):
+ newpath = path1.join('sampledir')
+ assert newpath.dirpath('x') == path1.join('x')
+
+ def test_newbasename(self, path1):
+ newpath = path1.join('samplefile')
+ newbase = newpath.new(basename="samplefile2")
+ assert newbase.basename == "samplefile2"
+ assert newbase.dirpath() == newpath.dirpath()
+
+ def test_not_exists(self, path1):
+ assert not path1.join('does_not_exist').check()
+ assert path1.join('does_not_exist').check(exists=0)
+
+ def test_exists(self, path1):
+ assert path1.join("samplefile").check()
+ assert path1.join("samplefile").check(exists=1)
+ assert path1.join("samplefile").exists()
+ assert path1.join("samplefile").isfile()
+ assert not path1.join("samplefile").isdir()
+
+ def test_dir(self, path1):
+ #print repr(path1.join("sampledir"))
+ assert path1.join("sampledir").check(dir=1)
+ assert path1.join('samplefile').check(notdir=1)
+ assert not path1.join("samplefile").check(dir=1)
+ assert path1.join("samplefile").exists()
+ assert not path1.join("samplefile").isdir()
+ assert path1.join("samplefile").isfile()
+
+ def test_fnmatch_file(self, path1):
+ assert path1.join("samplefile").check(fnmatch='s*e')
+ assert path1.join("samplefile").fnmatch('s*e')
+ assert not path1.join("samplefile").fnmatch('s*x')
+ assert not path1.join("samplefile").check(fnmatch='s*x')
+
+ #def test_fnmatch_dir(self, path1):
+
+ # pattern = path1.sep.join(['s*file'])
+ # sfile = path1.join("samplefile")
+ # assert sfile.check(fnmatch=pattern)
+
+ def test_relto(self, path1):
+ l=path1.join("sampledir", "otherfile")
+ assert l.relto(path1) == l.sep.join(["sampledir", "otherfile"])
+ assert l.check(relto=path1)
+ assert path1.check(notrelto=l)
+ assert not path1.check(relto=l)
+
+ def test_bestrelpath(self, path1):
+ curdir = path1
+ sep = curdir.sep
+ s = curdir.bestrelpath(curdir)
+ assert s == "."
+ s = curdir.bestrelpath(curdir.join("hello", "world"))
+ assert s == "hello" + sep + "world"
+
+ s = curdir.bestrelpath(curdir.dirpath().join("sister"))
+ assert s == ".." + sep + "sister"
+ assert curdir.bestrelpath(curdir.dirpath()) == ".."
+
+ assert curdir.bestrelpath("hello") == "hello"
+
+ def test_relto_not_relative(self, path1):
+ l1=path1.join("bcde")
+ l2=path1.join("b")
+ assert not l1.relto(l2)
+ assert not l2.relto(l1)
+
+ @py.test.mark.xfail("sys.platform.startswith('java')")
+ def test_listdir(self, path1):
+ l = path1.listdir()
+ assert path1.join('sampledir') in l
+ assert path1.join('samplefile') in l
+ py.test.raises(py.error.ENOTDIR,
+ "path1.join('samplefile').listdir()")
+
+ def test_listdir_fnmatchstring(self, path1):
+ l = path1.listdir('s*dir')
+ assert len(l)
+ assert l[0], path1.join('sampledir')
+
+ def test_listdir_filter(self, path1):
+ l = path1.listdir(lambda x: x.check(dir=1))
+ assert path1.join('sampledir') in l
+ assert not path1.join('samplefile') in l
+
+ def test_listdir_sorted(self, path1):
+ l = path1.listdir(lambda x: x.check(basestarts="sample"), sort=True)
+ assert path1.join('sampledir') == l[0]
+ assert path1.join('samplefile') == l[1]
+ assert path1.join('samplepickle') == l[2]
+
+ def test_visit_nofilter(self, path1):
+ l = []
+ for i in path1.visit():
+ l.append(i.relto(path1))
+ assert "sampledir" in l
+ assert path1.sep.join(["sampledir", "otherfile"]) in l
+
+ def test_visit_norecurse(self, path1):
+ l = []
+ for i in path1.visit(None, lambda x: x.basename != "sampledir"):
+ l.append(i.relto(path1))
+ assert "sampledir" in l
+ assert not path1.sep.join(["sampledir", "otherfile"]) in l
+
+ @pytest.mark.parametrize('fil', ['*dir', u'*dir',
+ pytest.mark.skip("sys.version_info <"
+ " (3,6)")(b'*dir')])
+ def test_visit_filterfunc_is_string(self, path1, fil):
+ l = []
+ for i in path1.visit(fil):
+ l.append(i.relto(path1))
+ assert len(l), 2
+ assert "sampledir" in l
+ assert "otherdir" in l
+
+ @py.test.mark.xfail("sys.platform.startswith('java')")
+ def test_visit_ignore(self, path1):
+ p = path1.join('nonexisting')
+ assert list(p.visit(ignore=py.error.ENOENT)) == []
+
+ def test_visit_endswith(self, path1):
+ l = []
+ for i in path1.visit(lambda x: x.check(endswith="file")):
+ l.append(i.relto(path1))
+ assert path1.sep.join(["sampledir", "otherfile"]) in l
+ assert "samplefile" in l
+
+ def test_endswith(self, path1):
+ assert path1.check(notendswith='.py')
+ x = path1.join('samplefile')
+ assert x.check(endswith='file')
+
+ def test_cmp(self, path1):
+ path1 = path1.join('samplefile')
+ path2 = path1.join('samplefile2')
+ assert (path1 < path2) == ('samplefile' < 'samplefile2')
+ assert not (path1 < path1)
+
+ def test_simple_read(self, path1):
+ x = path1.join('samplefile').read('r')
+ assert x == 'samplefile\n'
+
+ def test_join_div_operator(self, path1):
+ newpath = path1 / '/sampledir' / '/test//'
+ newpath2 = path1.join('sampledir', 'test')
+ assert newpath == newpath2
+
+ def test_ext(self, path1):
+ newpath = path1.join('sampledir.ext')
+ assert newpath.ext == '.ext'
+ newpath = path1.join('sampledir')
+ assert not newpath.ext
+
+ def test_purebasename(self, path1):
+ newpath = path1.join('samplefile.py')
+ assert newpath.purebasename == 'samplefile'
+
+ def test_multiple_parts(self, path1):
+ newpath = path1.join('samplefile.py')
+ dirname, purebasename, basename, ext = newpath._getbyspec(
+ 'dirname,purebasename,basename,ext')
+ assert str(path1).endswith(dirname) # be careful with win32 'drive'
+ assert purebasename == 'samplefile'
+ assert basename == 'samplefile.py'
+ assert ext == '.py'
+
+ def test_dotted_name_ext(self, path1):
+ newpath = path1.join('a.b.c')
+ ext = newpath.ext
+ assert ext == '.c'
+ assert newpath.ext == '.c'
+
+ def test_newext(self, path1):
+ newpath = path1.join('samplefile.py')
+ newext = newpath.new(ext='.txt')
+ assert newext.basename == "samplefile.txt"
+ assert newext.purebasename == "samplefile"
+
+ def test_readlines(self, path1):
+ fn = path1.join('samplefile')
+ contents = fn.readlines()
+ assert contents == ['samplefile\n']
+
+ def test_readlines_nocr(self, path1):
+ fn = path1.join('samplefile')
+ contents = fn.readlines(cr=0)
+ assert contents == ['samplefile', '']
+
+ def test_file(self, path1):
+ assert path1.join('samplefile').check(file=1)
+
+ def test_not_file(self, path1):
+ assert not path1.join("sampledir").check(file=1)
+ assert path1.join("sampledir").check(file=0)
+
+ def test_non_existent(self, path1):
+ assert path1.join("sampledir.nothere").check(dir=0)
+ assert path1.join("sampledir.nothere").check(file=0)
+ assert path1.join("sampledir.nothere").check(notfile=1)
+ assert path1.join("sampledir.nothere").check(notdir=1)
+ assert path1.join("sampledir.nothere").check(notexists=1)
+ assert not path1.join("sampledir.nothere").check(notfile=0)
+
+ # pattern = path1.sep.join(['s*file'])
+ # sfile = path1.join("samplefile")
+ # assert sfile.check(fnmatch=pattern)
+
+ def test_size(self, path1):
+ url = path1.join("samplefile")
+ assert url.size() > len("samplefile")
+
+ def test_mtime(self, path1):
+ url = path1.join("samplefile")
+ assert url.mtime() > 0
+
+ def test_relto_wrong_type(self, path1):
+ py.test.raises(TypeError, "path1.relto(42)")
+
+ def test_load(self, path1):
+ p = path1.join('samplepickle')
+ obj = p.load()
+ assert type(obj) is dict
+ assert obj.get('answer',None) == 42
+
+ def test_visit_filesonly(self, path1):
+ l = []
+ for i in path1.visit(lambda x: x.check(file=1)):
+ l.append(i.relto(path1))
+ assert not "sampledir" in l
+ assert path1.sep.join(["sampledir", "otherfile"]) in l
+
+ def test_visit_nodotfiles(self, path1):
+ l = []
+ for i in path1.visit(lambda x: x.check(dotfile=0)):
+ l.append(i.relto(path1))
+ assert "sampledir" in l
+ assert path1.sep.join(["sampledir", "otherfile"]) in l
+ assert not ".dotfile" in l
+
+ def test_visit_breadthfirst(self, path1):
+ l = []
+ for i in path1.visit(bf=True):
+ l.append(i.relto(path1))
+ for i, p in enumerate(l):
+ if path1.sep in p:
+ for j in range(i, len(l)):
+ assert path1.sep in l[j]
+ break
+ else:
+ py.test.fail("huh")
+
+ def test_visit_sort(self, path1):
+ l = []
+ for i in path1.visit(bf=True, sort=True):
+ l.append(i.relto(path1))
+ for i, p in enumerate(l):
+ if path1.sep in p:
+ break
+ assert l[:i] == sorted(l[:i])
+ assert l[i:] == sorted(l[i:])
+
+ def test_endswith(self, path1):
+ def chk(p):
+ return p.check(endswith="pickle")
+ assert not chk(path1)
+ assert not chk(path1.join('samplefile'))
+ assert chk(path1.join('somepickle'))
+
+ def test_copy_file(self, path1):
+ otherdir = path1.join('otherdir')
+ initpy = otherdir.join('__init__.py')
+ copied = otherdir.join('copied')
+ initpy.copy(copied)
+ try:
+ assert copied.check()
+ s1 = initpy.read()
+ s2 = copied.read()
+ assert s1 == s2
+ finally:
+ if copied.check():
+ copied.remove()
+
+ def test_copy_dir(self, path1):
+ otherdir = path1.join('otherdir')
+ copied = path1.join('newdir')
+ try:
+ otherdir.copy(copied)
+ assert copied.check(dir=1)
+ assert copied.join('__init__.py').check(file=1)
+ s1 = otherdir.join('__init__.py').read()
+ s2 = copied.join('__init__.py').read()
+ assert s1 == s2
+ finally:
+ if copied.check(dir=1):
+ copied.remove(rec=1)
+
+ def test_remove_file(self, path1):
+ d = path1.ensure('todeleted')
+ assert d.check()
+ d.remove()
+ assert not d.check()
+
+ def test_remove_dir_recursive_by_default(self, path1):
+ d = path1.ensure('to', 'be', 'deleted')
+ assert d.check()
+ p = path1.join('to')
+ p.remove()
+ assert not p.check()
+
+ def test_ensure_dir(self, path1):
+ b = path1.ensure_dir("001", "002")
+ assert b.basename == "002"
+ assert b.isdir()
+
+ def test_mkdir_and_remove(self, path1):
+ tmpdir = path1
+ py.test.raises(py.error.EEXIST, tmpdir.mkdir, 'sampledir')
+ new = tmpdir.join('mktest1')
+ new.mkdir()
+ assert new.check(dir=1)
+ new.remove()
+
+ new = tmpdir.mkdir('mktest')
+ assert new.check(dir=1)
+ new.remove()
+ assert tmpdir.join('mktest') == new
+
+ def test_move_file(self, path1):
+ p = path1.join('samplefile')
+ newp = p.dirpath('moved_samplefile')
+ p.move(newp)
+ try:
+ assert newp.check(file=1)
+ assert not p.check()
+ finally:
+ dp = newp.dirpath()
+ if hasattr(dp, 'revert'):
+ dp.revert()
+ else:
+ newp.move(p)
+ assert p.check()
+
+ def test_move_dir(self, path1):
+ source = path1.join('sampledir')
+ dest = path1.join('moveddir')
+ source.move(dest)
+ assert dest.check(dir=1)
+ assert dest.join('otherfile').check(file=1)
+ assert not source.join('sampledir').check()
+
+ def test_fspath_protocol_match_strpath(self, path1):
+ assert path1.__fspath__() == path1.strpath
+
+ def test_fspath_func_match_strpath(self, path1):
+ try:
+ from os import fspath
+ except ImportError:
+ from py._path.common import fspath
+ assert fspath(path1) == path1.strpath
+
+ @py.test.mark.skip("sys.version_info < (3,6)")
+ def test_fspath_open(self, path1):
+ f = path1.join('opentestfile')
+ open(f)
+
+ @py.test.mark.skip("sys.version_info < (3,6)")
+ def test_fspath_fsencode(self, path1):
+ from os import fsencode
+ assert fsencode(path1) == fsencode(path1.strpath)
+
+def setuptestfs(path):
+ if path.join('samplefile').check():
+ return
+ #print "setting up test fs for", repr(path)
+ samplefile = path.ensure('samplefile')
+ samplefile.write('samplefile\n')
+
+ execfile = path.ensure('execfile')
+ execfile.write('x=42')
+
+ execfilepy = path.ensure('execfile.py')
+ execfilepy.write('x=42')
+
+ d = {1:2, 'hello': 'world', 'answer': 42}
+ path.ensure('samplepickle').dump(d)
+
+ sampledir = path.ensure('sampledir', dir=1)
+ sampledir.ensure('otherfile')
+
+ otherdir = path.ensure('otherdir', dir=1)
+ otherdir.ensure('__init__.py')
+
+ module_a = otherdir.ensure('a.py')
+ module_a.write('from .b import stuff as result\n')
+ module_b = otherdir.ensure('b.py')
+ module_b.write('stuff="got it"\n')
+ module_c = otherdir.ensure('c.py')
+ module_c.write('''import py;
+import otherdir.a
+value = otherdir.a.result
+''')
+ module_d = otherdir.ensure('d.py')
+ module_d.write('''import py;
+from otherdir import a
+value2 = a.result
+''')
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/conftest.py
new file mode 100644
index 00000000000..84fb5c82694
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/conftest.py
@@ -0,0 +1,80 @@
+import py
+import sys
+from py._path import svnwc as svncommon
+
+svnbin = py.path.local.sysfind('svn')
+repodump = py.path.local(__file__).dirpath('repotest.dump')
+from py.builtin import print_
+
+def pytest_funcarg__repowc1(request):
+ if svnbin is None:
+ py.test.skip("svn binary not found")
+
+ tmpdir = request.getfuncargvalue("tmpdir")
+ repo, repourl, wc = request.cached_setup(
+ setup=lambda: getrepowc(tmpdir, "path1repo", "path1wc"),
+ scope="module",
+ )
+ for x in ('test_remove', 'test_move', 'test_status_deleted'):
+ if request.function.__name__.startswith(x):
+ #print >>sys.stderr, ("saving repo", repo, "for", request.function)
+ _savedrepowc = save_repowc(repo, wc)
+ request.addfinalizer(lambda: restore_repowc(_savedrepowc))
+ return repo, repourl, wc
+
+def pytest_funcarg__repowc2(request):
+ tmpdir = request.getfuncargvalue("tmpdir")
+ name = request.function.__name__
+ repo, url, wc = getrepowc(tmpdir, "%s-repo-2" % name, "%s-wc-2" % name)
+ return repo, url, wc
+
+def getsvnbin():
+ if svnbin is None:
+ py.test.skip("svn binary not found")
+ return svnbin
+
+# make a wc directory out of a given root url
+# cache previously obtained wcs!
+#
+def getrepowc(tmpdir, reponame='basetestrepo', wcname='wc'):
+ repo = tmpdir.mkdir(reponame)
+ wcdir = tmpdir.mkdir(wcname)
+ repo.ensure(dir=1)
+ py.process.cmdexec('svnadmin create "%s"' %
+ svncommon._escape_helper(repo))
+ py.process.cmdexec('svnadmin load -q "%s" <"%s"' %
+ (svncommon._escape_helper(repo), repodump))
+ print_("created svn repository", repo)
+ wcdir.ensure(dir=1)
+ wc = py.path.svnwc(wcdir)
+ if sys.platform == 'win32':
+ repourl = "file://" + '/' + str(repo).replace('\\', '/')
+ else:
+ repourl = "file://%s" % repo
+ wc.checkout(repourl)
+ print_("checked out new repo into", wc)
+ return (repo, repourl, wc)
+
+
+def save_repowc(repo, wc):
+ assert not str(repo).startswith("file://"), repo
+ assert repo.check()
+ savedrepo = repo.dirpath(repo.basename+".1")
+ savedwc = wc.dirpath(wc.basename+".1")
+ repo.copy(savedrepo)
+ wc.localpath.copy(savedwc.localpath)
+ return savedrepo, savedwc
+
+def restore_repowc(obj):
+ savedrepo, savedwc = obj
+ #print >>sys.stderr, ("restoring", savedrepo)
+ repo = savedrepo.new(basename=savedrepo.basename[:-2])
+ assert repo.check()
+ wc = savedwc.new(basename=savedwc.basename[:-2])
+ assert wc.check()
+ wc.localpath.remove()
+ repo.remove()
+ savedrepo.move(repo)
+ savedwc.localpath.move(wc.localpath)
+ py.path.svnurl._lsnorevcache.clear()
+ py.path.svnurl._lsrevcache.clear()
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/path/repotest.dump b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/repotest.dump
index c7819cad7a5..c7819cad7a5 100644
--- a/tests/wpt/web-platform-tests/tools/py/testing/path/repotest.dump
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/repotest.dump
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/path/svntestbase.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/svntestbase.py
index 8d94a9ca649..8d94a9ca649 100644
--- a/tests/wpt/web-platform-tests/tools/py/testing/path/svntestbase.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/svntestbase.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_cacheutil.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_cacheutil.py
new file mode 100644
index 00000000000..c9fc07463a7
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_cacheutil.py
@@ -0,0 +1,89 @@
+import pytest
+from py._path import cacheutil
+
+import time
+
+class BasicCacheAPITest:
+ cache = None
+ def test_getorbuild(self):
+ val = self.cache.getorbuild(-42, lambda: 42)
+ assert val == 42
+ val = self.cache.getorbuild(-42, lambda: 23)
+ assert val == 42
+
+ def test_cache_get_key_error(self):
+ pytest.raises(KeyError, "self.cache._getentry(-23)")
+
+ def test_delentry_non_raising(self):
+ self.cache.getorbuild(100, lambda: 100)
+ self.cache.delentry(100)
+ pytest.raises(KeyError, "self.cache._getentry(100)")
+
+ def test_delentry_raising(self):
+ self.cache.getorbuild(100, lambda: 100)
+ self.cache.delentry(100)
+ pytest.raises(KeyError, self.cache.delentry, 100, raising=True)
+
+ def test_clear(self):
+ self.cache.clear()
+
+
+class TestBuildcostAccess(BasicCacheAPITest):
+ cache = cacheutil.BuildcostAccessCache(maxentries=128)
+
+ def test_cache_works_somewhat_simple(self, monkeypatch):
+ cache = cacheutil.BuildcostAccessCache()
+ # the default gettime
+ # BuildcostAccessCache.build can
+ # result into time()-time() == 0 which makes the below
+ # test fail randomly. Let's rather use incrementing
+ # numbers instead.
+ l = [0]
+
+ def counter():
+ l[0] = l[0] + 1
+ return l[0]
+ monkeypatch.setattr(cacheutil, 'gettime', counter)
+ for x in range(cache.maxentries):
+ y = cache.getorbuild(x, lambda: x)
+ assert x == y
+ for x in range(cache.maxentries):
+ assert cache.getorbuild(x, None) == x
+ halfentries = int(cache.maxentries / 2)
+ for x in range(halfentries):
+ assert cache.getorbuild(x, None) == x
+ assert cache.getorbuild(x, None) == x
+ # evict one entry
+ val = cache.getorbuild(-1, lambda: 42)
+ assert val == 42
+ # check that recently used ones are still there
+ # and are not build again
+ for x in range(halfentries):
+ assert cache.getorbuild(x, None) == x
+ assert cache.getorbuild(-1, None) == 42
+
+
+class TestAging(BasicCacheAPITest):
+ maxsecs = 0.10
+ cache = cacheutil.AgingCache(maxentries=128, maxseconds=maxsecs)
+
+ def test_cache_eviction(self):
+ self.cache.getorbuild(17, lambda: 17)
+ endtime = time.time() + self.maxsecs * 10
+ while time.time() < endtime:
+ try:
+ self.cache._getentry(17)
+ except KeyError:
+ break
+ time.sleep(self.maxsecs*0.3)
+ else:
+ pytest.fail("waiting for cache eviction failed")
+
+
+def test_prune_lowestweight():
+ maxsecs = 0.05
+ cache = cacheutil.AgingCache(maxentries=10, maxseconds=maxsecs)
+ for x in range(cache.maxentries):
+ cache.getorbuild(x, lambda: x)
+ time.sleep(maxsecs*1.1)
+ cache.getorbuild(cache.maxentries+1, lambda: 42)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_local.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_local.py
new file mode 100644
index 00000000000..c9075d6fbb4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_local.py
@@ -0,0 +1,977 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import with_statement
+import time
+import py
+import pytest
+import os
+import sys
+import multiprocessing
+from py.path import local
+import common
+
+failsonjython = py.test.mark.xfail("sys.platform.startswith('java')")
+failsonjywin32 = py.test.mark.xfail(
+ "sys.platform.startswith('java') "
+ "and getattr(os, '_name', None) == 'nt'")
+win32only = py.test.mark.skipif(
+ "not (sys.platform == 'win32' or getattr(os, '_name', None) == 'nt')")
+skiponwin32 = py.test.mark.skipif(
+ "sys.platform == 'win32' or getattr(os, '_name', None) == 'nt'")
+
+ATIME_RESOLUTION = 0.01
+
+
+@pytest.yield_fixture(scope="session")
+def path1(tmpdir_factory):
+ path = tmpdir_factory.mktemp('path')
+ common.setuptestfs(path)
+ yield path
+ assert path.join("samplefile").check()
+
+
+@pytest.fixture
+def fake_fspath_obj(request):
+ class FakeFSPathClass(object):
+ def __init__(self, path):
+ self._path = path
+
+ def __fspath__(self):
+ return self._path
+
+ return FakeFSPathClass(os.path.join("this", "is", "a", "fake", "path"))
+
+
+def batch_make_numbered_dirs(rootdir, repeats):
+ try:
+ for i in range(repeats):
+ dir_ = py.path.local.make_numbered_dir(prefix='repro-', rootdir=rootdir)
+ file_ = dir_.join('foo')
+ file_.write('%s' % i)
+ actual = int(file_.read())
+ assert actual == i, 'int(file_.read()) is %s instead of %s' % (actual, i)
+ dir_.join('.lock').remove(ignore_errors=True)
+ return True
+ except KeyboardInterrupt:
+ # makes sure that interrupting test session won't hang it
+ os.exit(2)
+
+
+class TestLocalPath(common.CommonFSTests):
+ def test_join_normpath(self, tmpdir):
+ assert tmpdir.join(".") == tmpdir
+ p = tmpdir.join("../%s" % tmpdir.basename)
+ assert p == tmpdir
+ p = tmpdir.join("..//%s/" % tmpdir.basename)
+ assert p == tmpdir
+
+ @skiponwin32
+ def test_dirpath_abs_no_abs(self, tmpdir):
+ p = tmpdir.join('foo')
+ assert p.dirpath('/bar') == tmpdir.join('bar')
+ assert tmpdir.dirpath('/bar', abs=True) == local('/bar')
+
+ def test_gethash(self, tmpdir):
+ md5 = py.builtin._tryimport('md5', 'hashlib').md5
+ lib = py.builtin._tryimport('sha', 'hashlib')
+ sha = getattr(lib, 'sha1', getattr(lib, 'sha', None))
+ fn = tmpdir.join("testhashfile")
+ data = 'hello'.encode('ascii')
+ fn.write(data, mode="wb")
+ assert fn.computehash("md5") == md5(data).hexdigest()
+ assert fn.computehash("sha1") == sha(data).hexdigest()
+ py.test.raises(ValueError, fn.computehash, "asdasd")
+
+ def test_remove_removes_readonly_file(self, tmpdir):
+ readonly_file = tmpdir.join('readonly').ensure()
+ readonly_file.chmod(0)
+ readonly_file.remove()
+ assert not readonly_file.check(exists=1)
+
+ def test_remove_removes_readonly_dir(self, tmpdir):
+ readonly_dir = tmpdir.join('readonlydir').ensure(dir=1)
+ readonly_dir.chmod(int("500", 8))
+ readonly_dir.remove()
+ assert not readonly_dir.check(exists=1)
+
+ def test_remove_removes_dir_and_readonly_file(self, tmpdir):
+ readonly_dir = tmpdir.join('readonlydir').ensure(dir=1)
+ readonly_file = readonly_dir.join('readonlyfile').ensure()
+ readonly_file.chmod(0)
+ readonly_dir.remove()
+ assert not readonly_dir.check(exists=1)
+
+ def test_remove_routes_ignore_errors(self, tmpdir, monkeypatch):
+ l = []
+ monkeypatch.setattr(
+ 'shutil.rmtree',
+ lambda *args, **kwargs: l.append(kwargs))
+ tmpdir.remove()
+ assert not l[0]['ignore_errors']
+ for val in (True, False):
+ l[:] = []
+ tmpdir.remove(ignore_errors=val)
+ assert l[0]['ignore_errors'] == val
+
+ def test_initialize_curdir(self):
+ assert str(local()) == os.getcwd()
+
+ @skiponwin32
+ def test_chdir_gone(self, path1):
+ p = path1.ensure("dir_to_be_removed", dir=1)
+ p.chdir()
+ p.remove()
+ pytest.raises(py.error.ENOENT, py.path.local)
+ assert path1.chdir() is None
+ assert os.getcwd() == str(path1)
+
+ def test_as_cwd(self, path1):
+ dir = path1.ensure("subdir", dir=1)
+ old = py.path.local()
+ with dir.as_cwd() as x:
+ assert x == old
+ assert py.path.local() == dir
+ assert os.getcwd() == str(old)
+
+ def test_as_cwd_exception(self, path1):
+ old = py.path.local()
+ dir = path1.ensure("subdir", dir=1)
+ with pytest.raises(ValueError):
+ with dir.as_cwd():
+ raise ValueError()
+ assert old == py.path.local()
+
+ def test_initialize_reldir(self, path1):
+ with path1.as_cwd():
+ p = local('samplefile')
+ assert p.check()
+
+ def test_tilde_expansion(self, monkeypatch, tmpdir):
+ monkeypatch.setenv("HOME", str(tmpdir))
+ p = py.path.local("~", expanduser=True)
+ assert p == os.path.expanduser("~")
+
+ def test_eq_with_strings(self, path1):
+ path1 = path1.join('sampledir')
+ path2 = str(path1)
+ assert path1 == path2
+ assert path2 == path1
+ path3 = path1.join('samplefile')
+ assert path3 != path2
+ assert path2 != path3
+
+ def test_eq_with_none(self, path1):
+ assert path1 != None # noqa
+
+ def test_eq_non_ascii_unicode(self, path1):
+ path2 = path1.join(u'temp')
+ path3 = path1.join(u'ação')
+ path4 = path1.join(u'ディレクトリ')
+
+ assert path2 != path3
+ assert path2 != path4
+ assert path4 != path3
+
+ def test_gt_with_strings(self, path1):
+ path2 = path1.join('sampledir')
+ path3 = str(path1.join("ttt"))
+ assert path3 > path2
+ assert path2 < path3
+ assert path2 < "ttt"
+ assert "ttt" > path2
+ path4 = path1.join("aaa")
+ l = [path2, path4, path3]
+ assert sorted(l) == [path4, path2, path3]
+
+ def test_open_and_ensure(self, path1):
+ p = path1.join("sub1", "sub2", "file")
+ with p.open("w", ensure=1) as f:
+ f.write("hello")
+ assert p.read() == "hello"
+
+ def test_write_and_ensure(self, path1):
+ p = path1.join("sub1", "sub2", "file")
+ p.write("hello", ensure=1)
+ assert p.read() == "hello"
+
+ @py.test.mark.parametrize('bin', (False, True))
+ def test_dump(self, tmpdir, bin):
+ path = tmpdir.join("dumpfile%s" % int(bin))
+ try:
+ d = {'answer': 42}
+ path.dump(d, bin=bin)
+ f = path.open('rb+')
+ import pickle
+ dnew = pickle.load(f)
+ assert d == dnew
+ finally:
+ f.close()
+
+ @failsonjywin32
+ def test_setmtime(self):
+ import tempfile
+ import time
+ try:
+ fd, name = tempfile.mkstemp()
+ os.close(fd)
+ except AttributeError:
+ name = tempfile.mktemp()
+ open(name, 'w').close()
+ try:
+ mtime = int(time.time())-100
+ path = local(name)
+ assert path.mtime() != mtime
+ path.setmtime(mtime)
+ assert path.mtime() == mtime
+ path.setmtime()
+ assert path.mtime() != mtime
+ finally:
+ os.remove(name)
+
+ def test_normpath(self, path1):
+ new1 = path1.join("/otherdir")
+ new2 = path1.join("otherdir")
+ assert str(new1) == str(new2)
+
+ def test_mkdtemp_creation(self):
+ d = local.mkdtemp()
+ try:
+ assert d.check(dir=1)
+ finally:
+ d.remove(rec=1)
+
+ def test_tmproot(self):
+ d = local.mkdtemp()
+ tmproot = local.get_temproot()
+ try:
+ assert d.check(dir=1)
+ assert d.dirpath() == tmproot
+ finally:
+ d.remove(rec=1)
+
+ def test_chdir(self, tmpdir):
+ old = local()
+ try:
+ res = tmpdir.chdir()
+ assert str(res) == str(old)
+ assert os.getcwd() == str(tmpdir)
+ finally:
+ old.chdir()
+
+ def test_ensure_filepath_withdir(self, tmpdir):
+ newfile = tmpdir.join('test1', 'test')
+ newfile.ensure()
+ assert newfile.check(file=1)
+ newfile.write("42")
+ newfile.ensure()
+ s = newfile.read()
+ assert s == "42"
+
+ def test_ensure_filepath_withoutdir(self, tmpdir):
+ newfile = tmpdir.join('test1file')
+ t = newfile.ensure()
+ assert t == newfile
+ assert newfile.check(file=1)
+
+ def test_ensure_dirpath(self, tmpdir):
+ newfile = tmpdir.join('test1', 'testfile')
+ t = newfile.ensure(dir=1)
+ assert t == newfile
+ assert newfile.check(dir=1)
+
+ def test_ensure_non_ascii_unicode(self, tmpdir):
+ newfile = tmpdir.join(u'ação',u'ディレクトリ')
+ t = newfile.ensure(dir=1)
+ assert t == newfile
+ assert newfile.check(dir=1)
+
+ def test_init_from_path(self, tmpdir):
+ l = local()
+ l2 = local(l)
+ assert l2 == l
+
+ wc = py.path.svnwc('.')
+ l3 = local(wc)
+ assert l3 is not wc
+ assert l3.strpath == wc.strpath
+ assert not hasattr(l3, 'commit')
+
+ @py.test.mark.xfail(run=False, reason="unreliable est for long filenames")
+ def test_long_filenames(self, tmpdir):
+ if sys.platform == "win32":
+ py.test.skip("win32: work around needed for path length limit")
+ # see http://codespeak.net/pipermail/py-dev/2008q2/000922.html
+
+ # testing paths > 260 chars (which is Windows' limitation, but
+ # depending on how the paths are used), but > 4096 (which is the
+ # Linux' limitation) - the behaviour of paths with names > 4096 chars
+ # is undetermined
+ newfilename = '/test' * 60
+ l = tmpdir.join(newfilename)
+ l.ensure(file=True)
+ l.write('foo')
+ l2 = tmpdir.join(newfilename)
+ assert l2.read() == 'foo'
+
+ def test_visit_depth_first(self, tmpdir):
+ tmpdir.ensure("a", "1")
+ tmpdir.ensure("b", "2")
+ p3 = tmpdir.ensure("breadth")
+ l = list(tmpdir.visit(lambda x: x.check(file=1)))
+ assert len(l) == 3
+ # check that breadth comes last
+ assert l[2] == p3
+
+ def test_visit_rec_fnmatch(self, tmpdir):
+ p1 = tmpdir.ensure("a", "123")
+ tmpdir.ensure(".b", "345")
+ l = list(tmpdir.visit("???", rec="[!.]*"))
+ assert len(l) == 1
+ # check that breadth comes last
+ assert l[0] == p1
+
+ def test_fnmatch_file_abspath(self, tmpdir):
+ b = tmpdir.join("a", "b")
+ assert b.fnmatch(os.sep.join("ab"))
+ pattern = os.sep.join([str(tmpdir), "*", "b"])
+ assert b.fnmatch(pattern)
+
+ def test_sysfind(self):
+ name = sys.platform == "win32" and "cmd" or "test"
+ x = py.path.local.sysfind(name)
+ assert x.check(file=1)
+ assert py.path.local.sysfind('jaksdkasldqwe') is None
+ assert py.path.local.sysfind(name, paths=[]) is None
+ x2 = py.path.local.sysfind(name, paths=[x.dirpath()])
+ assert x2 == x
+
+ def test_fspath_protocol_other_class(self, fake_fspath_obj):
+ # py.path is always absolute
+ py_path = py.path.local(fake_fspath_obj)
+ str_path = fake_fspath_obj.__fspath__()
+ assert py_path.check(endswith=str_path)
+ assert py_path.join(fake_fspath_obj).strpath == os.path.join(
+ py_path.strpath, str_path)
+
+ def test_make_numbered_dir_multiprocess_safe(self, tmpdir):
+ # https://github.com/pytest-dev/py/issues/30
+ pool = multiprocessing.Pool()
+ results = [pool.apply_async(batch_make_numbered_dirs, [tmpdir, 100]) for _ in range(20)]
+ for r in results:
+ assert r.get()
+
+
+class TestExecutionOnWindows:
+ pytestmark = win32only
+
+ def test_sysfind_bat_exe_before(self, tmpdir, monkeypatch):
+ monkeypatch.setenv("PATH", str(tmpdir), prepend=os.pathsep)
+ tmpdir.ensure("hello")
+ h = tmpdir.ensure("hello.bat")
+ x = py.path.local.sysfind("hello")
+ assert x == h
+
+
+class TestExecution:
+ pytestmark = skiponwin32
+
+ def test_sysfind_no_permisson_ignored(self, monkeypatch, tmpdir):
+ noperm = tmpdir.ensure('noperm', dir=True)
+ monkeypatch.setenv("PATH", noperm, prepend=":")
+ noperm.chmod(0)
+ assert py.path.local.sysfind('jaksdkasldqwe') is None
+
+ def test_sysfind_absolute(self):
+ x = py.path.local.sysfind('test')
+ assert x.check(file=1)
+ y = py.path.local.sysfind(str(x))
+ assert y.check(file=1)
+ assert y == x
+
+ def test_sysfind_multiple(self, tmpdir, monkeypatch):
+ monkeypatch.setenv('PATH', "%s:%s" % (
+ tmpdir.ensure('a'),
+ tmpdir.join('b')),
+ prepend=":")
+ tmpdir.ensure('b', 'a')
+ x = py.path.local.sysfind(
+ 'a', checker=lambda x: x.dirpath().basename == 'b')
+ assert x.basename == 'a'
+ assert x.dirpath().basename == 'b'
+ assert py.path.local.sysfind('a', checker=lambda x: None) is None
+
+ def test_sysexec(self):
+ x = py.path.local.sysfind('ls')
+ out = x.sysexec('-a')
+ for x in py.path.local().listdir():
+ assert out.find(x.basename) != -1
+
+ def test_sysexec_failing(self):
+ x = py.path.local.sysfind('false')
+ with pytest.raises(py.process.cmdexec.Error):
+ x.sysexec('aksjdkasjd')
+
+ def test_make_numbered_dir(self, tmpdir):
+ tmpdir.ensure('base.not_an_int', dir=1)
+ for i in range(10):
+ numdir = local.make_numbered_dir(prefix='base.', rootdir=tmpdir,
+ keep=2, lock_timeout=0)
+ assert numdir.check()
+ assert numdir.basename == 'base.%d' % i
+ if i >= 1:
+ assert numdir.new(ext=str(i-1)).check()
+ if i >= 2:
+ assert numdir.new(ext=str(i-2)).check()
+ if i >= 3:
+ assert not numdir.new(ext=str(i-3)).check()
+
+ def test_make_numbered_dir_case_insensitive(self, tmpdir, monkeypatch):
+ # https://github.com/pytest-dev/pytest/issues/708
+ monkeypatch.setattr(py._path.local, 'normcase',
+ lambda path: path.lower())
+ monkeypatch.setattr(tmpdir, 'listdir',
+ lambda: [tmpdir._fastjoin('case.0')])
+ numdir = local.make_numbered_dir(prefix='CAse.', rootdir=tmpdir,
+ keep=2, lock_timeout=0)
+ assert numdir.basename.endswith('.1')
+
+ def test_make_numbered_dir_case_sensitive(self, tmpdir, monkeypatch):
+ # https://github.com/pytest-dev/pytest/issues/708
+ monkeypatch.setattr(py._path.local, 'normcase', lambda path: path)
+ monkeypatch.setattr(tmpdir, 'listdir',
+ lambda: [tmpdir._fastjoin('case.0')])
+ numdir = local.make_numbered_dir(prefix='CAse.', rootdir=tmpdir,
+ keep=2, lock_timeout=0)
+ assert numdir.basename.endswith('.0')
+
+ def test_make_numbered_dir_NotImplemented_Error(self, tmpdir, monkeypatch):
+ def notimpl(x, y):
+ raise NotImplementedError(42)
+ monkeypatch.setattr(os, 'symlink', notimpl)
+ x = tmpdir.make_numbered_dir(rootdir=tmpdir, lock_timeout=0)
+ assert x.relto(tmpdir)
+ assert x.check()
+
+ def test_locked_make_numbered_dir(self, tmpdir):
+ for i in range(10):
+ numdir = local.make_numbered_dir(prefix='base2.', rootdir=tmpdir,
+ keep=2)
+ assert numdir.check()
+ assert numdir.basename == 'base2.%d' % i
+ for j in range(i):
+ assert numdir.new(ext=str(j)).check()
+
+ def test_error_preservation(self, path1):
+ py.test.raises(EnvironmentError, path1.join('qwoeqiwe').mtime)
+ py.test.raises(EnvironmentError, path1.join('qwoeqiwe').read)
+
+ # def test_parentdirmatch(self):
+ # local.parentdirmatch('std', startmodule=__name__)
+ #
+
+
+class TestImport:
+ def test_pyimport(self, path1):
+ obj = path1.join('execfile.py').pyimport()
+ assert obj.x == 42
+ assert obj.__name__ == 'execfile'
+
+ def test_pyimport_renamed_dir_creates_mismatch(self, tmpdir):
+ p = tmpdir.ensure("a", "test_x123.py")
+ p.pyimport()
+ tmpdir.join("a").move(tmpdir.join("b"))
+ with pytest.raises(tmpdir.ImportMismatchError):
+ tmpdir.join("b", "test_x123.py").pyimport()
+
+ def test_pyimport_messy_name(self, tmpdir):
+ # http://bitbucket.org/hpk42/py-trunk/issue/129
+ path = tmpdir.ensure('foo__init__.py')
+ path.pyimport()
+
+ def test_pyimport_dir(self, tmpdir):
+ p = tmpdir.join("hello_123")
+ p_init = p.ensure("__init__.py")
+ m = p.pyimport()
+ assert m.__name__ == "hello_123"
+ m = p_init.pyimport()
+ assert m.__name__ == "hello_123"
+
+ def test_pyimport_execfile_different_name(self, path1):
+ obj = path1.join('execfile.py').pyimport(modname="0x.y.z")
+ assert obj.x == 42
+ assert obj.__name__ == '0x.y.z'
+
+ def test_pyimport_a(self, path1):
+ otherdir = path1.join('otherdir')
+ mod = otherdir.join('a.py').pyimport()
+ assert mod.result == "got it"
+ assert mod.__name__ == 'otherdir.a'
+
+ def test_pyimport_b(self, path1):
+ otherdir = path1.join('otherdir')
+ mod = otherdir.join('b.py').pyimport()
+ assert mod.stuff == "got it"
+ assert mod.__name__ == 'otherdir.b'
+
+ def test_pyimport_c(self, path1):
+ otherdir = path1.join('otherdir')
+ mod = otherdir.join('c.py').pyimport()
+ assert mod.value == "got it"
+
+ def test_pyimport_d(self, path1):
+ otherdir = path1.join('otherdir')
+ mod = otherdir.join('d.py').pyimport()
+ assert mod.value2 == "got it"
+
+ def test_pyimport_and_import(self, tmpdir):
+ tmpdir.ensure('xxxpackage', '__init__.py')
+ mod1path = tmpdir.ensure('xxxpackage', 'module1.py')
+ mod1 = mod1path.pyimport()
+ assert mod1.__name__ == 'xxxpackage.module1'
+ from xxxpackage import module1
+ assert module1 is mod1
+
+ def test_pyimport_check_filepath_consistency(self, monkeypatch, tmpdir):
+ name = 'pointsback123'
+ ModuleType = type(os)
+ p = tmpdir.ensure(name + '.py')
+ for ending in ('.pyc', '$py.class', '.pyo'):
+ mod = ModuleType(name)
+ pseudopath = tmpdir.ensure(name+ending)
+ mod.__file__ = str(pseudopath)
+ monkeypatch.setitem(sys.modules, name, mod)
+ newmod = p.pyimport()
+ assert mod == newmod
+ monkeypatch.undo()
+ mod = ModuleType(name)
+ pseudopath = tmpdir.ensure(name+"123.py")
+ mod.__file__ = str(pseudopath)
+ monkeypatch.setitem(sys.modules, name, mod)
+ excinfo = py.test.raises(pseudopath.ImportMismatchError, p.pyimport)
+ modname, modfile, orig = excinfo.value.args
+ assert modname == name
+ assert modfile == pseudopath
+ assert orig == p
+ assert issubclass(pseudopath.ImportMismatchError, ImportError)
+
+ def test_issue131_pyimport_on__init__(self, tmpdir):
+ # __init__.py files may be namespace packages, and thus the
+ # __file__ of an imported module may not be ourselves
+ # see issue
+ p1 = tmpdir.ensure("proja", "__init__.py")
+ p2 = tmpdir.ensure("sub", "proja", "__init__.py")
+ m1 = p1.pyimport()
+ m2 = p2.pyimport()
+ assert m1 == m2
+
+ def test_ensuresyspath_append(self, tmpdir):
+ root1 = tmpdir.mkdir("root1")
+ file1 = root1.ensure("x123.py")
+ assert str(root1) not in sys.path
+ file1.pyimport(ensuresyspath="append")
+ assert str(root1) == sys.path[-1]
+ assert str(root1) not in sys.path[:-1]
+
+
+def test_pypkgdir(tmpdir):
+ pkg = tmpdir.ensure('pkg1', dir=1)
+ pkg.ensure("__init__.py")
+ pkg.ensure("subdir/__init__.py")
+ assert pkg.pypkgpath() == pkg
+ assert pkg.join('subdir', '__init__.py').pypkgpath() == pkg
+
+
+def test_pypkgdir_unimportable(tmpdir):
+ pkg = tmpdir.ensure('pkg1-1', dir=1) # unimportable
+ pkg.ensure("__init__.py")
+ subdir = pkg.ensure("subdir/__init__.py").dirpath()
+ assert subdir.pypkgpath() == subdir
+ assert subdir.ensure("xyz.py").pypkgpath() == subdir
+ assert not pkg.pypkgpath()
+
+
+def test_isimportable():
+ from py._path.local import isimportable
+ assert not isimportable("")
+ assert isimportable("x")
+ assert isimportable("x1")
+ assert isimportable("x_1")
+ assert isimportable("_")
+ assert isimportable("_1")
+ assert not isimportable("x-1")
+ assert not isimportable("x:1")
+
+
+def test_homedir_from_HOME(monkeypatch):
+ path = os.getcwd()
+ monkeypatch.setenv("HOME", path)
+ assert py.path.local._gethomedir() == py.path.local(path)
+
+
+def test_homedir_not_exists(monkeypatch):
+ monkeypatch.delenv("HOME", raising=False)
+ monkeypatch.delenv("HOMEDRIVE", raising=False)
+ homedir = py.path.local._gethomedir()
+ assert homedir is None
+
+
+def test_samefile(tmpdir):
+ assert tmpdir.samefile(tmpdir)
+ p = tmpdir.ensure("hello")
+ assert p.samefile(p)
+ with p.dirpath().as_cwd():
+ assert p.samefile(p.basename)
+ if sys.platform == "win32":
+ p1 = p.__class__(str(p).lower())
+ p2 = p.__class__(str(p).upper())
+ assert p1.samefile(p2)
+
+
+def test_listdir_single_arg(tmpdir):
+ tmpdir.ensure("hello")
+ assert tmpdir.listdir("hello")[0].basename == "hello"
+
+
+def test_mkdtemp_rootdir(tmpdir):
+ dtmp = local.mkdtemp(rootdir=tmpdir)
+ assert tmpdir.listdir() == [dtmp]
+
+
+class TestWINLocalPath:
+ pytestmark = win32only
+
+ def test_owner_group_not_implemented(self, path1):
+ py.test.raises(NotImplementedError, "path1.stat().owner")
+ py.test.raises(NotImplementedError, "path1.stat().group")
+
+ def test_chmod_simple_int(self, path1):
+ py.builtin.print_("path1 is", path1)
+ mode = path1.stat().mode
+ # Ensure that we actually change the mode to something different.
+ path1.chmod(mode == 0 and 1 or 0)
+ try:
+ print(path1.stat().mode)
+ print(mode)
+ assert path1.stat().mode != mode
+ finally:
+ path1.chmod(mode)
+ assert path1.stat().mode == mode
+
+ def test_path_comparison_lowercase_mixed(self, path1):
+ t1 = path1.join("a_path")
+ t2 = path1.join("A_path")
+ assert t1 == t1
+ assert t1 == t2
+
+ def test_relto_with_mixed_case(self, path1):
+ t1 = path1.join("a_path", "fiLe")
+ t2 = path1.join("A_path")
+ assert t1.relto(t2) == "fiLe"
+
+ def test_allow_unix_style_paths(self, path1):
+ t1 = path1.join('a_path')
+ assert t1 == str(path1) + '\\a_path'
+ t1 = path1.join('a_path/')
+ assert t1 == str(path1) + '\\a_path'
+ t1 = path1.join('dir/a_path')
+ assert t1 == str(path1) + '\\dir\\a_path'
+
+ def test_sysfind_in_currentdir(self, path1):
+ cmd = py.path.local.sysfind('cmd')
+ root = cmd.new(dirname='', basename='') # c:\ in most installations
+ with root.as_cwd():
+ x = py.path.local.sysfind(cmd.relto(root))
+ assert x.check(file=1)
+
+ def test_fnmatch_file_abspath_posix_pattern_on_win32(self, tmpdir):
+ # path-matching patterns might contain a posix path separator '/'
+ # Test that we can match that pattern on windows.
+ import posixpath
+ b = tmpdir.join("a", "b")
+ assert b.fnmatch(posixpath.sep.join("ab"))
+ pattern = posixpath.sep.join([str(tmpdir), "*", "b"])
+ assert b.fnmatch(pattern)
+
+
+class TestPOSIXLocalPath:
+ pytestmark = skiponwin32
+
+ def test_hardlink(self, tmpdir):
+ linkpath = tmpdir.join('test')
+ filepath = tmpdir.join('file')
+ filepath.write("Hello")
+ nlink = filepath.stat().nlink
+ linkpath.mklinkto(filepath)
+ assert filepath.stat().nlink == nlink + 1
+
+ def test_symlink_are_identical(self, tmpdir):
+ filepath = tmpdir.join('file')
+ filepath.write("Hello")
+ linkpath = tmpdir.join('test')
+ linkpath.mksymlinkto(filepath)
+ assert linkpath.readlink() == str(filepath)
+
+ def test_symlink_isfile(self, tmpdir):
+ linkpath = tmpdir.join('test')
+ filepath = tmpdir.join('file')
+ filepath.write("")
+ linkpath.mksymlinkto(filepath)
+ assert linkpath.check(file=1)
+ assert not linkpath.check(link=0, file=1)
+ assert linkpath.islink()
+
+ def test_symlink_relative(self, tmpdir):
+ linkpath = tmpdir.join('test')
+ filepath = tmpdir.join('file')
+ filepath.write("Hello")
+ linkpath.mksymlinkto(filepath, absolute=False)
+ assert linkpath.readlink() == "file"
+ assert filepath.read() == linkpath.read()
+
+ def test_symlink_not_existing(self, tmpdir):
+ linkpath = tmpdir.join('testnotexisting')
+ assert not linkpath.check(link=1)
+ assert linkpath.check(link=0)
+
+ def test_relto_with_root(self, path1, tmpdir):
+ y = path1.join('x').relto(py.path.local('/'))
+ assert y[0] == str(path1)[1]
+
+ def test_visit_recursive_symlink(self, tmpdir):
+ linkpath = tmpdir.join('test')
+ linkpath.mksymlinkto(tmpdir)
+ visitor = tmpdir.visit(None, lambda x: x.check(link=0))
+ assert list(visitor) == [linkpath]
+
+ def test_symlink_isdir(self, tmpdir):
+ linkpath = tmpdir.join('test')
+ linkpath.mksymlinkto(tmpdir)
+ assert linkpath.check(dir=1)
+ assert not linkpath.check(link=0, dir=1)
+
+ def test_symlink_remove(self, tmpdir):
+ linkpath = tmpdir.join('test')
+ linkpath.mksymlinkto(linkpath) # point to itself
+ assert linkpath.check(link=1)
+ linkpath.remove()
+ assert not linkpath.check()
+
+ def test_realpath_file(self, tmpdir):
+ linkpath = tmpdir.join('test')
+ filepath = tmpdir.join('file')
+ filepath.write("")
+ linkpath.mksymlinkto(filepath)
+ realpath = linkpath.realpath()
+ assert realpath.basename == 'file'
+
+ def test_owner(self, path1, tmpdir):
+ from pwd import getpwuid
+ from grp import getgrgid
+ stat = path1.stat()
+ assert stat.path == path1
+
+ uid = stat.uid
+ gid = stat.gid
+ owner = getpwuid(uid)[0]
+ group = getgrgid(gid)[0]
+
+ assert uid == stat.uid
+ assert owner == stat.owner
+ assert gid == stat.gid
+ assert group == stat.group
+
+ def test_stat_helpers(self, tmpdir, monkeypatch):
+ path1 = tmpdir.ensure("file")
+ stat1 = path1.stat()
+ stat2 = tmpdir.stat()
+ assert stat1.isfile()
+ assert stat2.isdir()
+ assert not stat1.islink()
+ assert not stat2.islink()
+
+ def test_stat_non_raising(self, tmpdir):
+ path1 = tmpdir.join("file")
+ pytest.raises(py.error.ENOENT, lambda: path1.stat())
+ res = path1.stat(raising=False)
+ assert res is None
+
+ def test_atime(self, tmpdir):
+ import time
+ path = tmpdir.ensure('samplefile')
+ now = time.time()
+ atime1 = path.atime()
+ # we could wait here but timer resolution is very
+ # system dependent
+ path.read()
+ time.sleep(ATIME_RESOLUTION)
+ atime2 = path.atime()
+ time.sleep(ATIME_RESOLUTION)
+ duration = time.time() - now
+ assert (atime2-atime1) <= duration
+
+ def test_commondir(self, path1):
+ # XXX This is here in local until we find a way to implement this
+ # using the subversion command line api.
+ p1 = path1.join('something')
+ p2 = path1.join('otherthing')
+ assert p1.common(p2) == path1
+ assert p2.common(p1) == path1
+
+ def test_commondir_nocommon(self, path1):
+ # XXX This is here in local until we find a way to implement this
+ # using the subversion command line api.
+ p1 = path1.join('something')
+ p2 = py.path.local(path1.sep+'blabla')
+ assert p1.common(p2) == '/'
+
+ def test_join_to_root(self, path1):
+ root = path1.parts()[0]
+ assert len(str(root)) == 1
+ assert str(root.join('a')) == '/a'
+
+ def test_join_root_to_root_with_no_abs(self, path1):
+ nroot = path1.join('/')
+ assert str(path1) == str(nroot)
+ assert path1 == nroot
+
+ def test_chmod_simple_int(self, path1):
+ mode = path1.stat().mode
+ path1.chmod(int(mode/2))
+ try:
+ assert path1.stat().mode != mode
+ finally:
+ path1.chmod(mode)
+ assert path1.stat().mode == mode
+
+ def test_chmod_rec_int(self, path1):
+ # XXX fragile test
+ def recfilter(x): return x.check(dotfile=0, link=0)
+ oldmodes = {}
+ for x in path1.visit(rec=recfilter):
+ oldmodes[x] = x.stat().mode
+ path1.chmod(int("772", 8), rec=recfilter)
+ try:
+ for x in path1.visit(rec=recfilter):
+ assert x.stat().mode & int("777", 8) == int("772", 8)
+ finally:
+ for x, y in oldmodes.items():
+ x.chmod(y)
+
+ def test_copy_archiving(self, tmpdir):
+ unicode_fn = u"something-\342\200\223.txt"
+ f = tmpdir.ensure("a", unicode_fn)
+ a = f.dirpath()
+ oldmode = f.stat().mode
+ newmode = oldmode ^ 1
+ f.chmod(newmode)
+ b = tmpdir.join("b")
+ a.copy(b, mode=True)
+ assert b.join(f.basename).stat().mode == newmode
+
+ def test_copy_stat_file(self, tmpdir):
+ src = tmpdir.ensure('src')
+ dst = tmpdir.join('dst')
+ # a small delay before the copy
+ time.sleep(ATIME_RESOLUTION)
+ src.copy(dst, stat=True)
+ oldstat = src.stat()
+ newstat = dst.stat()
+ assert oldstat.mode == newstat.mode
+ assert (dst.atime() - src.atime()) < ATIME_RESOLUTION
+ assert (dst.mtime() - src.mtime()) < ATIME_RESOLUTION
+
+ def test_copy_stat_dir(self, tmpdir):
+ test_files = ['a', 'b', 'c']
+ src = tmpdir.join('src')
+ for f in test_files:
+ src.join(f).write(f, ensure=True)
+ dst = tmpdir.join('dst')
+ # a small delay before the copy
+ time.sleep(ATIME_RESOLUTION)
+ src.copy(dst, stat=True)
+ for f in test_files:
+ oldstat = src.join(f).stat()
+ newstat = dst.join(f).stat()
+ assert (newstat.atime - oldstat.atime) < ATIME_RESOLUTION
+ assert (newstat.mtime - oldstat.mtime) < ATIME_RESOLUTION
+ assert oldstat.mode == newstat.mode
+
+ @failsonjython
+ def test_chown_identity(self, path1):
+ owner = path1.stat().owner
+ group = path1.stat().group
+ path1.chown(owner, group)
+
+ @failsonjython
+ def test_chown_dangling_link(self, path1):
+ owner = path1.stat().owner
+ group = path1.stat().group
+ x = path1.join('hello')
+ x.mksymlinkto('qlwkejqwlek')
+ try:
+ path1.chown(owner, group, rec=1)
+ finally:
+ x.remove(rec=0)
+
+ @failsonjython
+ def test_chown_identity_rec_mayfail(self, path1):
+ owner = path1.stat().owner
+ group = path1.stat().group
+ path1.chown(owner, group)
+
+
+class TestUnicodePy2Py3:
+ def test_join_ensure(self, tmpdir, monkeypatch):
+ if sys.version_info >= (3, 0) and "LANG" not in os.environ:
+ pytest.skip("cannot run test without locale")
+ x = py.path.local(tmpdir.strpath)
+ part = "hällo"
+ y = x.ensure(part)
+ assert x.join(part) == y
+
+ def test_listdir(self, tmpdir):
+ if sys.version_info >= (3, 0) and "LANG" not in os.environ:
+ pytest.skip("cannot run test without locale")
+ x = py.path.local(tmpdir.strpath)
+ part = "hällo"
+ y = x.ensure(part)
+ assert x.listdir(part)[0] == y
+
+ @pytest.mark.xfail(
+ reason="changing read/write might break existing usages")
+ def test_read_write(self, tmpdir):
+ x = tmpdir.join("hello")
+ part = py.builtin._totext("hällo", "utf8")
+ x.write(part)
+ assert x.read() == part
+ x.write(part.encode(sys.getdefaultencoding()))
+ assert x.read() == part.encode(sys.getdefaultencoding())
+
+
+class TestBinaryAndTextMethods:
+ def test_read_binwrite(self, tmpdir):
+ x = tmpdir.join("hello")
+ part = py.builtin._totext("hällo", "utf8")
+ part_utf8 = part.encode("utf8")
+ x.write_binary(part_utf8)
+ assert x.read_binary() == part_utf8
+ s = x.read_text(encoding="utf8")
+ assert s == part
+ assert py.builtin._istext(s)
+
+ def test_read_textwrite(self, tmpdir):
+ x = tmpdir.join("hello")
+ part = py.builtin._totext("hällo", "utf8")
+ part_utf8 = part.encode("utf8")
+ x.write_text(part, encoding="utf8")
+ assert x.read_binary() == part_utf8
+ assert x.read_text(encoding="utf8") == part
+
+ def test_default_encoding(self, tmpdir):
+ x = tmpdir.join("hello")
+ # Can't use UTF8 as the default encoding (ASCII) doesn't support it
+ part = py.builtin._totext("hello", "ascii")
+ x.write_text(part, "ascii")
+ s = x.read_text("ascii")
+ assert s == part
+ assert type(s) == type(part)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_svnauth.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_svnauth.py
new file mode 100644
index 00000000000..654f033224f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_svnauth.py
@@ -0,0 +1,460 @@
+import py
+from py.path import SvnAuth
+import time
+import sys
+
+svnbin = py.path.local.sysfind('svn')
+
+
+def make_repo_auth(repo, userdata):
+ """ write config to repo
+
+ user information in userdata is used for auth
+ userdata has user names as keys, and a tuple (password, readwrite) as
+ values, where 'readwrite' is either 'r' or 'rw'
+ """
+ confdir = py.path.local(repo).join('conf')
+ confdir.join('svnserve.conf').write('''\
+[general]
+anon-access = none
+password-db = passwd
+authz-db = authz
+realm = TestRepo
+''')
+ authzdata = '[/]\n'
+ passwddata = '[users]\n'
+ for user in userdata:
+ authzdata += '%s = %s\n' % (user, userdata[user][1])
+ passwddata += '%s = %s\n' % (user, userdata[user][0])
+ confdir.join('authz').write(authzdata)
+ confdir.join('passwd').write(passwddata)
+
+def serve_bg(repopath):
+ pidfile = py.path.local(repopath).join('pid')
+ port = 10000
+ e = None
+ while port < 10010:
+ cmd = 'svnserve -d -T --listen-port=%d --pid-file=%s -r %s' % (
+ port, pidfile, repopath)
+ print(cmd)
+ try:
+ py.process.cmdexec(cmd)
+ except py.process.cmdexec.Error:
+ e = sys.exc_info()[1]
+ else:
+ # XXX we assume here that the pid file gets written somewhere, I
+ # guess this should be relatively safe... (I hope, at least?)
+ counter = pid = 0
+ while counter < 10:
+ counter += 1
+ try:
+ pid = pidfile.read()
+ except py.error.ENOENT:
+ pass
+ if pid:
+ break
+ time.sleep(0.2)
+ return port, int(pid)
+ port += 1
+ raise IOError('could not start svnserve: %s' % (e,))
+
+class TestSvnAuth(object):
+ def test_basic(self):
+ auth = SvnAuth('foo', 'bar')
+ assert auth.username == 'foo'
+ assert auth.password == 'bar'
+ assert str(auth)
+
+ def test_makecmdoptions_uname_pw_makestr(self):
+ auth = SvnAuth('foo', 'bar')
+ assert auth.makecmdoptions() == '--username="foo" --password="bar"'
+
+ def test_makecmdoptions_quote_escape(self):
+ auth = SvnAuth('fo"o', '"ba\'r"')
+ assert auth.makecmdoptions() == '--username="fo\\"o" --password="\\"ba\'r\\""'
+
+ def test_makecmdoptions_no_cache_auth(self):
+ auth = SvnAuth('foo', 'bar', cache_auth=False)
+ assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
+ '--no-auth-cache')
+
+ def test_makecmdoptions_no_interactive(self):
+ auth = SvnAuth('foo', 'bar', interactive=False)
+ assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
+ '--non-interactive')
+
+ def test_makecmdoptions_no_interactive_no_cache_auth(self):
+ auth = SvnAuth('foo', 'bar', cache_auth=False,
+ interactive=False)
+ assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
+ '--no-auth-cache --non-interactive')
+
+class svnwc_no_svn(py.path.svnwc):
+ def __new__(cls, *args, **kwargs):
+ self = super(svnwc_no_svn, cls).__new__(cls, *args, **kwargs)
+ self.commands = []
+ return self
+
+ def _svn(self, *args):
+ self.commands.append(args)
+
+class TestSvnWCAuth(object):
+ def setup_method(self, meth):
+ if not svnbin:
+ py.test.skip("svn binary required")
+ self.auth = SvnAuth('user', 'pass', cache_auth=False)
+
+ def test_checkout(self):
+ wc = svnwc_no_svn('foo', auth=self.auth)
+ wc.checkout('url')
+ assert wc.commands[0][-1] == ('--username="user" --password="pass" '
+ '--no-auth-cache')
+
+ def test_commit(self):
+ wc = svnwc_no_svn('foo', auth=self.auth)
+ wc.commit('msg')
+ assert wc.commands[0][-1] == ('--username="user" --password="pass" '
+ '--no-auth-cache')
+
+ def test_checkout_no_cache_auth(self):
+ wc = svnwc_no_svn('foo', auth=self.auth)
+ wc.checkout('url')
+ assert wc.commands[0][-1] == ('--username="user" --password="pass" '
+ '--no-auth-cache')
+
+ def test_checkout_auth_from_constructor(self):
+ wc = svnwc_no_svn('foo', auth=self.auth)
+ wc.checkout('url')
+ assert wc.commands[0][-1] == ('--username="user" --password="pass" '
+ '--no-auth-cache')
+
+class svnurl_no_svn(py.path.svnurl):
+ cmdexec_output = 'test'
+ popen_output = 'test'
+ def __new__(cls, *args, **kwargs):
+ self = super(svnurl_no_svn, cls).__new__(cls, *args, **kwargs)
+ self.commands = []
+ return self
+
+ def _cmdexec(self, cmd):
+ self.commands.append(cmd)
+ return self.cmdexec_output
+
+ def _popen(self, cmd):
+ self.commands.append(cmd)
+ return self.popen_output
+
+class TestSvnURLAuth(object):
+ def setup_method(self, meth):
+ self.auth = SvnAuth('foo', 'bar')
+
+ def test_init(self):
+ u = svnurl_no_svn('http://foo.bar/svn')
+ assert u.auth is None
+
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ assert u.auth is self.auth
+
+ def test_new(self):
+ u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
+ new = u.new(basename='bar')
+ assert new.auth is self.auth
+ assert new.url == 'http://foo.bar/svn/bar'
+
+ def test_join(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ new = u.join('foo')
+ assert new.auth is self.auth
+ assert new.url == 'http://foo.bar/svn/foo'
+
+ def test_listdir(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ u.cmdexec_output = '''\
+ 1717 johnny 1529 Nov 04 14:32 LICENSE.txt
+ 1716 johnny 5352 Nov 04 14:28 README.txt
+'''
+ paths = u.listdir()
+ assert paths[0].auth is self.auth
+ assert paths[1].auth is self.auth
+ assert paths[0].basename == 'LICENSE.txt'
+
+ def test_info(self):
+ u = svnurl_no_svn('http://foo.bar/svn/LICENSE.txt', auth=self.auth)
+ def dirpath(self):
+ return self
+ u.cmdexec_output = '''\
+ 1717 johnny 1529 Nov 04 14:32 LICENSE.txt
+ 1716 johnny 5352 Nov 04 14:28 README.txt
+'''
+ org_dp = u.__class__.dirpath
+ u.__class__.dirpath = dirpath
+ try:
+ info = u.info()
+ finally:
+ u.dirpath = org_dp
+ assert info.size == 1529
+
+ def test_open(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ foo = u.join('foo')
+ foo.check = lambda *args, **kwargs: True
+ ret = foo.open()
+ assert ret == 'test'
+ assert '--username="foo" --password="bar"' in foo.commands[0]
+
+ def test_dirpath(self):
+ u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
+ parent = u.dirpath()
+ assert parent.auth is self.auth
+
+ def test_mkdir(self):
+ u = svnurl_no_svn('http://foo.bar/svn/qweqwe', auth=self.auth)
+ assert not u.commands
+ u.mkdir(msg='created dir foo')
+ assert u.commands
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+ def test_copy(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ u2 = svnurl_no_svn('http://foo.bar/svn2')
+ u.copy(u2, 'copied dir')
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+ def test_rename(self):
+ u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
+ u.rename('http://foo.bar/svn/bar', 'moved foo to bar')
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+ def test_remove(self):
+ u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
+ u.remove(msg='removing foo')
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+ def test_export(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ target = py.path.local('/foo')
+ u.export(target)
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+ def test_log(self):
+ u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
+ u.popen_output = py.io.TextIO(py.builtin._totext('''\
+<?xml version="1.0"?>
+<log>
+<logentry revision="51381">
+<author>guido</author>
+<date>2008-02-11T12:12:18.476481Z</date>
+<msg>Creating branch to work on auth support for py.path.svn*.
+</msg>
+</logentry>
+</log>
+''', 'ascii'))
+ u.check = lambda *args, **kwargs: True
+ ret = u.log(10, 20, verbose=True)
+ assert '--username="foo" --password="bar"' in u.commands[0]
+ assert len(ret) == 1
+ assert int(ret[0].rev) == 51381
+ assert ret[0].author == 'guido'
+
+ def test_propget(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ u.propget('foo')
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+def pytest_funcarg__setup(request):
+ return Setup(request)
+
+class Setup:
+ def __init__(self, request):
+ if not svnbin:
+ py.test.skip("svn binary required")
+ if not request.config.option.runslowtests:
+ py.test.skip('use --runslowtests to run these tests')
+
+ tmpdir = request.getfuncargvalue("tmpdir")
+ repodir = tmpdir.join("repo")
+ py.process.cmdexec('svnadmin create %s' % repodir)
+ if sys.platform == 'win32':
+ repodir = '/' + str(repodir).replace('\\', '/')
+ self.repo = py.path.svnurl("file://%s" % repodir)
+ if sys.platform == 'win32':
+ # remove trailing slash...
+ repodir = repodir[1:]
+ self.repopath = py.path.local(repodir)
+ self.temppath = tmpdir.mkdir("temppath")
+ self.auth = SvnAuth('johnny', 'foo', cache_auth=False,
+ interactive=False)
+ make_repo_auth(self.repopath, {'johnny': ('foo', 'rw')})
+ self.port, self.pid = serve_bg(self.repopath.dirpath())
+ # XXX caching is too global
+ py.path.svnurl._lsnorevcache._dict.clear()
+ request.addfinalizer(lambda: py.process.kill(self.pid))
+
+class TestSvnWCAuthFunctional:
+ def test_checkout_constructor_arg(self, setup):
+ wc = py.path.svnwc(setup.temppath, auth=setup.auth)
+ wc.checkout(
+ 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
+ assert wc.join('.svn').check()
+
+ def test_checkout_function_arg(self, setup):
+ wc = py.path.svnwc(setup.temppath, auth=setup.auth)
+ wc.checkout(
+ 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
+ assert wc.join('.svn').check()
+
+ def test_checkout_failing_non_interactive(self, setup):
+ auth = SvnAuth('johnny', 'bar', cache_auth=False,
+ interactive=False)
+ wc = py.path.svnwc(setup.temppath, auth)
+ py.test.raises(Exception,
+ ("wc.checkout('svn://localhost:%(port)s/%(repopath)s')" %
+ setup.__dict__))
+
+ def test_log(self, setup):
+ wc = py.path.svnwc(setup.temppath, setup.auth)
+ wc.checkout(
+ 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
+ foo = wc.ensure('foo.txt')
+ wc.commit('added foo.txt')
+ log = foo.log()
+ assert len(log) == 1
+ assert log[0].msg == 'added foo.txt'
+
+ def test_switch(self, setup):
+ import pytest
+ try:
+ import xdist
+ pytest.skip('#160: fails under xdist')
+ except ImportError:
+ pass
+ wc = py.path.svnwc(setup.temppath, auth=setup.auth)
+ svnurl = 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename)
+ wc.checkout(svnurl)
+ wc.ensure('foo', dir=True).ensure('foo.txt').write('foo')
+ wc.commit('added foo dir with foo.txt file')
+ wc.ensure('bar', dir=True)
+ wc.commit('added bar dir')
+ bar = wc.join('bar')
+ bar.switch(svnurl + '/foo')
+ assert bar.join('foo.txt')
+
+ def test_update(self, setup):
+ wc1 = py.path.svnwc(setup.temppath.ensure('wc1', dir=True),
+ auth=setup.auth)
+ wc2 = py.path.svnwc(setup.temppath.ensure('wc2', dir=True),
+ auth=setup.auth)
+ wc1.checkout(
+ 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
+ wc2.checkout(
+ 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
+ wc1.ensure('foo', dir=True)
+ wc1.commit('added foo dir')
+ wc2.update()
+ assert wc2.join('foo').check()
+
+ auth = SvnAuth('unknown', 'unknown', interactive=False)
+ wc2.auth = auth
+ py.test.raises(Exception, 'wc2.update()')
+
+ def test_lock_unlock_status(self, setup):
+ port = setup.port
+ wc = py.path.svnwc(setup.temppath, auth=setup.auth)
+ wc.checkout(
+ 'svn://localhost:%s/%s' % (port, setup.repopath.basename,))
+ wc.ensure('foo', file=True)
+ wc.commit('added foo file')
+ foo = wc.join('foo')
+ foo.lock()
+ status = foo.status()
+ assert status.locked
+ foo.unlock()
+ status = foo.status()
+ assert not status.locked
+
+ auth = SvnAuth('unknown', 'unknown', interactive=False)
+ foo.auth = auth
+ py.test.raises(Exception, 'foo.lock()')
+ py.test.raises(Exception, 'foo.unlock()')
+
+ def test_diff(self, setup):
+ port = setup.port
+ wc = py.path.svnwc(setup.temppath, auth=setup.auth)
+ wc.checkout(
+ 'svn://localhost:%s/%s' % (port, setup.repopath.basename,))
+ wc.ensure('foo', file=True)
+ wc.commit('added foo file')
+ wc.update()
+ rev = int(wc.status().rev)
+ foo = wc.join('foo')
+ foo.write('bar')
+ diff = foo.diff()
+ assert '\n+bar\n' in diff
+ foo.commit('added some content')
+ diff = foo.diff()
+ assert not diff
+ diff = foo.diff(rev=rev)
+ assert '\n+bar\n' in diff
+
+ auth = SvnAuth('unknown', 'unknown', interactive=False)
+ foo.auth = auth
+ py.test.raises(Exception, 'foo.diff(rev=rev)')
+
+class TestSvnURLAuthFunctional:
+ def test_listdir(self, setup):
+ port = setup.port
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
+ auth=setup.auth)
+ u.ensure('foo')
+ paths = u.listdir()
+ assert len(paths) == 1
+ assert paths[0].auth is setup.auth
+
+ auth = SvnAuth('foo', 'bar', interactive=False)
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
+ auth=auth)
+ py.test.raises(Exception, 'u.listdir()')
+
+ def test_copy(self, setup):
+ port = setup.port
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
+ auth=setup.auth)
+ foo = u.mkdir('foo')
+ assert foo.check()
+ bar = u.join('bar')
+ foo.copy(bar)
+ assert bar.check()
+ assert bar.auth is setup.auth
+
+ auth = SvnAuth('foo', 'bar', interactive=False)
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
+ auth=auth)
+ foo = u.join('foo')
+ bar = u.join('bar')
+ py.test.raises(Exception, 'foo.copy(bar)')
+
+ def test_write_read(self, setup):
+ port = setup.port
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
+ auth=setup.auth)
+ foo = u.ensure('foo')
+ fp = foo.open()
+ try:
+ data = fp.read()
+ finally:
+ fp.close()
+ assert data == ''
+
+ auth = SvnAuth('foo', 'bar', interactive=False)
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, setup.repopath.basename),
+ auth=auth)
+ foo = u.join('foo')
+ py.test.raises(Exception, 'foo.open()')
+
+ # XXX rinse, repeat... :|
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/path/test_svnurl.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_svnurl.py
index 15fbea5047d..15fbea5047d 100644
--- a/tests/wpt/web-platform-tests/tools/py/testing/path/test_svnurl.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_svnurl.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_svnwc.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_svnwc.py
new file mode 100644
index 00000000000..c643d9983fb
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/path/test_svnwc.py
@@ -0,0 +1,557 @@
+import py
+import os, sys
+import pytest
+from py._path.svnwc import InfoSvnWCCommand, XMLWCStatus, parse_wcinfotime
+from py._path import svnwc as svncommon
+from svntestbase import CommonSvnTests
+
+
+pytestmark = pytest.mark.xfail(sys.platform.startswith('win'),
+ reason='#161 all tests in this file are failing on Windows',
+ run=False)
+
+
+def test_make_repo(path1, tmpdir):
+ repo = tmpdir.join("repo")
+ py.process.cmdexec('svnadmin create %s' % repo)
+ if sys.platform == 'win32':
+ repo = '/' + str(repo).replace('\\', '/')
+ repo = py.path.svnurl("file://%s" % repo)
+ wc = py.path.svnwc(tmpdir.join("wc"))
+ wc.checkout(repo)
+ assert wc.rev == 0
+ assert len(wc.listdir()) == 0
+ p = wc.join("a_file")
+ p.write("test file")
+ p.add()
+ rev = wc.commit("some test")
+ assert p.info().rev == 1
+ assert rev == 1
+ rev = wc.commit()
+ assert rev is None
+
+def pytest_funcarg__path1(request):
+ repo, repourl, wc = request.getfuncargvalue("repowc1")
+ return wc
+
+class TestWCSvnCommandPath(CommonSvnTests):
+ def test_status_attributes_simple(self, path1):
+ def assert_nochange(p):
+ s = p.status()
+ assert not s.modified
+ assert not s.prop_modified
+ assert not s.added
+ assert not s.deleted
+ assert not s.replaced
+
+ dpath = path1.join('sampledir')
+ assert_nochange(path1.join('sampledir'))
+ assert_nochange(path1.join('samplefile'))
+
+ def test_status_added(self, path1):
+ nf = path1.join('newfile')
+ nf.write('hello')
+ nf.add()
+ try:
+ s = nf.status()
+ assert s.added
+ assert not s.modified
+ assert not s.prop_modified
+ assert not s.replaced
+ finally:
+ nf.revert()
+
+ def test_status_change(self, path1):
+ nf = path1.join('samplefile')
+ try:
+ nf.write(nf.read() + 'change')
+ s = nf.status()
+ assert not s.added
+ assert s.modified
+ assert not s.prop_modified
+ assert not s.replaced
+ finally:
+ nf.revert()
+
+ def test_status_added_ondirectory(self, path1):
+ sampledir = path1.join('sampledir')
+ try:
+ t2 = sampledir.mkdir('t2')
+ t1 = t2.join('t1')
+ t1.write('test')
+ t1.add()
+ s = sampledir.status(rec=1)
+ # Comparing just the file names, because paths are unpredictable
+ # on Windows. (long vs. 8.3 paths)
+ assert t1.basename in [item.basename for item in s.added]
+ assert t2.basename in [item.basename for item in s.added]
+ finally:
+ t2.revert(rec=1)
+ t2.localpath.remove(rec=1)
+
+ def test_status_unknown(self, path1):
+ t1 = path1.join('un1')
+ try:
+ t1.write('test')
+ s = path1.status()
+ # Comparing just the file names, because paths are unpredictable
+ # on Windows. (long vs. 8.3 paths)
+ assert t1.basename in [item.basename for item in s.unknown]
+ finally:
+ t1.localpath.remove()
+
+ def test_status_unchanged(self, path1):
+ r = path1
+ s = path1.status(rec=1)
+ # Comparing just the file names, because paths are unpredictable
+ # on Windows. (long vs. 8.3 paths)
+ assert r.join('samplefile').basename in [item.basename
+ for item in s.unchanged]
+ assert r.join('sampledir').basename in [item.basename
+ for item in s.unchanged]
+ assert r.join('sampledir/otherfile').basename in [item.basename
+ for item in s.unchanged]
+
+ def test_status_update(self, path1):
+ # not a mark because the global "pytestmark" will end up overwriting a mark here
+ pytest.xfail("svn-1.7 has buggy 'status --xml' output")
+ r = path1
+ try:
+ r.update(rev=1)
+ s = r.status(updates=1, rec=1)
+ # Comparing just the file names, because paths are unpredictable
+ # on Windows. (long vs. 8.3 paths)
+ import pprint
+ pprint.pprint(s.allpath())
+ assert r.join('anotherfile').basename in [item.basename for
+ item in s.update_available]
+ #assert len(s.update_available) == 1
+ finally:
+ r.update()
+
+ def test_status_replaced(self, path1):
+ p = path1.join("samplefile")
+ p.remove()
+ p.ensure(dir=0)
+ try:
+ s = path1.status()
+ assert p.basename in [item.basename for item in s.replaced]
+ finally:
+ path1.revert(rec=1)
+
+ def test_status_ignored(self, path1):
+ try:
+ d = path1.join('sampledir')
+ p = py.path.local(d).join('ignoredfile')
+ p.ensure(file=True)
+ s = d.status()
+ assert [x.basename for x in s.unknown] == ['ignoredfile']
+ assert [x.basename for x in s.ignored] == []
+ d.propset('svn:ignore', 'ignoredfile')
+ s = d.status()
+ assert [x.basename for x in s.unknown] == []
+ assert [x.basename for x in s.ignored] == ['ignoredfile']
+ finally:
+ path1.revert(rec=1)
+
+ def test_status_conflict(self, path1, tmpdir):
+ wc = path1
+ wccopy = py.path.svnwc(tmpdir.join("conflict_copy"))
+ wccopy.checkout(wc.url)
+ p = wc.ensure('conflictsamplefile', file=1)
+ p.write('foo')
+ wc.commit('added conflictsamplefile')
+ wccopy.update()
+ assert wccopy.join('conflictsamplefile').check()
+ p.write('bar')
+ wc.commit('wrote some data')
+ wccopy.join('conflictsamplefile').write('baz')
+ wccopy.update(interactive=False)
+ s = wccopy.status()
+ assert [x.basename for x in s.conflict] == ['conflictsamplefile']
+
+ def test_status_external(self, path1, repowc2):
+ otherrepo, otherrepourl, otherwc = repowc2
+ d = path1.ensure('sampledir', dir=1)
+ try:
+ d.update()
+ d.propset('svn:externals', 'otherwc %s' % (otherwc.url,))
+ d.update()
+ s = d.status()
+ assert [x.basename for x in s.external] == ['otherwc']
+ assert 'otherwc' not in [x.basename for x in s.unchanged]
+ s = d.status(rec=1)
+ assert [x.basename for x in s.external] == ['otherwc']
+ assert 'otherwc' in [x.basename for x in s.unchanged]
+ finally:
+ path1.revert(rec=1)
+
+ def test_status_deleted(self, path1):
+ d = path1.ensure('sampledir', dir=1)
+ d.remove()
+ d.ensure(dir=1)
+ path1.commit()
+ d.ensure('deletefile', dir=0)
+ d.commit()
+ s = d.status()
+ assert 'deletefile' in [x.basename for x in s.unchanged]
+ assert not s.deleted
+ p = d.join('deletefile')
+ p.remove()
+ s = d.status()
+ assert 'deletefile' not in s.unchanged
+ assert [x.basename for x in s.deleted] == ['deletefile']
+
+ def test_status_noauthor(self, path1):
+ # testing for XML without author - this used to raise an exception
+ xml = '''\
+ <entry path="/tmp/pytest-23/wc">
+ <wc-status item="normal" props="none" revision="0">
+ <commit revision="0">
+ <date>2008-08-19T16:50:53.400198Z</date>
+ </commit>
+ </wc-status>
+ </entry>
+ '''
+ XMLWCStatus.fromstring(xml, path1)
+
+ def test_status_wrong_xml(self, path1):
+ # testing for XML without author - this used to raise an exception
+ xml = '<entry path="/home/jean/zope/venv/projectdb/parts/development-products/DataGridField">\n<wc-status item="incomplete" props="none" revision="784">\n</wc-status>\n</entry>'
+ st = XMLWCStatus.fromstring(xml, path1)
+ assert len(st.incomplete) == 1
+
+ def test_diff(self, path1):
+ p = path1 / 'anotherfile'
+ out = p.diff(rev=2)
+ assert out.find('hello') != -1
+
+ def test_blame(self, path1):
+ p = path1.join('samplepickle')
+ lines = p.blame()
+ assert sum([l[0] for l in lines]) == len(lines)
+ for l1, l2 in zip(p.readlines(), [l[2] for l in lines]):
+ assert l1 == l2
+ assert [l[1] for l in lines] == ['hpk'] * len(lines)
+ p = path1.join('samplefile')
+ lines = p.blame()
+ assert sum([l[0] for l in lines]) == len(lines)
+ for l1, l2 in zip(p.readlines(), [l[2] for l in lines]):
+ assert l1 == l2
+ assert [l[1] for l in lines] == ['hpk'] * len(lines)
+
+ def test_join_abs(self, path1):
+ s = str(path1.localpath)
+ n = path1.join(s, abs=1)
+ assert path1 == n
+
+ def test_join_abs2(self, path1):
+ assert path1.join('samplefile', abs=1) == path1.join('samplefile')
+
+ def test_str_gives_localpath(self, path1):
+ assert str(path1) == str(path1.localpath)
+
+ def test_versioned(self, path1):
+ assert path1.check(versioned=1)
+ # TODO: Why does my copy of svn think .svn is versioned?
+ #assert path1.join('.svn').check(versioned=0)
+ assert path1.join('samplefile').check(versioned=1)
+ assert not path1.join('notexisting').check(versioned=1)
+ notexisting = path1.join('hello').localpath
+ try:
+ notexisting.write("")
+ assert path1.join('hello').check(versioned=0)
+ finally:
+ notexisting.remove()
+
+ def test_listdir_versioned(self, path1):
+ assert path1.check(versioned=1)
+ p = path1.localpath.ensure("not_a_versioned_file")
+ l = [x.localpath
+ for x in path1.listdir(lambda x: x.check(versioned=True))]
+ assert p not in l
+
+ def test_nonversioned_remove(self, path1):
+ assert path1.check(versioned=1)
+ somefile = path1.join('nonversioned/somefile')
+ nonwc = py.path.local(somefile)
+ nonwc.ensure()
+ assert somefile.check()
+ assert not somefile.check(versioned=True)
+ somefile.remove() # this used to fail because it tried to 'svn rm'
+
+ def test_properties(self, path1):
+ try:
+ path1.propset('gaga', 'this')
+ assert path1.propget('gaga') == 'this'
+ # Comparing just the file names, because paths are unpredictable
+ # on Windows. (long vs. 8.3 paths)
+ assert path1.basename in [item.basename for item in
+ path1.status().prop_modified]
+ assert 'gaga' in path1.proplist()
+ assert path1.proplist()['gaga'] == 'this'
+
+ finally:
+ path1.propdel('gaga')
+
+ def test_proplist_recursive(self, path1):
+ s = path1.join('samplefile')
+ s.propset('gugu', 'that')
+ try:
+ p = path1.proplist(rec=1)
+ # Comparing just the file names, because paths are unpredictable
+ # on Windows. (long vs. 8.3 paths)
+ assert (path1 / 'samplefile').basename in [item.basename
+ for item in p]
+ finally:
+ s.propdel('gugu')
+
+ def test_long_properties(self, path1):
+ value = """
+ vadm:posix : root root 0100755
+ Properties on 'chroot/dns/var/bind/db.net.xots':
+ """
+ try:
+ path1.propset('gaga', value)
+ backvalue = path1.propget('gaga')
+ assert backvalue == value
+ #assert len(backvalue.split('\n')) == 1
+ finally:
+ path1.propdel('gaga')
+
+
+ def test_ensure(self, path1):
+ newpath = path1.ensure('a', 'b', 'c')
+ try:
+ assert newpath.check(exists=1, versioned=1)
+ newpath.write("hello")
+ newpath.ensure()
+ assert newpath.read() == "hello"
+ finally:
+ path1.join('a').remove(force=1)
+
+ def test_not_versioned(self, path1):
+ p = path1.localpath.mkdir('whatever')
+ f = path1.localpath.ensure('testcreatedfile')
+ try:
+ assert path1.join('whatever').check(versioned=0)
+ assert path1.join('testcreatedfile').check(versioned=0)
+ assert not path1.join('testcreatedfile').check(versioned=1)
+ finally:
+ p.remove(rec=1)
+ f.remove()
+
+ def test_lock_unlock(self, path1):
+ root = path1
+ somefile = root.join('somefile')
+ somefile.ensure(file=True)
+ # not yet added to repo
+ py.test.raises(Exception, 'somefile.lock()')
+ somefile.write('foo')
+ somefile.commit('test')
+ assert somefile.check(versioned=True)
+ somefile.lock()
+ try:
+ locked = root.status().locked
+ assert len(locked) == 1
+ assert locked[0].basename == somefile.basename
+ assert locked[0].dirpath().basename == somefile.dirpath().basename
+ #assert somefile.locked()
+ py.test.raises(Exception, 'somefile.lock()')
+ finally:
+ somefile.unlock()
+ #assert not somefile.locked()
+ locked = root.status().locked
+ assert locked == []
+ py.test.raises(Exception, 'somefile,unlock()')
+ somefile.remove()
+
+ def test_commit_nonrecursive(self, path1):
+ somedir = path1.join('sampledir')
+ somedir.mkdir("subsubdir")
+ somedir.propset('foo', 'bar')
+ status = somedir.status()
+ assert len(status.prop_modified) == 1
+ assert len(status.added) == 1
+
+ somedir.commit('non-recursive commit', rec=0)
+ status = somedir.status()
+ assert len(status.prop_modified) == 0
+ assert len(status.added) == 1
+
+ somedir.commit('recursive commit')
+ status = somedir.status()
+ assert len(status.prop_modified) == 0
+ assert len(status.added) == 0
+
+ def test_commit_return_value(self, path1):
+ testfile = path1.join('test.txt').ensure(file=True)
+ testfile.write('test')
+ rev = path1.commit('testing')
+ assert type(rev) == int
+
+ anotherfile = path1.join('another.txt').ensure(file=True)
+ anotherfile.write('test')
+ rev2 = path1.commit('testing more')
+ assert type(rev2) == int
+ assert rev2 == rev + 1
+
+ #def test_log(self, path1):
+ # l = path1.log()
+ # assert len(l) == 3 # might need to be upped if more tests are added
+
+class XTestWCSvnCommandPathSpecial:
+
+ rooturl = 'http://codespeak.net/svn/py.path/trunk/dist/py.path/test/data'
+ #def test_update_none_rev(self, path1):
+ # path = tmpdir.join('checkouttest')
+ # wcpath = newpath(xsvnwc=str(path), url=path1url)
+ # try:
+ # wcpath.checkout(rev=2100)
+ # wcpath.update()
+ # assert wcpath.info().rev > 2100
+ # finally:
+ # wcpath.localpath.remove(rec=1)
+
+def test_parse_wcinfotime():
+ assert (parse_wcinfotime('2006-05-30 20:45:26 +0200 (Tue, 30 May 2006)') ==
+ 1149021926)
+ assert (parse_wcinfotime('2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003)') ==
+ 1067287394)
+
+class TestInfoSvnWCCommand:
+
+ def test_svn_1_2(self, path1):
+ output = """
+ Path: test_svnwc.py
+ Name: test_svnwc.py
+ URL: http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py
+ Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada
+ Revision: 28137
+ Node Kind: file
+ Schedule: normal
+ Last Changed Author: jan
+ Last Changed Rev: 27939
+ Last Changed Date: 2006-05-30 20:45:26 +0200 (Tue, 30 May 2006)
+ Text Last Updated: 2006-06-01 00:42:53 +0200 (Thu, 01 Jun 2006)
+ Properties Last Updated: 2006-05-23 11:54:59 +0200 (Tue, 23 May 2006)
+ Checksum: 357e44880e5d80157cc5fbc3ce9822e3
+ """
+ path = py.path.local(__file__).dirpath().chdir()
+ try:
+ info = InfoSvnWCCommand(output)
+ finally:
+ path.chdir()
+ assert info.last_author == 'jan'
+ assert info.kind == 'file'
+ assert info.mtime == 1149021926.0
+ assert info.url == 'http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py'
+ assert info.time == 1149021926000000.0
+ assert info.rev == 28137
+
+
+ def test_svn_1_3(self, path1):
+ output = """
+ Path: test_svnwc.py
+ Name: test_svnwc.py
+ URL: http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py
+ Repository Root: http://codespeak.net/svn
+ Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada
+ Revision: 28124
+ Node Kind: file
+ Schedule: normal
+ Last Changed Author: jan
+ Last Changed Rev: 27939
+ Last Changed Date: 2006-05-30 20:45:26 +0200 (Tue, 30 May 2006)
+ Text Last Updated: 2006-06-02 23:46:11 +0200 (Fri, 02 Jun 2006)
+ Properties Last Updated: 2006-06-02 23:45:28 +0200 (Fri, 02 Jun 2006)
+ Checksum: 357e44880e5d80157cc5fbc3ce9822e3
+ """
+ path = py.path.local(__file__).dirpath().chdir()
+ try:
+ info = InfoSvnWCCommand(output)
+ finally:
+ path.chdir()
+ assert info.last_author == 'jan'
+ assert info.kind == 'file'
+ assert info.mtime == 1149021926.0
+ assert info.url == 'http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py'
+ assert info.rev == 28124
+ assert info.time == 1149021926000000.0
+
+
+def test_characters_at():
+ py.test.raises(ValueError, "py.path.svnwc('/tmp/@@@:')")
+
+def test_characters_tilde():
+ py.path.svnwc('/tmp/test~')
+
+
+class TestRepo:
+ def test_trailing_slash_is_stripped(self, path1):
+ # XXX we need to test more normalizing properties
+ url = path1.join("/")
+ assert path1 == url
+
+ #def test_different_revs_compare_unequal(self, path1):
+ # newpath = path1.new(rev=1199)
+ # assert newpath != path1
+
+ def test_exists_svn_root(self, path1):
+ assert path1.check()
+
+ #def test_not_exists_rev(self, path1):
+ # url = path1.__class__(path1url, rev=500)
+ # assert url.check(exists=0)
+
+ #def test_nonexisting_listdir_rev(self, path1):
+ # url = path1.__class__(path1url, rev=500)
+ # raises(py.error.ENOENT, url.listdir)
+
+ #def test_newrev(self, path1):
+ # url = path1.new(rev=None)
+ # assert url.rev == None
+ # assert url.strpath == path1.strpath
+ # url = path1.new(rev=10)
+ # assert url.rev == 10
+
+ #def test_info_rev(self, path1):
+ # url = path1.__class__(path1url, rev=1155)
+ # url = url.join("samplefile")
+ # res = url.info()
+ # assert res.size > len("samplefile") and res.created_rev == 1155
+
+ # the following tests are easier if we have a path class
+ def test_repocache_simple(self, path1):
+ repocache = svncommon.RepoCache()
+ repocache.put(path1.strpath, 42)
+ url, rev = repocache.get(path1.join('test').strpath)
+ assert rev == 42
+ assert url == path1.strpath
+
+ def test_repocache_notimeout(self, path1):
+ repocache = svncommon.RepoCache()
+ repocache.timeout = 0
+ repocache.put(path1.strpath, path1.rev)
+ url, rev = repocache.get(path1.strpath)
+ assert rev == -1
+ assert url == path1.strpath
+
+ def test_repocache_outdated(self, path1):
+ repocache = svncommon.RepoCache()
+ repocache.put(path1.strpath, 42, timestamp=0)
+ url, rev = repocache.get(path1.join('test').strpath)
+ assert rev == -1
+ assert url == path1.strpath
+
+ def _test_getreporev(self):
+ """ this test runs so slow it's usually disabled """
+ old = svncommon.repositories.repos
+ try:
+ _repocache.clear()
+ root = path1.new(rev=-1)
+ url, rev = cache.repocache.get(root.strpath)
+ assert rev>=0
+ assert url == svnrepourl
+ finally:
+ repositories.repos = old
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/process/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/process/__init__.py
index 792d6005489..792d6005489 100644
--- a/tests/wpt/web-platform-tests/tools/py/testing/process/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/process/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_cmdexec.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_cmdexec.py
new file mode 100644
index 00000000000..98463d906d1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_cmdexec.py
@@ -0,0 +1,41 @@
+import py
+from py.process import cmdexec
+
+def exvalue():
+ import sys
+ return sys.exc_info()[1]
+
+
+class Test_exec_cmd:
+ def test_simple(self):
+ out = cmdexec('echo hallo')
+ assert out.strip() == 'hallo'
+ assert py.builtin._istext(out)
+
+ def test_simple_newline(self):
+ import sys
+ out = cmdexec(r"""%s -c "print ('hello')" """ % sys.executable)
+ assert out == 'hello\n'
+ assert py.builtin._istext(out)
+
+ def test_simple_error(self):
+ py.test.raises(cmdexec.Error, cmdexec, 'exit 1')
+
+ def test_simple_error_exact_status(self):
+ try:
+ cmdexec('exit 1')
+ except cmdexec.Error:
+ e = exvalue()
+ assert e.status == 1
+ assert py.builtin._istext(e.out)
+ assert py.builtin._istext(e.err)
+
+ def test_err(self):
+ try:
+ cmdexec('echoqweqwe123 hallo')
+ raise AssertionError("command succeeded but shouldn't")
+ except cmdexec.Error:
+ e = exvalue()
+ assert hasattr(e, 'err')
+ assert hasattr(e, 'out')
+ assert e.err or e.out
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_forkedfunc.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_forkedfunc.py
new file mode 100644
index 00000000000..ae0d9ab7e6d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_forkedfunc.py
@@ -0,0 +1,173 @@
+import pytest
+import py, sys, os
+
+pytestmark = py.test.mark.skipif("not hasattr(os, 'fork')")
+
+
+def test_waitfinish_removes_tempdir():
+ ff = py.process.ForkedFunc(boxf1)
+ assert ff.tempdir.check()
+ ff.waitfinish()
+ assert not ff.tempdir.check()
+
+def test_tempdir_gets_gc_collected(monkeypatch):
+ monkeypatch.setattr(os, 'fork', lambda: os.getpid())
+ ff = py.process.ForkedFunc(boxf1)
+ assert ff.tempdir.check()
+ ff.__del__()
+ assert not ff.tempdir.check()
+
+def test_basic_forkedfunc():
+ result = py.process.ForkedFunc(boxf1).waitfinish()
+ assert result.out == "some out\n"
+ assert result.err == "some err\n"
+ assert result.exitstatus == 0
+ assert result.signal == 0
+ assert result.retval == 1
+
+def test_exitstatus():
+ def func():
+ os._exit(4)
+ result = py.process.ForkedFunc(func).waitfinish()
+ assert result.exitstatus == 4
+ assert result.signal == 0
+ assert not result.out
+ assert not result.err
+
+def test_execption_in_func():
+ def fun():
+ raise ValueError(42)
+ ff = py.process.ForkedFunc(fun)
+ result = ff.waitfinish()
+ assert result.exitstatus == ff.EXITSTATUS_EXCEPTION
+ assert result.err.find("ValueError: 42") != -1
+ assert result.signal == 0
+ assert not result.retval
+
+def test_forkedfunc_on_fds():
+ result = py.process.ForkedFunc(boxf2).waitfinish()
+ assert result.out == "someout"
+ assert result.err == "someerr"
+ assert result.exitstatus == 0
+ assert result.signal == 0
+ assert result.retval == 2
+
+def test_forkedfunc_on_fds_output():
+ result = py.process.ForkedFunc(boxf3).waitfinish()
+ assert result.signal == 11
+ assert result.out == "s"
+
+
+def test_forkedfunc_on_stdout():
+ def boxf3():
+ import sys
+ sys.stdout.write("hello\n")
+ os.kill(os.getpid(), 11)
+ result = py.process.ForkedFunc(boxf3).waitfinish()
+ assert result.signal == 11
+ assert result.out == "hello\n"
+
+def test_forkedfunc_signal():
+ result = py.process.ForkedFunc(boxseg).waitfinish()
+ assert result.retval is None
+ assert result.signal == 11
+
+def test_forkedfunc_huge_data():
+ result = py.process.ForkedFunc(boxhuge).waitfinish()
+ assert result.out
+ assert result.exitstatus == 0
+ assert result.signal == 0
+ assert result.retval == 3
+
+def test_box_seq():
+ # we run many boxes with huge data, just one after another
+ for i in range(50):
+ result = py.process.ForkedFunc(boxhuge).waitfinish()
+ assert result.out
+ assert result.exitstatus == 0
+ assert result.signal == 0
+ assert result.retval == 3
+
+def test_box_in_a_box():
+ def boxfun():
+ result = py.process.ForkedFunc(boxf2).waitfinish()
+ print (result.out)
+ sys.stderr.write(result.err + "\n")
+ return result.retval
+
+ result = py.process.ForkedFunc(boxfun).waitfinish()
+ assert result.out == "someout\n"
+ assert result.err == "someerr\n"
+ assert result.exitstatus == 0
+ assert result.signal == 0
+ assert result.retval == 2
+
+def test_kill_func_forked():
+ class A:
+ pass
+ info = A()
+ import time
+
+ def box_fun():
+ time.sleep(10) # we don't want to last forever here
+
+ ff = py.process.ForkedFunc(box_fun)
+ os.kill(ff.pid, 15)
+ result = ff.waitfinish()
+ assert result.signal == 15
+
+
+def test_hooks(monkeypatch):
+ def _boxed():
+ return 1
+
+ def _on_start():
+ sys.stdout.write("some out\n")
+ sys.stdout.flush()
+
+ def _on_exit():
+ sys.stderr.write("some err\n")
+ sys.stderr.flush()
+
+ result = py.process.ForkedFunc(_boxed, child_on_start=_on_start,
+ child_on_exit=_on_exit).waitfinish()
+ assert result.out == "some out\n"
+ assert result.err == "some err\n"
+ assert result.exitstatus == 0
+ assert result.signal == 0
+ assert result.retval == 1
+
+
+# ======================================================================
+# examples
+# ======================================================================
+#
+
+def boxf1():
+ sys.stdout.write("some out\n")
+ sys.stderr.write("some err\n")
+ return 1
+
+def boxf2():
+ os.write(1, "someout".encode('ascii'))
+ os.write(2, "someerr".encode('ascii'))
+ return 2
+
+def boxf3():
+ os.write(1, "s".encode('ascii'))
+ os.kill(os.getpid(), 11)
+
+def boxseg():
+ os.kill(os.getpid(), 11)
+
+def boxhuge():
+ s = " ".encode('ascii')
+ os.write(1, s * 10000)
+ os.write(2, s * 10000)
+ os.write(1, s * 10000)
+
+ os.write(1, s * 10000)
+ os.write(2, s * 10000)
+ os.write(2, s * 10000)
+ os.write(1, s * 10000)
+ return 3
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_killproc.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_killproc.py
new file mode 100644
index 00000000000..b0d6e2f5153
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/process/test_killproc.py
@@ -0,0 +1,18 @@
+import pytest
+import sys
+import py
+
+
+@pytest.mark.skipif("sys.platform.startswith('java')")
+def test_kill(tmpdir):
+ subprocess = pytest.importorskip("subprocess")
+ t = tmpdir.join("t.py")
+ t.write("import time ; time.sleep(100)")
+ proc = subprocess.Popen([sys.executable, str(t)])
+ assert proc.poll() is None # no return value yet
+ py.process.kill(proc.pid)
+ ret = proc.wait()
+ if sys.platform == "win32" and ret == 0:
+ pytest.skip("XXX on win32, subprocess.Popen().wait() on a killed "
+ "process does not yield return value != 0")
+ assert ret != 0
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/root/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/__init__.py
index 792d6005489..792d6005489 100644
--- a/tests/wpt/web-platform-tests/tools/py/testing/root/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/root/test_builtin.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_builtin.py
index a6f1a3c7399..a6f1a3c7399 100644
--- a/tests/wpt/web-platform-tests/tools/py/testing/root/test_builtin.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_builtin.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_error.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_error.py
new file mode 100644
index 00000000000..a1185f33dc9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_error.py
@@ -0,0 +1,68 @@
+
+import py
+
+import errno
+
+
+def test_error_classes():
+ for name in errno.errorcode.values():
+ x = getattr(py.error, name)
+ assert issubclass(x, py.error.Error)
+ assert issubclass(x, EnvironmentError)
+
+
+def test_has_name():
+ assert py.error.__name__ == 'py.error'
+
+
+def test_picklability_issue1():
+ import pickle
+ e1 = py.error.ENOENT()
+ s = pickle.dumps(e1)
+ e2 = pickle.loads(s)
+ assert isinstance(e2, py.error.ENOENT)
+
+
+def test_unknown_error():
+ num = 3999
+ cls = py.error._geterrnoclass(num)
+ assert cls.__name__ == 'UnknownErrno%d' % (num,)
+ assert issubclass(cls, py.error.Error)
+ assert issubclass(cls, EnvironmentError)
+ cls2 = py.error._geterrnoclass(num)
+ assert cls is cls2
+
+
+def test_error_conversion_ENOTDIR(testdir):
+ p = testdir.makepyfile("")
+ excinfo = py.test.raises(py.error.Error, py.error.checked_call, p.listdir)
+ assert isinstance(excinfo.value, EnvironmentError)
+ assert isinstance(excinfo.value, py.error.Error)
+ assert "ENOTDIR" in repr(excinfo.value)
+
+
+def test_checked_call_supports_kwargs(tmpdir):
+ import tempfile
+ py.error.checked_call(tempfile.mkdtemp, dir=str(tmpdir))
+
+
+try:
+ import unittest
+ unittest.TestCase.assertWarns
+except (ImportError, AttributeError):
+ pass # required interface not available
+else:
+ import sys
+ import warnings
+
+ class Case(unittest.TestCase):
+ def test_assertWarns(self):
+ # Clear everything "py.*" from sys.modules and re-import py
+ # as a fresh start
+ for mod in tuple(sys.modules.keys()):
+ if mod and (mod == 'py' or mod.startswith('py.')):
+ del sys.modules[mod]
+ import py
+
+ with self.assertWarns(UserWarning):
+ warnings.warn('this should work')
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_py_imports.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_py_imports.py
new file mode 100644
index 00000000000..31fe6ead810
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_py_imports.py
@@ -0,0 +1,71 @@
+import py
+import sys
+
+
+@py.test.mark.parametrize('name', [x for x in dir(py) if x[0] != '_'])
+def test_dir(name):
+ obj = getattr(py, name)
+ if hasattr(obj, '__map__'): # isinstance(obj, Module):
+ keys = dir(obj)
+ assert len(keys) > 0
+ print (obj.__map__)
+ for name in list(obj.__map__):
+ assert hasattr(obj, name), (obj, name)
+
+
+def test_virtual_module_identity():
+ from py import path as path1
+ from py import path as path2
+ assert path1 is path2
+ from py.path import local as local1
+ from py.path import local as local2
+ assert local1 is local2
+
+
+def test_importall():
+ base = py._pydir
+ nodirs = [
+ ]
+ if sys.version_info >= (3, 0):
+ nodirs.append(base.join('_code', '_assertionold.py'))
+ else:
+ nodirs.append(base.join('_code', '_assertionnew.py'))
+
+ def recurse(p):
+ return p.check(dotfile=0) and p.basename != "attic"
+
+ for p in base.visit('*.py', recurse):
+ if p.basename == '__init__.py':
+ continue
+ relpath = p.new(ext='').relto(base)
+ if base.sep in relpath: # not py/*.py itself
+ for x in nodirs:
+ if p == x or p.relto(x):
+ break
+ else:
+ relpath = relpath.replace(base.sep, '.')
+ modpath = 'py.%s' % relpath
+ try:
+ check_import(modpath)
+ except py.test.skip.Exception:
+ pass
+
+
+def check_import(modpath):
+ py.builtin.print_("checking import", modpath)
+ assert __import__(modpath)
+
+
+def test_star_import():
+ exec("from py import *")
+
+
+def test_all_resolves():
+ seen = py.builtin.set([py])
+ lastlength = None
+ while len(seen) != lastlength:
+ lastlength = len(seen)
+ for item in py.builtin.frozenset(seen):
+ for value in item.__dict__.values():
+ if isinstance(value, type(py.test)):
+ seen.add(value)
diff --git a/tests/wpt/web-platform-tests/tools/py/testing/root/test_std.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_std.py
index 143556a0557..143556a0557 100644
--- a/tests/wpt/web-platform-tests/tools/py/testing/root/test_std.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_std.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_xmlgen.py b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_xmlgen.py
new file mode 100644
index 00000000000..fc0e82665f7
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/testing/root/test_xmlgen.py
@@ -0,0 +1,146 @@
+
+import py
+from py._xmlgen import unicode, html, raw
+import sys
+
+class ns(py.xml.Namespace):
+ pass
+
+def test_escape():
+ uvalue = py.builtin._totext('\xc4\x85\xc4\x87\n\xe2\x82\xac\n', 'utf-8')
+ class A:
+ def __unicode__(self):
+ return uvalue
+ def __str__(self):
+ x = self.__unicode__()
+ if sys.version_info[0] < 3:
+ return x.encode('utf-8')
+ return x
+ y = py.xml.escape(uvalue)
+ assert y == uvalue
+ x = py.xml.escape(A())
+ assert x == uvalue
+ if sys.version_info[0] < 3:
+ assert isinstance(x, unicode)
+ assert isinstance(y, unicode)
+ y = py.xml.escape(uvalue.encode('utf-8'))
+ assert y == uvalue
+
+
+def test_tag_with_text():
+ x = ns.hello("world")
+ u = unicode(x)
+ assert u == "<hello>world</hello>"
+
+def test_class_identity():
+ assert ns.hello is ns.hello
+
+def test_tag_with_text_and_attributes():
+ x = ns.some(name="hello", value="world")
+ assert x.attr.name == 'hello'
+ assert x.attr.value == 'world'
+ u = unicode(x)
+ assert u == '<some name="hello" value="world"/>'
+
+def test_tag_with_subclassed_attr_simple():
+ class my(ns.hello):
+ class Attr(ns.hello.Attr):
+ hello="world"
+ x = my()
+ assert x.attr.hello == 'world'
+ assert unicode(x) == '<my hello="world"/>'
+
+def test_tag_with_raw_attr():
+ x = html.object(data=raw('&'))
+ assert unicode(x) == '<object data="&"></object>'
+
+def test_tag_nested():
+ x = ns.hello(ns.world())
+ unicode(x) # triggers parentifying
+ assert x[0].parent is x
+ u = unicode(x)
+ assert u == '<hello><world/></hello>'
+
+def test_list_nested():
+ x = ns.hello([ns.world()]) #pass in a list here
+ u = unicode(x)
+ assert u == '<hello><world/></hello>'
+
+def test_tag_xmlname():
+ class my(ns.hello):
+ xmlname = 'world'
+ u = unicode(my())
+ assert u == '<world/>'
+
+def test_tag_with_text_entity():
+ x = ns.hello('world & rest')
+ u = unicode(x)
+ assert u == "<hello>world &amp; rest</hello>"
+
+def test_tag_with_text_and_attributes_entity():
+ x = ns.some(name="hello & world")
+ assert x.attr.name == "hello & world"
+ u = unicode(x)
+ assert u == '<some name="hello &amp; world"/>'
+
+def test_raw():
+ x = ns.some(py.xml.raw("<p>literal</p>"))
+ u = unicode(x)
+ assert u == "<some><p>literal</p></some>"
+
+
+def test_html_name_stickyness():
+ class my(html.p):
+ pass
+ x = my("hello")
+ assert unicode(x) == '<p>hello</p>'
+
+def test_stylenames():
+ class my:
+ class body(html.body):
+ style = html.Style(font_size = "12pt")
+ u = unicode(my.body())
+ assert u == '<body style="font-size: 12pt"></body>'
+
+def test_class_None():
+ t = html.body(class_=None)
+ u = unicode(t)
+ assert u == '<body></body>'
+
+def test_alternating_style():
+ alternating = (
+ html.Style(background="white"),
+ html.Style(background="grey"),
+ )
+ class my(html):
+ class li(html.li):
+ def style(self):
+ i = self.parent.index(self)
+ return alternating[i%2]
+ style = property(style)
+
+ x = my.ul(
+ my.li("hello"),
+ my.li("world"),
+ my.li("42"))
+ u = unicode(x)
+ assert u == ('<ul><li style="background: white">hello</li>'
+ '<li style="background: grey">world</li>'
+ '<li style="background: white">42</li>'
+ '</ul>')
+
+def test_singleton():
+ h = html.head(html.link(href="foo"))
+ assert unicode(h) == '<head><link href="foo"/></head>'
+
+ h = html.head(html.script(src="foo"))
+ assert unicode(h) == '<head><script src="foo"></script></head>'
+
+def test_inline():
+ h = html.div(html.span('foo'), html.span('bar'))
+ assert (h.unicode(indent=2) ==
+ '<div><span>foo</span><span>bar</span></div>')
+
+def test_object_tags():
+ o = html.object(html.object())
+ assert o.unicode(indent=0) == '<object><object></object></object>'
diff --git a/tests/wpt/web-platform-tests/tools/third_party/py/tox.ini b/tests/wpt/web-platform-tests/tools/third_party/py/tox.ini
new file mode 100644
index 00000000000..601661cf8e2
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/py/tox.ini
@@ -0,0 +1,33 @@
+[tox]
+envlist=py{27,34,35,36}-pytest{29,30,31}
+
+[testenv]
+changedir=testing
+commands=
+ pip install -U .. # hande the install order fallout since pytest depends on pip
+
+ py.test --confcutdir=.. -rfsxX --junitxml={envlogdir}/junit-{envname}.xml []
+deps=
+ pytest29: pytest~=2.9.0
+ pytest30: pytest~=3.0.0
+ pytest31: pytest~=3.1.0
+
+[testenv:py27-xdist]
+basepython=python2.7
+deps=
+ pytest~=2.9.0
+ pytest-xdist<=1.16.0
+commands=
+ pip install -U .. # hande the install order fallout since pytest depends on pip
+ py.test -n3 -rfsxX --confcutdir=.. --runslowtests \
+ --junitxml={envlogdir}/junit-{envname}.xml []
+
+[testenv:jython]
+changedir=testing
+commands=
+ {envpython} -m pip install -U .. # hande the install order fallout since pytest depends on pip
+ {envpython} -m pytest --confcutdir=.. -rfsxX --junitxml={envlogdir}/junit-{envname}0.xml {posargs:io_ code}
+
+[pytest]
+rsyncdirs = conftest.py py doc testing
+addopts = -ra
diff --git a/tests/wpt/web-platform-tests/tools/pytest/.gitattributes b/tests/wpt/web-platform-tests/tools/third_party/pytest/.gitattributes
index 242d3da0d74..242d3da0d74 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/.gitattributes
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/.gitattributes
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/.github/ISSUE_TEMPLATE.md b/tests/wpt/web-platform-tests/tools/third_party/pytest/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000000..fbcbb16fc35
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,8 @@
+Thanks for submitting an issue!
+
+Here's a quick checklist in what to include:
+
+- [ ] Include a detailed description of the bug or suggestion
+- [ ] `pip list` of the virtual environment you are using
+- [ ] pytest and operating system versions
+- [ ] Minimal example if possible
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/.github/PULL_REQUEST_TEMPLATE.md b/tests/wpt/web-platform-tests/tools/third_party/pytest/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000000..bf9fc199f59
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,15 @@
+Thanks for submitting a PR, your contribution is really appreciated!
+
+Here's a quick checklist that should be present in PRs:
+
+- [ ] Add a new news fragment into the changelog folder
+ * name it `$issue_id.$type` for example (588.bug)
+ * if you don't have an issue_id change it to the pr id after creating the pr
+ * ensure type is one of `removal`, `feature`, `bugfix`, `vendor`, `doc` or `trivial`
+ * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files."
+- [ ] Target: for `bugfix`, `vendor`, `doc` or `trivial` fixes, target `master`; for removals or features target `features`;
+- [ ] Make sure to include reasonable tests for your change if necessary
+
+Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please:
+
+- [ ] Add yourself to `AUTHORS`, in alphabetical order;
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/.gitignore b/tests/wpt/web-platform-tests/tools/third_party/pytest/.gitignore
new file mode 100644
index 00000000000..3b7ec9facf2
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/.gitignore
@@ -0,0 +1,39 @@
+# Automatically generated by `hgimportsvn`
+.svn
+.hgsvn
+
+# Ignore local virtualenvs
+lib/
+bin/
+include/
+.Python/
+
+# These lines are suggested according to the svn:ignore property
+# Feel free to enable them by uncommenting them
+*.pyc
+*.pyo
+*.swp
+*.class
+*.orig
+*~
+.hypothesis/
+
+# autogenerated
+_pytest/_version.py
+# setuptools
+.eggs/
+
+doc/*/_build
+build/
+dist/
+*.egg-info
+issue/
+env/
+.env/
+3rdparty/
+.tox
+.cache
+.coverage
+.ropeproject
+.idea
+.hypothesis
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/.travis.yml b/tests/wpt/web-platform-tests/tools/third_party/pytest/.travis.yml
new file mode 100644
index 00000000000..938391cde09
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/.travis.yml
@@ -0,0 +1,56 @@
+sudo: false
+language: python
+python:
+ - '3.6'
+# command to install dependencies
+install:
+ - pip install --upgrade --pre tox
+# # command to run tests
+env:
+ matrix:
+ # coveralls is not listed in tox's envlist, but should run in travis
+ - TOXENV=coveralls
+ # note: please use "tox --listenvs" to populate the build matrix below
+ - TOXENV=linting
+ - TOXENV=py27
+ - TOXENV=py34
+ - TOXENV=py36
+ - TOXENV=py27-pexpect
+ - TOXENV=py27-xdist
+ - TOXENV=py27-trial
+ - TOXENV=py27-numpy
+ - TOXENV=py27-pluggymaster
+ - TOXENV=py36-pexpect
+ - TOXENV=py36-xdist
+ - TOXENV=py36-trial
+ - TOXENV=py36-numpy
+ - TOXENV=py36-pluggymaster
+ - TOXENV=py27-nobyte
+ - TOXENV=doctesting
+ - TOXENV=docs
+
+matrix:
+ include:
+ - env: TOXENV=pypy
+ python: 'pypy-5.4'
+ - env: TOXENV=py35
+ python: '3.5'
+ - env: TOXENV=py35-freeze
+ python: '3.5'
+ - env: TOXENV=py37
+ python: 'nightly'
+ allow_failures:
+ - env: TOXENV=py37
+ python: 'nightly'
+
+script: tox --recreate
+
+notifications:
+ irc:
+ channels:
+ - "chat.freenode.net#pytest"
+ on_success: change
+ on_failure: change
+ skip_join: true
+ email:
+ - pytest-commit@python.org
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/AUTHORS b/tests/wpt/web-platform-tests/tools/third_party/pytest/AUTHORS
new file mode 100644
index 00000000000..44ae6aa43ab
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/AUTHORS
@@ -0,0 +1,189 @@
+Holger Krekel, holger at merlinux eu
+merlinux GmbH, Germany, office at merlinux eu
+
+Contributors include::
+
+Abdeali JK
+Abhijeet Kasurde
+Ahn Ki-Wook
+Alexander Johnson
+Alexei Kozlenok
+Anatoly Bubenkoff
+Andras Tim
+Andreas Zeidler
+Andrzej Ostrowski
+Andy Freeland
+Anthon van der Neut
+Anthony Sottile
+Antony Lee
+Armin Rigo
+Aron Curzon
+Aviv Palivoda
+Barney Gale
+Ben Webb
+Benjamin Peterson
+Bernard Pratz
+Bob Ippolito
+Brian Dorsey
+Brian Okken
+Brianna Laugher
+Bruno Oliveira
+Cal Leeming
+Carl Friedrich Bolz
+Ceridwen
+Charles Cloud
+Charnjit SiNGH (CCSJ)
+Chris Lamb
+Christian Boelsen
+Christian Theunert
+Christian Tismer
+Christopher Gilling
+Daniel Grana
+Daniel Hahler
+Daniel Nuri
+Daniel Wandschneider
+Danielle Jenkins
+Dave Hunt
+David Díaz-Barquero
+David Mohr
+David Vierra
+Daw-Ran Liou
+Denis Kirisov
+Diego Russo
+Dmitry Dygalo
+Dmitry Pribysh
+Duncan Betts
+Edison Gustavo Muenz
+Edoardo Batini
+Eduardo Schettino
+Eli Boyarski
+Elizaveta Shashkova
+Endre Galaczi
+Eric Hunsberger
+Eric Siegerman
+Erik M. Bray
+Feng Ma
+Florian Bruhin
+Floris Bruynooghe
+Gabriel Reis
+George Kussumoto
+Georgy Dyuldin
+Graham Horler
+Greg Price
+Grig Gheorghiu
+Grigorii Eremeev (budulianin)
+Guido Wesdorp
+Harald Armin Massa
+Hugo van Kemenade
+Hui Wang (coldnight)
+Ian Bicking
+Jaap Broekhuizen
+Jan Balster
+Janne Vanhala
+Jason R. Coombs
+Javier Domingo Cansino
+Javier Romero
+Jeff Widman
+John Eddie Ayson
+John Towler
+Jon Sonesen
+Jonas Obrist
+Jordan Guymon
+Jordan Moldow
+Joshua Bronson
+Jurko Gospodnetić
+Justyna Janczyszyn
+Kale Kundert
+Katarzyna Jachim
+Kevin Cox
+Kodi B. Arfer
+Lawrence Mitchell
+Lee Kamentsky
+Lev Maximov
+Llandy Riveron Del Risco
+Loic Esteve
+Lukas Bednar
+Luke Murphy
+Maciek Fijalkowski
+Maho
+Maik Figura
+Mandeep Bhutani
+Manuel Krebber
+Marc Schlaich
+Marcin Bachry
+Mark Abramowitz
+Markus Unterwaditzer
+Martijn Faassen
+Martin Altmayer
+Martin K. Scherer
+Martin Prusse
+Mathieu Clabaut
+Matt Bachmann
+Matt Duck
+Matt Williams
+Matthias Hafner
+Maxim Filipenko
+mbyt
+Michael Aquilina
+Michael Birtwell
+Michael Droettboom
+Michael Seifert
+Michal Wajszczuk
+Mihai Capotă
+Mike Lundy
+Nathaniel Waisbrot
+Ned Batchelder
+Neven Mundar
+Nicolas Delaby
+Oleg Pidsadnyi
+Oliver Bestwalter
+Omar Kohl
+Omer Hadari
+Patrick Hayes
+Paweł Adamczak
+Pieter Mulder
+Piotr Banaszkiewicz
+Punyashloka Biswal
+Quentin Pradet
+Ralf Schmitt
+Ran Benita
+Raphael Pierzina
+Raquel Alegre
+Ravi Chandra
+Roberto Polli
+Romain Dorgueil
+Roman Bolshakov
+Ronny Pfannschmidt
+Ross Lawley
+Russel Winder
+Ryan Wooden
+Samuel Dion-Girardeau
+Samuele Pedroni
+Segev Finer
+Simon Gomizelj
+Skylar Downes
+Srinivas Reddy Thatiparthy
+Stefan Farmbauer
+Stefan Zimmermann
+Stefano Taschini
+Steffen Allner
+Stephan Obermann
+Tarcisio Fischer
+Tareq Alayan
+Ted Xiao
+Thomas Grainger
+Thomas Hisch
+Tom Dalton
+Tom Viner
+Trevor Bekolay
+Tyler Goodlet
+Vasily Kuznetsov
+Victor Uriarte
+Vidar T. Fauske
+Vitaly Lashmanov
+Vlad Dragos
+Wouter van Ackooy
+Xuan Luong
+Xuecong Liao
+Zoltán Máté
+Roland Puntaier
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/CHANGELOG.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/CHANGELOG.rst
new file mode 100644
index 00000000000..7e7bfaf0441
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/CHANGELOG.rst
@@ -0,0 +1,4194 @@
+..
+ You should *NOT* be adding new change log entries to this file, this
+ file is managed by towncrier. You *may* edit previous change logs to
+ fix problems like typo corrections or such.
+ To add a new change log entry, please see
+ https://pip.pypa.io/en/latest/development/#adding-a-news-entry
+ we named the news folder changelog
+
+.. towncrier release notes start
+
+Pytest 3.3.0 (2017-11-23)
+=========================
+
+Deprecations and Removals
+-------------------------
+
+- Pytest no longer supports Python **2.6** and **3.3**. Those Python versions
+ are EOL for some time now and incur maintenance and compatibility costs on
+ the pytest core team, and following up with the rest of the community we
+ decided that they will no longer be supported starting on this version. Users
+ which still require those versions should pin pytest to ``<3.3``. (`#2812
+ <https://github.com/pytest-dev/pytest/issues/2812>`_)
+
+- Remove internal ``_preloadplugins()`` function. This removal is part of the
+ ``pytest_namespace()`` hook deprecation. (`#2636
+ <https://github.com/pytest-dev/pytest/issues/2636>`_)
+
+- Internally change ``CallSpec2`` to have a list of marks instead of a broken
+ mapping of keywords. This removes the keywords attribute of the internal
+ ``CallSpec2`` class. (`#2672
+ <https://github.com/pytest-dev/pytest/issues/2672>`_)
+
+- Remove ParameterSet.deprecated_arg_dict - its not a public api and the lack
+ of the underscore was a naming error. (`#2675
+ <https://github.com/pytest-dev/pytest/issues/2675>`_)
+
+- Remove the internal multi-typed attribute ``Node._evalskip`` and replace it
+ with the boolean ``Node._skipped_by_mark``. (`#2767
+ <https://github.com/pytest-dev/pytest/issues/2767>`_)
+
+- The ``params`` list passed to ``pytest.fixture`` is now for
+ all effects considered immutable and frozen at the moment of the ``pytest.fixture``
+ call. Previously the list could be changed before the first invocation of the fixture
+ allowing for a form of dynamic parametrization (for example, updated from command-line options),
+ but this was an unwanted implementation detail which complicated the internals and prevented
+ some internal cleanup. See issue `#2959 <https://github.com/pytest-dev/pytest/issues/2959>`_
+ for details and a recommended workaround.
+
+Features
+--------
+
+- ``pytest_fixture_post_finalizer`` hook can now receive a ``request``
+ argument. (`#2124 <https://github.com/pytest-dev/pytest/issues/2124>`_)
+
+- Replace the old introspection code in compat.py that determines the available
+ arguments of fixtures with inspect.signature on Python 3 and
+ funcsigs.signature on Python 2. This should respect ``__signature__``
+ declarations on functions. (`#2267
+ <https://github.com/pytest-dev/pytest/issues/2267>`_)
+
+- Report tests with global ``pytestmark`` variable only once. (`#2549
+ <https://github.com/pytest-dev/pytest/issues/2549>`_)
+
+- Now pytest displays the total progress percentage while running tests. The
+ previous output style can be set by configuring the ``console_output_style``
+ setting to ``classic``. (`#2657 <https://github.com/pytest-dev/pytest/issues/2657>`_)
+
+- Match ``warns`` signature to ``raises`` by adding ``match`` keyword. (`#2708
+ <https://github.com/pytest-dev/pytest/issues/2708>`_)
+
+- Pytest now captures and displays output from the standard `logging` module.
+ The user can control the logging level to be captured by specifying options
+ in ``pytest.ini``, the command line and also during individual tests using
+ markers. Also, a ``caplog`` fixture is available that enables users to test
+ the captured log during specific tests (similar to ``capsys`` for example).
+ For more information, please see the `logging docs
+ <https://docs.pytest.org/en/latest/logging.html>`_. This feature was
+ introduced by merging the popular `pytest-catchlog
+ <https://pypi.org/project/pytest-catchlog/>`_ plugin, thanks to `Thomas Hisch
+ <https://github.com/thisch>`_. Be advised that during the merging the
+ backward compatibility interface with the defunct ``pytest-capturelog`` has
+ been dropped. (`#2794 <https://github.com/pytest-dev/pytest/issues/2794>`_)
+
+- Add ``allow_module_level`` kwarg to ``pytest.skip()``, enabling to skip the
+ whole module. (`#2808 <https://github.com/pytest-dev/pytest/issues/2808>`_)
+
+- Allow setting ``file_or_dir``, ``-c``, and ``-o`` in PYTEST_ADDOPTS. (`#2824
+ <https://github.com/pytest-dev/pytest/issues/2824>`_)
+
+- Return stdout/stderr capture results as a ``namedtuple``, so ``out`` and
+ ``err`` can be accessed by attribute. (`#2879
+ <https://github.com/pytest-dev/pytest/issues/2879>`_)
+
+- Add ``capfdbinary``, a version of ``capfd`` which returns bytes from
+ ``readouterr()``. (`#2923
+ <https://github.com/pytest-dev/pytest/issues/2923>`_)
+
+- Add ``capsysbinary`` a version of ``capsys`` which returns bytes from
+ ``readouterr()``. (`#2934
+ <https://github.com/pytest-dev/pytest/issues/2934>`_)
+
+- Implement feature to skip ``setup.py`` files when run with
+ ``--doctest-modules``. (`#502
+ <https://github.com/pytest-dev/pytest/issues/502>`_)
+
+
+Bug Fixes
+---------
+
+- Resume output capturing after ``capsys/capfd.disabled()`` context manager.
+ (`#1993 <https://github.com/pytest-dev/pytest/issues/1993>`_)
+
+- ``pytest_fixture_setup`` and ``pytest_fixture_post_finalizer`` hooks are now
+ called for all ``conftest.py`` files. (`#2124
+ <https://github.com/pytest-dev/pytest/issues/2124>`_)
+
+- If an exception happens while loading a plugin, pytest no longer hides the
+ original traceback. In python2 it will show the original traceback with a new
+ message that explains in which plugin. In python3 it will show 2 canonized
+ exceptions, the original exception while loading the plugin in addition to an
+ exception that PyTest throws about loading a plugin. (`#2491
+ <https://github.com/pytest-dev/pytest/issues/2491>`_)
+
+- ``capsys`` and ``capfd`` can now be used by other fixtures. (`#2709
+ <https://github.com/pytest-dev/pytest/issues/2709>`_)
+
+- Internal ``pytester`` plugin properly encodes ``bytes`` arguments to
+ ``utf-8``. (`#2738 <https://github.com/pytest-dev/pytest/issues/2738>`_)
+
+- ``testdir`` now uses use the same method used by ``tmpdir`` to create its
+ temporary directory. This changes the final structure of the ``testdir``
+ directory slightly, but should not affect usage in normal scenarios and
+ avoids a number of potential problems. (`#2751
+ <https://github.com/pytest-dev/pytest/issues/2751>`_)
+
+- Pytest no longer complains about warnings with unicode messages being
+ non-ascii compatible even for ascii-compatible messages. As a result of this,
+ warnings with unicode messages are converted first to an ascii representation
+ for safety. (`#2809 <https://github.com/pytest-dev/pytest/issues/2809>`_)
+
+- Change return value of pytest command when ``--maxfail`` is reached from
+ ``2`` (interrupted) to ``1`` (failed). (`#2845
+ <https://github.com/pytest-dev/pytest/issues/2845>`_)
+
+- Fix issue in assertion rewriting which could lead it to rewrite modules which
+ should not be rewritten. (`#2939
+ <https://github.com/pytest-dev/pytest/issues/2939>`_)
+
+- Handle marks without description in ``pytest.ini``. (`#2942
+ <https://github.com/pytest-dev/pytest/issues/2942>`_)
+
+
+Trivial/Internal Changes
+------------------------
+
+- pytest now depends on `attrs <https://pypi.org/project/attrs/>`_ for internal
+ structures to ease code maintainability. (`#2641
+ <https://github.com/pytest-dev/pytest/issues/2641>`_)
+
+- Refactored internal Python 2/3 compatibility code to use ``six``. (`#2642
+ <https://github.com/pytest-dev/pytest/issues/2642>`_)
+
+- Stop vendoring ``pluggy`` - we're missing out on its latest changes for not
+ much benefit (`#2719 <https://github.com/pytest-dev/pytest/issues/2719>`_)
+
+- Internal refactor: simplify ascii string escaping by using the
+ backslashreplace error handler in newer Python 3 versions. (`#2734
+ <https://github.com/pytest-dev/pytest/issues/2734>`_)
+
+- Remove unnecessary mark evaluator in unittest plugin (`#2767
+ <https://github.com/pytest-dev/pytest/issues/2767>`_)
+
+- Calls to ``Metafunc.addcall`` now emit a deprecation warning. This function
+ is scheduled to be removed in ``pytest-4.0``. (`#2876
+ <https://github.com/pytest-dev/pytest/issues/2876>`_)
+
+- Internal move of the parameterset extraction to a more maintainable place.
+ (`#2877 <https://github.com/pytest-dev/pytest/issues/2877>`_)
+
+- Internal refactoring to simplify scope node lookup. (`#2910
+ <https://github.com/pytest-dev/pytest/issues/2910>`_)
+
+- Configure ``pytest`` to prevent pip from installing pytest in unsupported
+ Python versions. (`#2922
+ <https://github.com/pytest-dev/pytest/issues/2922>`_)
+
+
+Pytest 3.2.5 (2017-11-15)
+=========================
+
+Bug Fixes
+---------
+
+- Remove ``py<1.5`` restriction from ``pytest`` as this can cause version
+ conflicts in some installations. (`#2926
+ <https://github.com/pytest-dev/pytest/issues/2926>`_)
+
+
+Pytest 3.2.4 (2017-11-13)
+=========================
+
+Bug Fixes
+---------
+
+- Fix the bug where running with ``--pyargs`` will result in items with
+ empty ``parent.nodeid`` if run from a different root directory. (`#2775
+ <https://github.com/pytest-dev/pytest/issues/2775>`_)
+
+- Fix issue with ``@pytest.parametrize`` if argnames was specified as keyword arguments.
+ (`#2819 <https://github.com/pytest-dev/pytest/issues/2819>`_)
+
+- Strip whitespace from marker names when reading them from INI config. (`#2856
+ <https://github.com/pytest-dev/pytest/issues/2856>`_)
+
+- Show full context of doctest source in the pytest output, if the line number of
+ failed example in the docstring is < 9. (`#2882
+ <https://github.com/pytest-dev/pytest/issues/2882>`_)
+
+- Match fixture paths against actual path segments in order to avoid matching folders which share a prefix.
+ (`#2836 <https://github.com/pytest-dev/pytest/issues/2836>`_)
+
+Improved Documentation
+----------------------
+
+- Introduce a dedicated section about conftest.py. (`#1505
+ <https://github.com/pytest-dev/pytest/issues/1505>`_)
+
+- Explicitly mention ``xpass`` in the documentation of ``xfail``. (`#1997
+ <https://github.com/pytest-dev/pytest/issues/1997>`_)
+
+- Append example for pytest.param in the example/parametrize document. (`#2658
+ <https://github.com/pytest-dev/pytest/issues/2658>`_)
+
+- Clarify language of proposal for fixtures parameters (`#2893
+ <https://github.com/pytest-dev/pytest/issues/2893>`_)
+
+- List python 3.6 in the documented supported versions in the getting started
+ document. (`#2903 <https://github.com/pytest-dev/pytest/issues/2903>`_)
+
+- Clarify the documentation of available fixture scopes. (`#538
+ <https://github.com/pytest-dev/pytest/issues/538>`_)
+
+- Add documentation about the ``python -m pytest`` invocation adding the
+ current directory to sys.path. (`#911
+ <https://github.com/pytest-dev/pytest/issues/911>`_)
+
+
+Pytest 3.2.3 (2017-10-03)
+=========================
+
+Bug Fixes
+---------
+
+- Fix crash in tab completion when no prefix is given. (`#2748
+ <https://github.com/pytest-dev/pytest/issues/2748>`_)
+
+- The equality checking function (``__eq__``) of ``MarkDecorator`` returns
+ ``False`` if one object is not an instance of ``MarkDecorator``. (`#2758
+ <https://github.com/pytest-dev/pytest/issues/2758>`_)
+
+- When running ``pytest --fixtures-per-test``: don't crash if an item has no
+ _fixtureinfo attribute (e.g. doctests) (`#2788
+ <https://github.com/pytest-dev/pytest/issues/2788>`_)
+
+
+Improved Documentation
+----------------------
+
+- In help text of ``-k`` option, add example of using ``not`` to not select
+ certain tests whose names match the provided expression. (`#1442
+ <https://github.com/pytest-dev/pytest/issues/1442>`_)
+
+- Add note in ``parametrize.rst`` about calling ``metafunc.parametrize``
+ multiple times. (`#1548 <https://github.com/pytest-dev/pytest/issues/1548>`_)
+
+
+Trivial/Internal Changes
+------------------------
+
+- Set ``xfail_strict=True`` in pytest's own test suite to catch expected
+ failures as soon as they start to pass. (`#2722
+ <https://github.com/pytest-dev/pytest/issues/2722>`_)
+
+- Fix typo in example of passing a callable to markers (in example/markers.rst)
+ (`#2765 <https://github.com/pytest-dev/pytest/issues/2765>`_)
+
+
+Pytest 3.2.2 (2017-09-06)
+=========================
+
+Bug Fixes
+---------
+
+- Calling the deprecated `request.getfuncargvalue()` now shows the source of
+ the call. (`#2681 <https://github.com/pytest-dev/pytest/issues/2681>`_)
+
+- Allow tests declared as ``@staticmethod`` to use fixtures. (`#2699
+ <https://github.com/pytest-dev/pytest/issues/2699>`_)
+
+- Fixed edge-case during collection: attributes which raised ``pytest.fail``
+ when accessed would abort the entire collection. (`#2707
+ <https://github.com/pytest-dev/pytest/issues/2707>`_)
+
+- Fix ``ReprFuncArgs`` with mixed unicode and UTF-8 args. (`#2731
+ <https://github.com/pytest-dev/pytest/issues/2731>`_)
+
+
+Improved Documentation
+----------------------
+
+- In examples on working with custom markers, add examples demonstrating the
+ usage of ``pytest.mark.MARKER_NAME.with_args`` in comparison with
+ ``pytest.mark.MARKER_NAME.__call__`` (`#2604
+ <https://github.com/pytest-dev/pytest/issues/2604>`_)
+
+- In one of the simple examples, use `pytest_collection_modifyitems()` to skip
+ tests based on a command-line option, allowing its sharing while preventing a
+ user error when acessing `pytest.config` before the argument parsing. (`#2653
+ <https://github.com/pytest-dev/pytest/issues/2653>`_)
+
+
+Trivial/Internal Changes
+------------------------
+
+- Fixed minor error in 'Good Practices/Manual Integration' code snippet.
+ (`#2691 <https://github.com/pytest-dev/pytest/issues/2691>`_)
+
+- Fixed typo in goodpractices.rst. (`#2721
+ <https://github.com/pytest-dev/pytest/issues/2721>`_)
+
+- Improve user guidance regarding ``--resultlog`` deprecation. (`#2739
+ <https://github.com/pytest-dev/pytest/issues/2739>`_)
+
+
+Pytest 3.2.1 (2017-08-08)
+=========================
+
+Bug Fixes
+---------
+
+- Fixed small terminal glitch when collecting a single test item. (`#2579
+ <https://github.com/pytest-dev/pytest/issues/2579>`_)
+
+- Correctly consider ``/`` as the file separator to automatically mark plugin
+ files for rewrite on Windows. (`#2591 <https://github.com/pytest-
+ dev/pytest/issues/2591>`_)
+
+- Properly escape test names when setting ``PYTEST_CURRENT_TEST`` environment
+ variable. (`#2644 <https://github.com/pytest-dev/pytest/issues/2644>`_)
+
+- Fix error on Windows and Python 3.6+ when ``sys.stdout`` has been replaced
+ with a stream-like object which does not implement the full ``io`` module
+ buffer protocol. In particular this affects ``pytest-xdist`` users on the
+ aforementioned platform. (`#2666 <https://github.com/pytest-
+ dev/pytest/issues/2666>`_)
+
+
+Improved Documentation
+----------------------
+
+- Explicitly document which pytest features work with ``unittest``. (`#2626
+ <https://github.com/pytest-dev/pytest/issues/2626>`_)
+
+
+Pytest 3.2.0 (2017-07-30)
+=========================
+
+Deprecations and Removals
+-------------------------
+
+- ``pytest.approx`` no longer supports ``>``, ``>=``, ``<`` and ``<=``
+ operators to avoid surprising/inconsistent behavior. See `the approx docs
+ <https://docs.pytest.org/en/latest/builtin.html#pytest.approx>`_ for more
+ information. (`#2003 <https://github.com/pytest-dev/pytest/issues/2003>`_)
+
+- All old-style specific behavior in current classes in the pytest's API is
+ considered deprecated at this point and will be removed in a future release.
+ This affects Python 2 users only and in rare situations. (`#2147
+ <https://github.com/pytest-dev/pytest/issues/2147>`_)
+
+- A deprecation warning is now raised when using marks for parameters
+ in ``pytest.mark.parametrize``. Use ``pytest.param`` to apply marks to
+ parameters instead. (`#2427 <https://github.com/pytest-dev/pytest/issues/2427>`_)
+
+
+Features
+--------
+
+- Add support for numpy arrays (and dicts) to approx. (`#1994
+ <https://github.com/pytest-dev/pytest/issues/1994>`_)
+
+- Now test function objects have a ``pytestmark`` attribute containing a list
+ of marks applied directly to the test function, as opposed to marks inherited
+ from parent classes or modules. (`#2516 <https://github.com/pytest-
+ dev/pytest/issues/2516>`_)
+
+- Collection ignores local virtualenvs by default; `--collect-in-virtualenv`
+ overrides this behavior. (`#2518 <https://github.com/pytest-
+ dev/pytest/issues/2518>`_)
+
+- Allow class methods decorated as ``@staticmethod`` to be candidates for
+ collection as a test function. (Only for Python 2.7 and above. Python 2.6
+ will still ignore static methods.) (`#2528 <https://github.com/pytest-
+ dev/pytest/issues/2528>`_)
+
+- Introduce ``mark.with_args`` in order to allow passing functions/classes as
+ sole argument to marks. (`#2540 <https://github.com/pytest-
+ dev/pytest/issues/2540>`_)
+
+- New ``cache_dir`` ini option: sets the directory where the contents of the
+ cache plugin are stored. Directory may be relative or absolute path: if relative path, then
+ directory is created relative to ``rootdir``, otherwise it is used as is.
+ Additionally path may contain environment variables which are expanded during
+ runtime. (`#2543 <https://github.com/pytest-dev/pytest/issues/2543>`_)
+
+- Introduce the ``PYTEST_CURRENT_TEST`` environment variable that is set with
+ the ``nodeid`` and stage (``setup``, ``call`` and ``teardown``) of the test
+ being currently executed. See the `documentation
+ <https://docs.pytest.org/en/latest/example/simple.html#pytest-current-test-
+ environment-variable>`_ for more info. (`#2583 <https://github.com/pytest-
+ dev/pytest/issues/2583>`_)
+
+- Introduced ``@pytest.mark.filterwarnings`` mark which allows overwriting the
+ warnings filter on a per test, class or module level. See the `docs
+ <https://docs.pytest.org/en/latest/warnings.html#pytest-mark-
+ filterwarnings>`_ for more information. (`#2598 <https://github.com/pytest-
+ dev/pytest/issues/2598>`_)
+
+- ``--last-failed`` now remembers forever when a test has failed and only
+ forgets it if it passes again. This makes it easy to fix a test suite by
+ selectively running files and fixing tests incrementally. (`#2621
+ <https://github.com/pytest-dev/pytest/issues/2621>`_)
+
+- New ``pytest_report_collectionfinish`` hook which allows plugins to add
+ messages to the terminal reporting after collection has been finished
+ successfully. (`#2622 <https://github.com/pytest-dev/pytest/issues/2622>`_)
+
+- Added support for `PEP-415's <https://www.python.org/dev/peps/pep-0415/>`_
+ ``Exception.__suppress_context__``. Now if a ``raise exception from None`` is
+ caught by pytest, pytest will no longer chain the context in the test report.
+ The behavior now matches Python's traceback behavior. (`#2631
+ <https://github.com/pytest-dev/pytest/issues/2631>`_)
+
+- Exceptions raised by ``pytest.fail``, ``pytest.skip`` and ``pytest.xfail``
+ now subclass BaseException, making them harder to be caught unintentionally
+ by normal code. (`#580 <https://github.com/pytest-dev/pytest/issues/580>`_)
+
+
+Bug Fixes
+---------
+
+- Set ``stdin`` to a closed ``PIPE`` in ``pytester.py.Testdir.popen()`` for
+ avoid unwanted interactive ``pdb`` (`#2023 <https://github.com/pytest-
+ dev/pytest/issues/2023>`_)
+
+- Add missing ``encoding`` attribute to ``sys.std*`` streams when using
+ ``capsys`` capture mode. (`#2375 <https://github.com/pytest-
+ dev/pytest/issues/2375>`_)
+
+- Fix terminal color changing to black on Windows if ``colorama`` is imported
+ in a ``conftest.py`` file. (`#2510 <https://github.com/pytest-
+ dev/pytest/issues/2510>`_)
+
+- Fix line number when reporting summary of skipped tests. (`#2548
+ <https://github.com/pytest-dev/pytest/issues/2548>`_)
+
+- capture: ensure that EncodedFile.name is a string. (`#2555
+ <https://github.com/pytest-dev/pytest/issues/2555>`_)
+
+- The options ``--fixtures`` and ``--fixtures-per-test`` will now keep
+ indentation within docstrings. (`#2574 <https://github.com/pytest-
+ dev/pytest/issues/2574>`_)
+
+- doctests line numbers are now reported correctly, fixing `pytest-sugar#122
+ <https://github.com/Frozenball/pytest-sugar/issues/122>`_. (`#2610
+ <https://github.com/pytest-dev/pytest/issues/2610>`_)
+
+- Fix non-determinism in order of fixture collection. Adds new dependency
+ (ordereddict) for Python 2.6. (`#920 <https://github.com/pytest-
+ dev/pytest/issues/920>`_)
+
+
+Improved Documentation
+----------------------
+
+- Clarify ``pytest_configure`` hook call order. (`#2539
+ <https://github.com/pytest-dev/pytest/issues/2539>`_)
+
+- Extend documentation for testing plugin code with the ``pytester`` plugin.
+ (`#971 <https://github.com/pytest-dev/pytest/issues/971>`_)
+
+
+Trivial/Internal Changes
+------------------------
+
+- Update help message for ``--strict`` to make it clear it only deals with
+ unregistered markers, not warnings. (`#2444 <https://github.com/pytest-
+ dev/pytest/issues/2444>`_)
+
+- Internal code move: move code for pytest.approx/pytest.raises to own files in
+ order to cut down the size of python.py (`#2489 <https://github.com/pytest-
+ dev/pytest/issues/2489>`_)
+
+- Renamed the utility function ``_pytest.compat._escape_strings`` to
+ ``_ascii_escaped`` to better communicate the function's purpose. (`#2533
+ <https://github.com/pytest-dev/pytest/issues/2533>`_)
+
+- Improve error message for CollectError with skip/skipif. (`#2546
+ <https://github.com/pytest-dev/pytest/issues/2546>`_)
+
+- Emit warning about ``yield`` tests being deprecated only once per generator.
+ (`#2562 <https://github.com/pytest-dev/pytest/issues/2562>`_)
+
+- Ensure final collected line doesn't include artifacts of previous write.
+ (`#2571 <https://github.com/pytest-dev/pytest/issues/2571>`_)
+
+- Fixed all flake8 errors and warnings. (`#2581 <https://github.com/pytest-
+ dev/pytest/issues/2581>`_)
+
+- Added ``fix-lint`` tox environment to run automatic pep8 fixes on the code.
+ (`#2582 <https://github.com/pytest-dev/pytest/issues/2582>`_)
+
+- Turn warnings into errors in pytest's own test suite in order to catch
+ regressions due to deprecations more promptly. (`#2588
+ <https://github.com/pytest-dev/pytest/issues/2588>`_)
+
+- Show multiple issue links in CHANGELOG entries. (`#2620
+ <https://github.com/pytest-dev/pytest/issues/2620>`_)
+
+
+Pytest 3.1.3 (2017-07-03)
+=========================
+
+Bug Fixes
+---------
+
+- Fix decode error in Python 2 for doctests in docstrings. (`#2434
+ <https://github.com/pytest-dev/pytest/issues/2434>`_)
+
+- Exceptions raised during teardown by finalizers are now suppressed until all
+ finalizers are called, with the initial exception reraised. (`#2440
+ <https://github.com/pytest-dev/pytest/issues/2440>`_)
+
+- Fix incorrect "collected items" report when specifying tests on the command-
+ line. (`#2464 <https://github.com/pytest-dev/pytest/issues/2464>`_)
+
+- ``deprecated_call`` in context-manager form now captures deprecation warnings
+ even if the same warning has already been raised. Also, ``deprecated_call``
+ will always produce the same error message (previously it would produce
+ different messages in context-manager vs. function-call mode). (`#2469
+ <https://github.com/pytest-dev/pytest/issues/2469>`_)
+
+- Fix issue where paths collected by pytest could have triple leading ``/``
+ characters. (`#2475 <https://github.com/pytest-dev/pytest/issues/2475>`_)
+
+- Fix internal error when trying to detect the start of a recursive traceback.
+ (`#2486 <https://github.com/pytest-dev/pytest/issues/2486>`_)
+
+
+Improved Documentation
+----------------------
+
+- Explicitly state for which hooks the calls stop after the first non-None
+ result. (`#2493 <https://github.com/pytest-dev/pytest/issues/2493>`_)
+
+
+Trivial/Internal Changes
+------------------------
+
+- Create invoke tasks for updating the vendored packages. (`#2474
+ <https://github.com/pytest-dev/pytest/issues/2474>`_)
+
+- Update copyright dates in LICENSE, README.rst and in the documentation.
+ (`#2499 <https://github.com/pytest-dev/pytest/issues/2499>`_)
+
+
+Pytest 3.1.2 (2017-06-08)
+=========================
+
+Bug Fixes
+---------
+
+- Required options added via ``pytest_addoption`` will no longer prevent using
+ --help without passing them. (#1999)
+
+- Respect ``python_files`` in assertion rewriting. (#2121)
+
+- Fix recursion error detection when frames in the traceback contain objects
+ that can't be compared (like ``numpy`` arrays). (#2459)
+
+- ``UnicodeWarning`` is issued from the internal pytest warnings plugin only
+ when the message contains non-ascii unicode (Python 2 only). (#2463)
+
+- Added a workaround for Python 3.6 ``WindowsConsoleIO`` breaking due to Pytests's
+ ``FDCapture``. Other code using console handles might still be affected by the
+ very same issue and might require further workarounds/fixes, i.e. ``colorama``.
+ (#2467)
+
+
+Improved Documentation
+----------------------
+
+- Fix internal API links to ``pluggy`` objects. (#2331)
+
+- Make it clear that ``pytest.xfail`` stops test execution at the calling point
+ and improve overall flow of the ``skipping`` docs. (#810)
+
+
+Pytest 3.1.1 (2017-05-30)
+=========================
+
+Bug Fixes
+---------
+
+- pytest warning capture no longer overrides existing warning filters. The
+ previous behaviour would override all filters and caused regressions in test
+ suites which configure warning filters to match their needs. Note that as a
+ side-effect of this is that ``DeprecationWarning`` and
+ ``PendingDeprecationWarning`` are no longer shown by default. (#2430)
+
+- Fix issue with non-ascii contents in doctest text files. (#2434)
+
+- Fix encoding errors for unicode warnings in Python 2. (#2436)
+
+- ``pytest.deprecated_call`` now captures ``PendingDeprecationWarning`` in
+ context manager form. (#2441)
+
+
+Improved Documentation
+----------------------
+
+- Addition of towncrier for changelog management. (#2390)
+
+
+3.1.0 (2017-05-22)
+==================
+
+
+New Features
+------------
+
+* The ``pytest-warnings`` plugin has been integrated into the core and now ``pytest`` automatically
+ captures and displays warnings at the end of the test session.
+
+ .. warning::
+
+ This feature may disrupt test suites which apply and treat warnings themselves, and can be
+ disabled in your ``pytest.ini``:
+
+ .. code-block:: ini
+
+ [pytest]
+ addopts = -p no:warnings
+
+ See the `warnings documentation page <https://docs.pytest.org/en/latest/warnings.html>`_ for more
+ information.
+
+ Thanks `@nicoddemus`_ for the PR.
+
+* Added ``junit_suite_name`` ini option to specify root ``<testsuite>`` name for JUnit XML reports (`#533`_).
+
+* Added an ini option ``doctest_encoding`` to specify which encoding to use for doctest files.
+ Thanks `@wheerd`_ for the PR (`#2101`_).
+
+* ``pytest.warns`` now checks for subclass relationship rather than
+ class equality. Thanks `@lesteve`_ for the PR (`#2166`_)
+
+* ``pytest.raises`` now asserts that the error message matches a text or regex
+ with the ``match`` keyword argument. Thanks `@Kriechi`_ for the PR.
+
+* ``pytest.param`` can be used to declare test parameter sets with marks and test ids.
+ Thanks `@RonnyPfannschmidt`_ for the PR.
+
+
+Changes
+-------
+
+* remove all internal uses of pytest_namespace hooks,
+ this is to prepare the removal of preloadconfig in pytest 4.0
+ Thanks to `@RonnyPfannschmidt`_ for the PR.
+
+* pytest now warns when a callable ids raises in a parametrized test. Thanks `@fogo`_ for the PR.
+
+* It is now possible to skip test classes from being collected by setting a
+ ``__test__`` attribute to ``False`` in the class body (`#2007`_). Thanks
+ to `@syre`_ for the report and `@lwm`_ for the PR.
+
+* Change junitxml.py to produce reports that comply with Junitxml schema.
+ If the same test fails with failure in call and then errors in teardown
+ we split testcase element into two, one containing the error and the other
+ the failure. (`#2228`_) Thanks to `@kkoukiou`_ for the PR.
+
+* Testcase reports with a ``url`` attribute will now properly write this to junitxml.
+ Thanks `@fushi`_ for the PR (`#1874`_).
+
+* Remove common items from dict comparision output when verbosity=1. Also update
+ the truncation message to make it clearer that pytest truncates all
+ assertion messages if verbosity < 2 (`#1512`_).
+ Thanks `@mattduck`_ for the PR
+
+* ``--pdbcls`` no longer implies ``--pdb``. This makes it possible to use
+ ``addopts=--pdbcls=module.SomeClass`` on ``pytest.ini``. Thanks `@davidszotten`_ for
+ the PR (`#1952`_).
+
+* fix `#2013`_: turn RecordedWarning into ``namedtuple``,
+ to give it a comprehensible repr while preventing unwarranted modification.
+
+* fix `#2208`_: ensure a iteration limit for _pytest.compat.get_real_func.
+ Thanks `@RonnyPfannschmidt`_ for the report and PR.
+
+* Hooks are now verified after collection is complete, rather than right after loading installed plugins. This
+ makes it easy to write hooks for plugins which will be loaded during collection, for example using the
+ ``pytest_plugins`` special variable (`#1821`_).
+ Thanks `@nicoddemus`_ for the PR.
+
+* Modify ``pytest_make_parametrize_id()`` hook to accept ``argname`` as an
+ additional parameter.
+ Thanks `@unsignedint`_ for the PR.
+
+* Add ``venv`` to the default ``norecursedirs`` setting.
+ Thanks `@The-Compiler`_ for the PR.
+
+* ``PluginManager.import_plugin`` now accepts unicode plugin names in Python 2.
+ Thanks `@reutsharabani`_ for the PR.
+
+* fix `#2308`_: When using both ``--lf`` and ``--ff``, only the last failed tests are run.
+ Thanks `@ojii`_ for the PR.
+
+* Replace minor/patch level version numbers in the documentation with placeholders.
+ This significantly reduces change-noise as different contributors regnerate
+ the documentation on different platforms.
+ Thanks `@RonnyPfannschmidt`_ for the PR.
+
+* fix `#2391`_: consider pytest_plugins on all plugin modules
+ Thanks `@RonnyPfannschmidt`_ for the PR.
+
+
+Bug Fixes
+---------
+
+* Fix ``AttributeError`` on ``sys.stdout.buffer`` / ``sys.stderr.buffer``
+ while using ``capsys`` fixture in python 3. (`#1407`_).
+ Thanks to `@asottile`_.
+
+* Change capture.py's ``DontReadFromInput`` class to throw ``io.UnsupportedOperation`` errors rather
+ than ValueErrors in the ``fileno`` method (`#2276`_).
+ Thanks `@metasyn`_ and `@vlad-dragos`_ for the PR.
+
+* Fix exception formatting while importing modules when the exception message
+ contains non-ascii characters (`#2336`_).
+ Thanks `@fabioz`_ for the report and `@nicoddemus`_ for the PR.
+
+* Added documentation related to issue (`#1937`_)
+ Thanks `@skylarjhdownes`_ for the PR.
+
+* Allow collecting files with any file extension as Python modules (`#2369`_).
+ Thanks `@Kodiologist`_ for the PR.
+
+* Show the correct error message when collect "parametrize" func with wrong args (`#2383`_).
+ Thanks `@The-Compiler`_ for the report and `@robin0371`_ for the PR.
+
+
+.. _@davidszotten: https://github.com/davidszotten
+.. _@fabioz: https://github.com/fabioz
+.. _@fogo: https://github.com/fogo
+.. _@fushi: https://github.com/fushi
+.. _@Kodiologist: https://github.com/Kodiologist
+.. _@Kriechi: https://github.com/Kriechi
+.. _@mandeep: https://github.com/mandeep
+.. _@mattduck: https://github.com/mattduck
+.. _@metasyn: https://github.com/metasyn
+.. _@MichalTHEDUDE: https://github.com/MichalTHEDUDE
+.. _@ojii: https://github.com/ojii
+.. _@reutsharabani: https://github.com/reutsharabani
+.. _@robin0371: https://github.com/robin0371
+.. _@skylarjhdownes: https://github.com/skylarjhdownes
+.. _@unsignedint: https://github.com/unsignedint
+.. _@wheerd: https://github.com/wheerd
+
+
+.. _#1407: https://github.com/pytest-dev/pytest/issues/1407
+.. _#1512: https://github.com/pytest-dev/pytest/issues/1512
+.. _#1821: https://github.com/pytest-dev/pytest/issues/1821
+.. _#1874: https://github.com/pytest-dev/pytest/pull/1874
+.. _#1937: https://github.com/pytest-dev/pytest/issues/1937
+.. _#1952: https://github.com/pytest-dev/pytest/pull/1952
+.. _#2007: https://github.com/pytest-dev/pytest/issues/2007
+.. _#2013: https://github.com/pytest-dev/pytest/issues/2013
+.. _#2101: https://github.com/pytest-dev/pytest/pull/2101
+.. _#2166: https://github.com/pytest-dev/pytest/pull/2166
+.. _#2208: https://github.com/pytest-dev/pytest/issues/2208
+.. _#2228: https://github.com/pytest-dev/pytest/issues/2228
+.. _#2276: https://github.com/pytest-dev/pytest/issues/2276
+.. _#2308: https://github.com/pytest-dev/pytest/issues/2308
+.. _#2336: https://github.com/pytest-dev/pytest/issues/2336
+.. _#2369: https://github.com/pytest-dev/pytest/issues/2369
+.. _#2383: https://github.com/pytest-dev/pytest/issues/2383
+.. _#2391: https://github.com/pytest-dev/pytest/issues/2391
+.. _#533: https://github.com/pytest-dev/pytest/issues/533
+
+
+
+3.0.7 (2017-03-14)
+==================
+
+
+* Fix issue in assertion rewriting breaking due to modules silently discarding
+ other modules when importing fails
+ Notably, importing the ``anydbm`` module is fixed. (`#2248`_).
+ Thanks `@pfhayes`_ for the PR.
+
+* junitxml: Fix problematic case where system-out tag occured twice per testcase
+ element in the XML report. Thanks `@kkoukiou`_ for the PR.
+
+* Fix regression, pytest now skips unittest correctly if run with ``--pdb``
+ (`#2137`_). Thanks to `@gst`_ for the report and `@mbyt`_ for the PR.
+
+* Ignore exceptions raised from descriptors (e.g. properties) during Python test collection (`#2234`_).
+ Thanks to `@bluetech`_.
+
+* ``--override-ini`` now correctly overrides some fundamental options like ``python_files`` (`#2238`_).
+ Thanks `@sirex`_ for the report and `@nicoddemus`_ for the PR.
+
+* Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_).
+ Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR.
+
+* Fix internal errors when an unprintable ``AssertionError`` is raised inside a test.
+ Thanks `@omerhadari`_ for the PR.
+
+* Skipping plugin now also works with test items generated by custom collectors (`#2231`_).
+ Thanks to `@vidartf`_.
+
+* Fix trailing whitespace in console output if no .ini file presented (`#2281`_). Thanks `@fbjorn`_ for the PR.
+
+* Conditionless ``xfail`` markers no longer rely on the underlying test item
+ being an instance of ``PyobjMixin``, and can therefore apply to tests not
+ collected by the built-in python test collector. Thanks `@barneygale`_ for the
+ PR.
+
+
+.. _@pfhayes: https://github.com/pfhayes
+.. _@bluetech: https://github.com/bluetech
+.. _@gst: https://github.com/gst
+.. _@sirex: https://github.com/sirex
+.. _@vidartf: https://github.com/vidartf
+.. _@kkoukiou: https://github.com/KKoukiou
+.. _@omerhadari: https://github.com/omerhadari
+.. _@fbjorn: https://github.com/fbjorn
+
+.. _#2248: https://github.com/pytest-dev/pytest/issues/2248
+.. _#2137: https://github.com/pytest-dev/pytest/issues/2137
+.. _#2160: https://github.com/pytest-dev/pytest/issues/2160
+.. _#2231: https://github.com/pytest-dev/pytest/issues/2231
+.. _#2234: https://github.com/pytest-dev/pytest/issues/2234
+.. _#2238: https://github.com/pytest-dev/pytest/issues/2238
+.. _#2281: https://github.com/pytest-dev/pytest/issues/2281
+
+.. _PEP-479: https://www.python.org/dev/peps/pep-0479/
+
+
+3.0.6 (2017-01-22)
+==================
+
+* pytest no longer generates ``PendingDeprecationWarning`` from its own operations, which was introduced by mistake in version ``3.0.5`` (`#2118`_).
+ Thanks to `@nicoddemus`_ for the report and `@RonnyPfannschmidt`_ for the PR.
+
+
+* pytest no longer recognizes coroutine functions as yield tests (`#2129`_).
+ Thanks to `@malinoff`_ for the PR.
+
+* Plugins loaded by the ``PYTEST_PLUGINS`` environment variable are now automatically
+ considered for assertion rewriting (`#2185`_).
+ Thanks `@nicoddemus`_ for the PR.
+
+* Improve error message when pytest.warns fails (`#2150`_). The type(s) of the
+ expected warnings and the list of caught warnings is added to the
+ error message. Thanks `@lesteve`_ for the PR.
+
+* Fix ``pytester`` internal plugin to work correctly with latest versions of
+ ``zope.interface`` (`#1989`_). Thanks `@nicoddemus`_ for the PR.
+
+* Assert statements of the ``pytester`` plugin again benefit from assertion rewriting (`#1920`_).
+ Thanks `@RonnyPfannschmidt`_ for the report and `@nicoddemus`_ for the PR.
+
+* Specifying tests with colons like ``test_foo.py::test_bar`` for tests in
+ subdirectories with ini configuration files now uses the correct ini file
+ (`#2148`_). Thanks `@pelme`_.
+
+* Fail ``testdir.runpytest().assert_outcomes()`` explicitly if the pytest
+ terminal output it relies on is missing. Thanks to `@eli-b`_ for the PR.
+
+
+.. _@barneygale: https://github.com/barneygale
+.. _@lesteve: https://github.com/lesteve
+.. _@malinoff: https://github.com/malinoff
+.. _@pelme: https://github.com/pelme
+.. _@eli-b: https://github.com/eli-b
+
+.. _#2118: https://github.com/pytest-dev/pytest/issues/2118
+
+.. _#1989: https://github.com/pytest-dev/pytest/issues/1989
+.. _#1920: https://github.com/pytest-dev/pytest/issues/1920
+.. _#2129: https://github.com/pytest-dev/pytest/issues/2129
+.. _#2148: https://github.com/pytest-dev/pytest/issues/2148
+.. _#2150: https://github.com/pytest-dev/pytest/issues/2150
+.. _#2185: https://github.com/pytest-dev/pytest/issues/2185
+
+
+3.0.5 (2016-12-05)
+==================
+
+* Add warning when not passing ``option=value`` correctly to ``-o/--override-ini`` (`#2105`_).
+ Also improved the help documentation. Thanks to `@mbukatov`_ for the report and
+ `@lwm`_ for the PR.
+
+* Now ``--confcutdir`` and ``--junit-xml`` are properly validated if they are directories
+ and filenames, respectively (`#2089`_ and `#2078`_). Thanks to `@lwm`_ for the PR.
+
+* Add hint to error message hinting possible missing ``__init__.py`` (`#478`_). Thanks `@DuncanBetts`_.
+
+* More accurately describe when fixture finalization occurs in documentation (`#687`_). Thanks `@DuncanBetts`_.
+
+* Provide ``:ref:`` targets for ``recwarn.rst`` so we can use intersphinx referencing.
+ Thanks to `@dupuy`_ for the report and `@lwm`_ for the PR.
+
+* In Python 2, use a simple ``+-`` ASCII string in the string representation of ``pytest.approx`` (for example ``"4 +- 4.0e-06"``)
+ because it is brittle to handle that in different contexts and representations internally in pytest
+ which can result in bugs such as `#2111`_. In Python 3, the representation still uses ``±`` (for example ``4 ± 4.0e-06``).
+ Thanks `@kerrick-lyft`_ for the report and `@nicoddemus`_ for the PR.
+
+* Using ``item.Function``, ``item.Module``, etc., is now issuing deprecation warnings, prefer
+ ``pytest.Function``, ``pytest.Module``, etc., instead (`#2034`_).
+ Thanks `@nmundar`_ for the PR.
+
+* Fix error message using ``approx`` with complex numbers (`#2082`_).
+ Thanks `@adler-j`_ for the report and `@nicoddemus`_ for the PR.
+
+* Fixed false-positives warnings from assertion rewrite hook for modules imported more than
+ once by the ``pytest_plugins`` mechanism.
+ Thanks `@nicoddemus`_ for the PR.
+
+* Remove an internal cache which could cause hooks from ``conftest.py`` files in
+ sub-directories to be called in other directories incorrectly (`#2016`_).
+ Thanks `@d-b-w`_ for the report and `@nicoddemus`_ for the PR.
+
+* Remove internal code meant to support earlier Python 3 versions that produced the side effect
+ of leaving ``None`` in ``sys.modules`` when expressions were evaluated by pytest (for example passing a condition
+ as a string to ``pytest.mark.skipif``)(`#2103`_).
+ Thanks `@jaraco`_ for the report and `@nicoddemus`_ for the PR.
+
+* Cope gracefully with a .pyc file with no matching .py file (`#2038`_). Thanks
+ `@nedbat`_.
+
+.. _@syre: https://github.com/syre
+.. _@adler-j: https://github.com/adler-j
+.. _@d-b-w: https://bitbucket.org/d-b-w/
+.. _@DuncanBetts: https://github.com/DuncanBetts
+.. _@dupuy: https://bitbucket.org/dupuy/
+.. _@kerrick-lyft: https://github.com/kerrick-lyft
+.. _@lwm: https://github.com/lwm
+.. _@mbukatov: https://github.com/mbukatov
+.. _@nedbat: https://github.com/nedbat
+.. _@nmundar: https://github.com/nmundar
+
+.. _#2016: https://github.com/pytest-dev/pytest/issues/2016
+.. _#2034: https://github.com/pytest-dev/pytest/issues/2034
+.. _#2038: https://github.com/pytest-dev/pytest/issues/2038
+.. _#2078: https://github.com/pytest-dev/pytest/issues/2078
+.. _#2082: https://github.com/pytest-dev/pytest/issues/2082
+.. _#2089: https://github.com/pytest-dev/pytest/issues/2089
+.. _#2103: https://github.com/pytest-dev/pytest/issues/2103
+.. _#2105: https://github.com/pytest-dev/pytest/issues/2105
+.. _#2111: https://github.com/pytest-dev/pytest/issues/2111
+.. _#478: https://github.com/pytest-dev/pytest/issues/478
+.. _#687: https://github.com/pytest-dev/pytest/issues/687
+
+
+3.0.4 (2016-11-09)
+==================
+
+* Import errors when collecting test modules now display the full traceback (`#1976`_).
+ Thanks `@cwitty`_ for the report and `@nicoddemus`_ for the PR.
+
+* Fix confusing command-line help message for custom options with two or more ``metavar`` properties (`#2004`_).
+ Thanks `@okulynyak`_ and `@davehunt`_ for the report and `@nicoddemus`_ for the PR.
+
+* When loading plugins, import errors which contain non-ascii messages are now properly handled in Python 2 (`#1998`_).
+ Thanks `@nicoddemus`_ for the PR.
+
+* Fixed cyclic reference when ``pytest.raises`` is used in context-manager form (`#1965`_). Also as a
+ result of this fix, ``sys.exc_info()`` is left empty in both context-manager and function call usages.
+ Previously, ``sys.exc_info`` would contain the exception caught by the context manager,
+ even when the expected exception occurred.
+ Thanks `@MSeifert04`_ for the report and the PR.
+
+* Fixed false-positives warnings from assertion rewrite hook for modules that were rewritten but
+ were later marked explicitly by ``pytest.register_assert_rewrite``
+ or implicitly as a plugin (`#2005`_).
+ Thanks `@RonnyPfannschmidt`_ for the report and `@nicoddemus`_ for the PR.
+
+* Report teardown output on test failure (`#442`_).
+ Thanks `@matclab`_ for the PR.
+
+* Fix teardown error message in generated xUnit XML.
+ Thanks `@gdyuldin`_ for the PR.
+
+* Properly handle exceptions in ``multiprocessing`` tasks (`#1984`_).
+ Thanks `@adborden`_ for the report and `@nicoddemus`_ for the PR.
+
+* Clean up unittest TestCase objects after tests are complete (`#1649`_).
+ Thanks `@d_b_w`_ for the report and PR.
+
+
+.. _@adborden: https://github.com/adborden
+.. _@cwitty: https://github.com/cwitty
+.. _@d_b_w: https://github.com/d_b_w
+.. _@gdyuldin: https://github.com/gdyuldin
+.. _@matclab: https://github.com/matclab
+.. _@MSeifert04: https://github.com/MSeifert04
+.. _@okulynyak: https://github.com/okulynyak
+
+.. _#442: https://github.com/pytest-dev/pytest/issues/442
+.. _#1965: https://github.com/pytest-dev/pytest/issues/1965
+.. _#1976: https://github.com/pytest-dev/pytest/issues/1976
+.. _#1984: https://github.com/pytest-dev/pytest/issues/1984
+.. _#1998: https://github.com/pytest-dev/pytest/issues/1998
+.. _#2004: https://github.com/pytest-dev/pytest/issues/2004
+.. _#2005: https://github.com/pytest-dev/pytest/issues/2005
+.. _#1649: https://github.com/pytest-dev/pytest/issues/1649
+
+
+3.0.3 (2016-09-28)
+==================
+
+* The ``ids`` argument to ``parametrize`` again accepts ``unicode`` strings
+ in Python 2 (`#1905`_).
+ Thanks `@philpep`_ for the report and `@nicoddemus`_ for the PR.
+
+* Assertions are now being rewritten for plugins in development mode
+ (``pip install -e``) (`#1934`_).
+ Thanks `@nicoddemus`_ for the PR.
+
+* Fix pkg_resources import error in Jython projects (`#1853`_).
+ Thanks `@raquel-ucl`_ for the PR.
+
+* Got rid of ``AttributeError: 'Module' object has no attribute '_obj'`` exception
+ in Python 3 (`#1944`_).
+ Thanks `@axil`_ for the PR.
+
+* Explain a bad scope value passed to ``@fixture`` declarations or
+ a ``MetaFunc.parametrize()`` call. Thanks `@tgoodlet`_ for the PR.
+
+* This version includes ``pluggy-0.4.0``, which correctly handles
+ ``VersionConflict`` errors in plugins (`#704`_).
+ Thanks `@nicoddemus`_ for the PR.
+
+
+.. _@philpep: https://github.com/philpep
+.. _@raquel-ucl: https://github.com/raquel-ucl
+.. _@axil: https://github.com/axil
+.. _@tgoodlet: https://github.com/tgoodlet
+.. _@vlad-dragos: https://github.com/vlad-dragos
+
+.. _#1853: https://github.com/pytest-dev/pytest/issues/1853
+.. _#1905: https://github.com/pytest-dev/pytest/issues/1905
+.. _#1934: https://github.com/pytest-dev/pytest/issues/1934
+.. _#1944: https://github.com/pytest-dev/pytest/issues/1944
+.. _#704: https://github.com/pytest-dev/pytest/issues/704
+
+
+
+
+3.0.2 (2016-09-01)
+==================
+
+* Improve error message when passing non-string ids to ``pytest.mark.parametrize`` (`#1857`_).
+ Thanks `@okken`_ for the report and `@nicoddemus`_ for the PR.
+
+* Add ``buffer`` attribute to stdin stub class ``pytest.capture.DontReadFromInput``
+ Thanks `@joguSD`_ for the PR.
+
+* Fix ``UnicodeEncodeError`` when string comparison with unicode has failed. (`#1864`_)
+ Thanks `@AiOO`_ for the PR.
+
+* ``pytest_plugins`` is now handled correctly if defined as a string (as opposed as
+ a sequence of strings) when modules are considered for assertion rewriting.
+ Due to this bug, much more modules were being rewritten than necessary
+ if a test suite uses ``pytest_plugins`` to load internal plugins (`#1888`_).
+ Thanks `@jaraco`_ for the report and `@nicoddemus`_ for the PR (`#1891`_).
+
+* Do not call tearDown and cleanups when running tests from
+ ``unittest.TestCase`` subclasses with ``--pdb``
+ enabled. This allows proper post mortem debugging for all applications
+ which have significant logic in their tearDown machinery (`#1890`_). Thanks
+ `@mbyt`_ for the PR.
+
+* Fix use of deprecated ``getfuncargvalue`` method in the internal doctest plugin.
+ Thanks `@ViviCoder`_ for the report (`#1898`_).
+
+.. _@joguSD: https://github.com/joguSD
+.. _@AiOO: https://github.com/AiOO
+.. _@mbyt: https://github.com/mbyt
+.. _@ViviCoder: https://github.com/ViviCoder
+
+.. _#1857: https://github.com/pytest-dev/pytest/issues/1857
+.. _#1864: https://github.com/pytest-dev/pytest/issues/1864
+.. _#1888: https://github.com/pytest-dev/pytest/issues/1888
+.. _#1891: https://github.com/pytest-dev/pytest/pull/1891
+.. _#1890: https://github.com/pytest-dev/pytest/issues/1890
+.. _#1898: https://github.com/pytest-dev/pytest/issues/1898
+
+
+3.0.1 (2016-08-23)
+==================
+
+* Fix regression when ``importorskip`` is used at module level (`#1822`_).
+ Thanks `@jaraco`_ and `@The-Compiler`_ for the report and `@nicoddemus`_ for the PR.
+
+* Fix parametrization scope when session fixtures are used in conjunction
+ with normal parameters in the same call (`#1832`_).
+ Thanks `@The-Compiler`_ for the report, `@Kingdread`_ and `@nicoddemus`_ for the PR.
+
+* Fix internal error when parametrizing tests or fixtures using an empty ``ids`` argument (`#1849`_).
+ Thanks `@OPpuolitaival`_ for the report and `@nicoddemus`_ for the PR.
+
+* Fix loader error when running ``pytest`` embedded in a zipfile.
+ Thanks `@mbachry`_ for the PR.
+
+
+.. _@Kingdread: https://github.com/Kingdread
+.. _@mbachry: https://github.com/mbachry
+.. _@OPpuolitaival: https://github.com/OPpuolitaival
+
+.. _#1822: https://github.com/pytest-dev/pytest/issues/1822
+.. _#1832: https://github.com/pytest-dev/pytest/issues/1832
+.. _#1849: https://github.com/pytest-dev/pytest/issues/1849
+
+
+3.0.0 (2016-08-18)
+==================
+
+**Incompatible changes**
+
+
+A number of incompatible changes were made in this release, with the intent of removing features deprecated for a long
+time or change existing behaviors in order to make them less surprising/more useful.
+
+* Reinterpretation mode has now been removed. Only plain and rewrite
+ mode are available, consequently the ``--assert=reinterp`` option is
+ no longer available. This also means files imported from plugins or
+ ``conftest.py`` will not benefit from improved assertions by
+ default, you should use ``pytest.register_assert_rewrite()`` to
+ explicitly turn on assertion rewriting for those files. Thanks
+ `@flub`_ for the PR.
+
+* The following deprecated commandline options were removed:
+
+ * ``--genscript``: no longer supported;
+ * ``--no-assert``: use ``--assert=plain`` instead;
+ * ``--nomagic``: use ``--assert=plain`` instead;
+ * ``--report``: use ``-r`` instead;
+
+ Thanks to `@RedBeardCode`_ for the PR (`#1664`_).
+
+* ImportErrors in plugins now are a fatal error instead of issuing a
+ pytest warning (`#1479`_). Thanks to `@The-Compiler`_ for the PR.
+
+* Removed support code for Python 3 versions < 3.3 (`#1627`_).
+
+* Removed all ``py.test-X*`` entry points. The versioned, suffixed entry points
+ were never documented and a leftover from a pre-virtualenv era. These entry
+ points also created broken entry points in wheels, so removing them also
+ removes a source of confusion for users (`#1632`_).
+ Thanks `@obestwalter`_ for the PR.
+
+* ``pytest.skip()`` now raises an error when used to decorate a test function,
+ as opposed to its original intent (to imperatively skip a test inside a test function). Previously
+ this usage would cause the entire module to be skipped (`#607`_).
+ Thanks `@omarkohl`_ for the complete PR (`#1519`_).
+
+* Exit tests if a collection error occurs. A poll indicated most users will hit CTRL-C
+ anyway as soon as they see collection errors, so pytest might as well make that the default behavior (`#1421`_).
+ A ``--continue-on-collection-errors`` option has been added to restore the previous behaviour.
+ Thanks `@olegpidsadnyi`_ and `@omarkohl`_ for the complete PR (`#1628`_).
+
+* Renamed the pytest ``pdb`` module (plugin) into ``debugging`` to avoid clashes with the builtin ``pdb`` module.
+
+* Raise a helpful failure message when requesting a parametrized fixture at runtime,
+ e.g. with ``request.getfixturevalue``. Previously these parameters were simply
+ never defined, so a fixture decorated like ``@pytest.fixture(params=[0, 1, 2])``
+ only ran once (`#460`_).
+ Thanks to `@nikratio`_ for the bug report, `@RedBeardCode`_ and `@tomviner`_ for the PR.
+
+* ``_pytest.monkeypatch.monkeypatch`` class has been renamed to ``_pytest.monkeypatch.MonkeyPatch``
+ so it doesn't conflict with the ``monkeypatch`` fixture.
+
+* ``--exitfirst / -x`` can now be overridden by a following ``--maxfail=N``
+ and is just a synonym for ``--maxfail=1``.
+
+
+**New Features**
+
+* Support nose-style ``__test__`` attribute on methods of classes,
+ including unittest-style Classes. If set to ``False``, the test will not be
+ collected.
+
+* New ``doctest_namespace`` fixture for injecting names into the
+ namespace in which doctests run.
+ Thanks `@milliams`_ for the complete PR (`#1428`_).
+
+* New ``--doctest-report`` option available to change the output format of diffs
+ when running (failing) doctests (implements `#1749`_).
+ Thanks `@hartym`_ for the PR.
+
+* New ``name`` argument to ``pytest.fixture`` decorator which allows a custom name
+ for a fixture (to solve the funcarg-shadowing-fixture problem).
+ Thanks `@novas0x2a`_ for the complete PR (`#1444`_).
+
+* New ``approx()`` function for easily comparing floating-point numbers in
+ tests.
+ Thanks `@kalekundert`_ for the complete PR (`#1441`_).
+
+* Ability to add global properties in the final xunit output file by accessing
+ the internal ``junitxml`` plugin (experimental).
+ Thanks `@tareqalayan`_ for the complete PR `#1454`_).
+
+* New ``ExceptionInfo.match()`` method to match a regular expression on the
+ string representation of an exception (`#372`_).
+ Thanks `@omarkohl`_ for the complete PR (`#1502`_).
+
+* ``__tracebackhide__`` can now also be set to a callable which then can decide
+ whether to filter the traceback based on the ``ExceptionInfo`` object passed
+ to it. Thanks `@The-Compiler`_ for the complete PR (`#1526`_).
+
+* New ``pytest_make_parametrize_id(config, val)`` hook which can be used by plugins to provide
+ friendly strings for custom types.
+ Thanks `@palaviv`_ for the PR.
+
+* ``capsys`` and ``capfd`` now have a ``disabled()`` context-manager method, which
+ can be used to temporarily disable capture within a test.
+ Thanks `@nicoddemus`_ for the PR.
+
+* New cli flag ``--fixtures-per-test``: shows which fixtures are being used
+ for each selected test item. Features doc strings of fixtures by default.
+ Can also show where fixtures are defined if combined with ``-v``.
+ Thanks `@hackebrot`_ for the PR.
+
+* Introduce ``pytest`` command as recommended entry point. Note that ``py.test``
+ still works and is not scheduled for removal. Closes proposal
+ `#1629`_. Thanks `@obestwalter`_ and `@davehunt`_ for the complete PR
+ (`#1633`_).
+
+* New cli flags:
+
+ + ``--setup-plan``: performs normal collection and reports
+ the potential setup and teardown and does not execute any fixtures and tests;
+ + ``--setup-only``: performs normal collection, executes setup and teardown of
+ fixtures and reports them;
+ + ``--setup-show``: performs normal test execution and additionally shows
+ setup and teardown of fixtures;
+ + ``--keep-duplicates``: py.test now ignores duplicated paths given in the command
+ line. To retain the previous behavior where the same test could be run multiple
+ times by specifying it in the command-line multiple times, pass the ``--keep-duplicates``
+ argument (`#1609`_);
+
+ Thanks `@d6e`_, `@kvas-it`_, `@sallner`_, `@ioggstream`_ and `@omarkohl`_ for the PRs.
+
+* New CLI flag ``--override-ini``/``-o``: overrides values from the ini file.
+ For example: ``"-o xfail_strict=True"``'.
+ Thanks `@blueyed`_ and `@fengxx`_ for the PR.
+
+* New hooks:
+
+ + ``pytest_fixture_setup(fixturedef, request)``: executes fixture setup;
+ + ``pytest_fixture_post_finalizer(fixturedef)``: called after the fixture's
+ finalizer and has access to the fixture's result cache.
+
+ Thanks `@d6e`_, `@sallner`_.
+
+* Issue warnings for asserts whose test is a tuple literal. Such asserts will
+ never fail because tuples are always truthy and are usually a mistake
+ (see `#1562`_). Thanks `@kvas-it`_, for the PR.
+
+* Allow passing a custom debugger class (e.g. ``--pdbcls=IPython.core.debugger:Pdb``).
+ Thanks to `@anntzer`_ for the PR.
+
+
+**Changes**
+
+* Plugins now benefit from assertion rewriting. Thanks
+ `@sober7`_, `@nicoddemus`_ and `@flub`_ for the PR.
+
+* Change ``report.outcome`` for ``xpassed`` tests to ``"passed"`` in non-strict
+ mode and ``"failed"`` in strict mode. Thanks to `@hackebrot`_ for the PR
+ (`#1795`_) and `@gprasad84`_ for report (`#1546`_).
+
+* Tests marked with ``xfail(strict=False)`` (the default) now appear in
+ JUnitXML reports as passing tests instead of skipped.
+ Thanks to `@hackebrot`_ for the PR (`#1795`_).
+
+* Highlight path of the file location in the error report to make it easier to copy/paste.
+ Thanks `@suzaku`_ for the PR (`#1778`_).
+
+* Fixtures marked with ``@pytest.fixture`` can now use ``yield`` statements exactly like
+ those marked with the ``@pytest.yield_fixture`` decorator. This change renders
+ ``@pytest.yield_fixture`` deprecated and makes ``@pytest.fixture`` with ``yield`` statements
+ the preferred way to write teardown code (`#1461`_).
+ Thanks `@csaftoiu`_ for bringing this to attention and `@nicoddemus`_ for the PR.
+
+* Explicitly passed parametrize ids do not get escaped to ascii (`#1351`_).
+ Thanks `@ceridwen`_ for the PR.
+
+* Fixtures are now sorted in the error message displayed when an unknown
+ fixture is declared in a test function.
+ Thanks `@nicoddemus`_ for the PR.
+
+* ``pytest_terminal_summary`` hook now receives the ``exitstatus``
+ of the test session as argument. Thanks `@blueyed`_ for the PR (`#1809`_).
+
+* Parametrize ids can accept ``None`` as specific test id, in which case the
+ automatically generated id for that argument will be used.
+ Thanks `@palaviv`_ for the complete PR (`#1468`_).
+
+* The parameter to xunit-style setup/teardown methods (``setup_method``,
+ ``setup_module``, etc.) is now optional and may be omitted.
+ Thanks `@okken`_ for bringing this to attention and `@nicoddemus`_ for the PR.
+
+* Improved automatic id generation selection in case of duplicate ids in
+ parametrize.
+ Thanks `@palaviv`_ for the complete PR (`#1474`_).
+
+* Now pytest warnings summary is shown up by default. Added a new flag
+ ``--disable-pytest-warnings`` to explicitly disable the warnings summary (`#1668`_).
+
+* Make ImportError during collection more explicit by reminding
+ the user to check the name of the test module/package(s) (`#1426`_).
+ Thanks `@omarkohl`_ for the complete PR (`#1520`_).
+
+* Add ``build/`` and ``dist/`` to the default ``--norecursedirs`` list. Thanks
+ `@mikofski`_ for the report and `@tomviner`_ for the PR (`#1544`_).
+
+* ``pytest.raises`` in the context manager form accepts a custom
+ ``message`` to raise when no exception occurred.
+ Thanks `@palaviv`_ for the complete PR (`#1616`_).
+
+* ``conftest.py`` files now benefit from assertion rewriting; previously it
+ was only available for test modules. Thanks `@flub`_, `@sober7`_ and
+ `@nicoddemus`_ for the PR (`#1619`_).
+
+* Text documents without any doctests no longer appear as "skipped".
+ Thanks `@graingert`_ for reporting and providing a full PR (`#1580`_).
+
+* Ensure that a module within a namespace package can be found when it
+ is specified on the command line together with the ``--pyargs``
+ option. Thanks to `@taschini`_ for the PR (`#1597`_).
+
+* Always include full assertion explanation during assertion rewriting. The previous behaviour was hiding
+ sub-expressions that happened to be ``False``, assuming this was redundant information.
+ Thanks `@bagerard`_ for reporting (`#1503`_). Thanks to `@davehunt`_ and
+ `@tomviner`_ for the PR.
+
+* ``OptionGroup.addoption()`` now checks if option names were already
+ added before, to make it easier to track down issues like `#1618`_.
+ Before, you only got exceptions later from ``argparse`` library,
+ giving no clue about the actual reason for double-added options.
+
+* ``yield``-based tests are considered deprecated and will be removed in pytest-4.0.
+ Thanks `@nicoddemus`_ for the PR.
+
+* ``[pytest]`` sections in ``setup.cfg`` files should now be named ``[tool:pytest]``
+ to avoid conflicts with other distutils commands (see `#567`_). ``[pytest]`` sections in
+ ``pytest.ini`` or ``tox.ini`` files are supported and unchanged.
+ Thanks `@nicoddemus`_ for the PR.
+
+* Using ``pytest_funcarg__`` prefix to declare fixtures is considered deprecated and will be
+ removed in pytest-4.0 (`#1684`_).
+ Thanks `@nicoddemus`_ for the PR.
+
+* Passing a command-line string to ``pytest.main()`` is considered deprecated and scheduled
+ for removal in pytest-4.0. It is recommended to pass a list of arguments instead (`#1723`_).
+
+* Rename ``getfuncargvalue`` to ``getfixturevalue``. ``getfuncargvalue`` is
+ still present but is now considered deprecated. Thanks to `@RedBeardCode`_ and `@tomviner`_
+ for the PR (`#1626`_).
+
+* ``optparse`` type usage now triggers DeprecationWarnings (`#1740`_).
+
+
+* ``optparse`` backward compatibility supports float/complex types (`#457`_).
+
+* Refined logic for determining the ``rootdir``, considering only valid
+ paths which fixes a number of issues: `#1594`_, `#1435`_ and `#1471`_.
+ Updated the documentation according to current behavior. Thanks to
+ `@blueyed`_, `@davehunt`_ and `@matthiasha`_ for the PR.
+
+* Always include full assertion explanation. The previous behaviour was hiding
+ sub-expressions that happened to be False, assuming this was redundant information.
+ Thanks `@bagerard`_ for reporting (`#1503`_). Thanks to `@davehunt`_ and
+ `@tomviner`_ for PR.
+
+* Better message in case of not using parametrized variable (see `#1539`_).
+ Thanks to `@tramwaj29`_ for the PR.
+
+* Updated docstrings with a more uniform style.
+
+* Add stderr write for ``pytest.exit(msg)`` during startup. Previously the message was never shown.
+ Thanks `@BeyondEvil`_ for reporting `#1210`_. Thanks to `@JonathonSonesen`_ and
+ `@tomviner`_ for the PR.
+
+* No longer display the incorrect test deselection reason (`#1372`_).
+ Thanks `@ronnypfannschmidt`_ for the PR.
+
+* The ``--resultlog`` command line option has been deprecated: it is little used
+ and there are more modern and better alternatives (see `#830`_).
+ Thanks `@nicoddemus`_ for the PR.
+
+* Improve error message with fixture lookup errors: add an 'E' to the first
+ line and '>' to the rest. Fixes `#717`_. Thanks `@blueyed`_ for reporting and
+ a PR, `@eolo999`_ for the initial PR and `@tomviner`_ for his guidance during
+ EuroPython2016 sprint.
+
+
+**Bug Fixes**
+
+* Parametrize now correctly handles duplicated test ids.
+
+* Fix internal error issue when the ``method`` argument is missing for
+ ``teardown_method()`` (`#1605`_).
+
+* Fix exception visualization in case the current working directory (CWD) gets
+ deleted during testing (`#1235`_). Thanks `@bukzor`_ for reporting. PR by
+ `@marscher`_.
+
+* Improve test output for logical expression with brackets (`#925`_).
+ Thanks `@DRMacIver`_ for reporting and `@RedBeardCode`_ for the PR.
+
+* Create correct diff for strings ending with newlines (`#1553`_).
+ Thanks `@Vogtinator`_ for reporting and `@RedBeardCode`_ and
+ `@tomviner`_ for the PR.
+
+* ``ConftestImportFailure`` now shows the traceback making it easier to
+ identify bugs in ``conftest.py`` files (`#1516`_). Thanks `@txomon`_ for
+ the PR.
+
+* Text documents without any doctests no longer appear as "skipped".
+ Thanks `@graingert`_ for reporting and providing a full PR (`#1580`_).
+
+* Fixed collection of classes with custom ``__new__`` method.
+ Fixes `#1579`_. Thanks to `@Stranger6667`_ for the PR.
+
+* Fixed scope overriding inside metafunc.parametrize (`#634`_).
+ Thanks to `@Stranger6667`_ for the PR.
+
+* Fixed the total tests tally in junit xml output (`#1798`_).
+ Thanks to `@cryporchild`_ for the PR.
+
+* Fixed off-by-one error with lines from ``request.node.warn``.
+ Thanks to `@blueyed`_ for the PR.
+
+
+.. _#1210: https://github.com/pytest-dev/pytest/issues/1210
+.. _#1235: https://github.com/pytest-dev/pytest/issues/1235
+.. _#1351: https://github.com/pytest-dev/pytest/issues/1351
+.. _#1372: https://github.com/pytest-dev/pytest/issues/1372
+.. _#1421: https://github.com/pytest-dev/pytest/issues/1421
+.. _#1426: https://github.com/pytest-dev/pytest/issues/1426
+.. _#1428: https://github.com/pytest-dev/pytest/pull/1428
+.. _#1435: https://github.com/pytest-dev/pytest/issues/1435
+.. _#1441: https://github.com/pytest-dev/pytest/pull/1441
+.. _#1444: https://github.com/pytest-dev/pytest/pull/1444
+.. _#1454: https://github.com/pytest-dev/pytest/pull/1454
+.. _#1461: https://github.com/pytest-dev/pytest/pull/1461
+.. _#1468: https://github.com/pytest-dev/pytest/pull/1468
+.. _#1471: https://github.com/pytest-dev/pytest/issues/1471
+.. _#1474: https://github.com/pytest-dev/pytest/pull/1474
+.. _#1479: https://github.com/pytest-dev/pytest/issues/1479
+.. _#1502: https://github.com/pytest-dev/pytest/pull/1502
+.. _#1503: https://github.com/pytest-dev/pytest/issues/1503
+.. _#1516: https://github.com/pytest-dev/pytest/pull/1516
+.. _#1519: https://github.com/pytest-dev/pytest/pull/1519
+.. _#1520: https://github.com/pytest-dev/pytest/pull/1520
+.. _#1526: https://github.com/pytest-dev/pytest/pull/1526
+.. _#1539: https://github.com/pytest-dev/pytest/issues/1539
+.. _#1544: https://github.com/pytest-dev/pytest/issues/1544
+.. _#1546: https://github.com/pytest-dev/pytest/issues/1546
+.. _#1553: https://github.com/pytest-dev/pytest/issues/1553
+.. _#1562: https://github.com/pytest-dev/pytest/issues/1562
+.. _#1579: https://github.com/pytest-dev/pytest/issues/1579
+.. _#1580: https://github.com/pytest-dev/pytest/pull/1580
+.. _#1594: https://github.com/pytest-dev/pytest/issues/1594
+.. _#1597: https://github.com/pytest-dev/pytest/pull/1597
+.. _#1605: https://github.com/pytest-dev/pytest/issues/1605
+.. _#1616: https://github.com/pytest-dev/pytest/pull/1616
+.. _#1618: https://github.com/pytest-dev/pytest/issues/1618
+.. _#1619: https://github.com/pytest-dev/pytest/issues/1619
+.. _#1626: https://github.com/pytest-dev/pytest/pull/1626
+.. _#1627: https://github.com/pytest-dev/pytest/pull/1627
+.. _#1628: https://github.com/pytest-dev/pytest/pull/1628
+.. _#1629: https://github.com/pytest-dev/pytest/issues/1629
+.. _#1632: https://github.com/pytest-dev/pytest/issues/1632
+.. _#1633: https://github.com/pytest-dev/pytest/pull/1633
+.. _#1664: https://github.com/pytest-dev/pytest/pull/1664
+.. _#1668: https://github.com/pytest-dev/pytest/issues/1668
+.. _#1684: https://github.com/pytest-dev/pytest/pull/1684
+.. _#1723: https://github.com/pytest-dev/pytest/pull/1723
+.. _#1740: https://github.com/pytest-dev/pytest/issues/1740
+.. _#1749: https://github.com/pytest-dev/pytest/issues/1749
+.. _#1778: https://github.com/pytest-dev/pytest/pull/1778
+.. _#1795: https://github.com/pytest-dev/pytest/pull/1795
+.. _#1798: https://github.com/pytest-dev/pytest/pull/1798
+.. _#1809: https://github.com/pytest-dev/pytest/pull/1809
+.. _#372: https://github.com/pytest-dev/pytest/issues/372
+.. _#457: https://github.com/pytest-dev/pytest/issues/457
+.. _#460: https://github.com/pytest-dev/pytest/pull/460
+.. _#567: https://github.com/pytest-dev/pytest/pull/567
+.. _#607: https://github.com/pytest-dev/pytest/issues/607
+.. _#634: https://github.com/pytest-dev/pytest/issues/634
+.. _#717: https://github.com/pytest-dev/pytest/issues/717
+.. _#830: https://github.com/pytest-dev/pytest/issues/830
+.. _#925: https://github.com/pytest-dev/pytest/issues/925
+
+
+.. _@anntzer: https://github.com/anntzer
+.. _@bagerard: https://github.com/bagerard
+.. _@BeyondEvil: https://github.com/BeyondEvil
+.. _@blueyed: https://github.com/blueyed
+.. _@ceridwen: https://github.com/ceridwen
+.. _@cryporchild: https://github.com/cryporchild
+.. _@csaftoiu: https://github.com/csaftoiu
+.. _@d6e: https://github.com/d6e
+.. _@davehunt: https://github.com/davehunt
+.. _@DRMacIver: https://github.com/DRMacIver
+.. _@eolo999: https://github.com/eolo999
+.. _@fengxx: https://github.com/fengxx
+.. _@flub: https://github.com/flub
+.. _@gprasad84: https://github.com/gprasad84
+.. _@graingert: https://github.com/graingert
+.. _@hartym: https://github.com/hartym
+.. _@JonathonSonesen: https://github.com/JonathonSonesen
+.. _@kalekundert: https://github.com/kalekundert
+.. _@kvas-it: https://github.com/kvas-it
+.. _@marscher: https://github.com/marscher
+.. _@mikofski: https://github.com/mikofski
+.. _@milliams: https://github.com/milliams
+.. _@nikratio: https://github.com/nikratio
+.. _@novas0x2a: https://github.com/novas0x2a
+.. _@obestwalter: https://github.com/obestwalter
+.. _@okken: https://github.com/okken
+.. _@olegpidsadnyi: https://github.com/olegpidsadnyi
+.. _@omarkohl: https://github.com/omarkohl
+.. _@palaviv: https://github.com/palaviv
+.. _@RedBeardCode: https://github.com/RedBeardCode
+.. _@sallner: https://github.com/sallner
+.. _@sober7: https://github.com/sober7
+.. _@Stranger6667: https://github.com/Stranger6667
+.. _@suzaku: https://github.com/suzaku
+.. _@tareqalayan: https://github.com/tareqalayan
+.. _@taschini: https://github.com/taschini
+.. _@tramwaj29: https://github.com/tramwaj29
+.. _@txomon: https://github.com/txomon
+.. _@Vogtinator: https://github.com/Vogtinator
+.. _@matthiasha: https://github.com/matthiasha
+
+
+2.9.2 (2016-05-31)
+==================
+
+**Bug Fixes**
+
+* fix `#510`_: skip tests where one parameterize dimension was empty
+ thanks Alex Stapleton for the Report and `@RonnyPfannschmidt`_ for the PR
+
+* Fix Xfail does not work with condition keyword argument.
+ Thanks `@astraw38`_ for reporting the issue (`#1496`_) and `@tomviner`_
+ for PR the (`#1524`_).
+
+* Fix win32 path issue when putting custom config file with absolute path
+ in ``pytest.main("-c your_absolute_path")``.
+
+* Fix maximum recursion depth detection when raised error class is not aware
+ of unicode/encoded bytes.
+ Thanks `@prusse-martin`_ for the PR (`#1506`_).
+
+* Fix ``pytest.mark.skip`` mark when used in strict mode.
+ Thanks `@pquentin`_ for the PR and `@RonnyPfannschmidt`_ for
+ showing how to fix the bug.
+
+* Minor improvements and fixes to the documentation.
+ Thanks `@omarkohl`_ for the PR.
+
+* Fix ``--fixtures`` to show all fixture definitions as opposed to just
+ one per fixture name.
+ Thanks to `@hackebrot`_ for the PR.
+
+.. _#510: https://github.com/pytest-dev/pytest/issues/510
+.. _#1506: https://github.com/pytest-dev/pytest/pull/1506
+.. _#1496: https://github.com/pytest-dev/pytest/issues/1496
+.. _#1524: https://github.com/pytest-dev/pytest/pull/1524
+
+.. _@prusse-martin: https://github.com/prusse-martin
+.. _@astraw38: https://github.com/astraw38
+
+
+2.9.1 (2016-03-17)
+==================
+
+**Bug Fixes**
+
+* Improve error message when a plugin fails to load.
+ Thanks `@nicoddemus`_ for the PR.
+
+* Fix (`#1178 <https://github.com/pytest-dev/pytest/issues/1178>`_):
+ ``pytest.fail`` with non-ascii characters raises an internal pytest error.
+ Thanks `@nicoddemus`_ for the PR.
+
+* Fix (`#469`_): junit parses report.nodeid incorrectly, when params IDs
+ contain ``::``. Thanks `@tomviner`_ for the PR (`#1431`_).
+
+* Fix (`#578 <https://github.com/pytest-dev/pytest/issues/578>`_): SyntaxErrors
+ containing non-ascii lines at the point of failure generated an internal
+ py.test error.
+ Thanks `@asottile`_ for the report and `@nicoddemus`_ for the PR.
+
+* Fix (`#1437`_): When passing in a bytestring regex pattern to parameterize
+ attempt to decode it as utf-8 ignoring errors.
+
+* Fix (`#649`_): parametrized test nodes cannot be specified to run on the command line.
+
+* Fix (`#138`_): better reporting for python 3.3+ chained exceptions
+
+.. _#1437: https://github.com/pytest-dev/pytest/issues/1437
+.. _#469: https://github.com/pytest-dev/pytest/issues/469
+.. _#1431: https://github.com/pytest-dev/pytest/pull/1431
+.. _#649: https://github.com/pytest-dev/pytest/issues/649
+.. _#138: https://github.com/pytest-dev/pytest/issues/138
+
+.. _@asottile: https://github.com/asottile
+
+
+2.9.0 (2016-02-29)
+==================
+
+**New Features**
+
+* New ``pytest.mark.skip`` mark, which unconditionally skips marked tests.
+ Thanks `@MichaelAquilina`_ for the complete PR (`#1040`_).
+
+* ``--doctest-glob`` may now be passed multiple times in the command-line.
+ Thanks `@jab`_ and `@nicoddemus`_ for the PR.
+
+* New ``-rp`` and ``-rP`` reporting options give the summary and full output
+ of passing tests, respectively. Thanks to `@codewarrior0`_ for the PR.
+
+* ``pytest.mark.xfail`` now has a ``strict`` option, which makes ``XPASS``
+ tests to fail the test suite (defaulting to ``False``). There's also a
+ ``xfail_strict`` ini option that can be used to configure it project-wise.
+ Thanks `@rabbbit`_ for the request and `@nicoddemus`_ for the PR (`#1355`_).
+
+* ``Parser.addini`` now supports options of type ``bool``.
+ Thanks `@nicoddemus`_ for the PR.
+
+* New ``ALLOW_BYTES`` doctest option. This strips ``b`` prefixes from byte strings
+ in doctest output (similar to ``ALLOW_UNICODE``).
+ Thanks `@jaraco`_ for the request and `@nicoddemus`_ for the PR (`#1287`_).
+
+* Give a hint on ``KeyboardInterrupt`` to use the ``--fulltrace`` option to show the errors.
+ Fixes `#1366`_.
+ Thanks to `@hpk42`_ for the report and `@RonnyPfannschmidt`_ for the PR.
+
+* Catch ``IndexError`` exceptions when getting exception source location.
+ Fixes a pytest internal error for dynamically generated code (fixtures and tests)
+ where source lines are fake by intention.
+
+**Changes**
+
+* **Important**: `py.code <https://pylib.readthedocs.io/en/latest/code.html>`_ has been
+ merged into the ``pytest`` repository as ``pytest._code``. This decision
+ was made because ``py.code`` had very few uses outside ``pytest`` and the
+ fact that it was in a different repository made it difficult to fix bugs on
+ its code in a timely manner. The team hopes with this to be able to better
+ refactor out and improve that code.
+ This change shouldn't affect users, but it is useful to let users aware
+ if they encounter any strange behavior.
+
+ Keep in mind that the code for ``pytest._code`` is **private** and
+ **experimental**, so you definitely should not import it explicitly!
+
+ Please note that the original ``py.code`` is still available in
+ `pylib <https://pylib.readthedocs.io>`_.
+
+* ``pytest_enter_pdb`` now optionally receives the pytest config object.
+ Thanks `@nicoddemus`_ for the PR.
+
+* Removed code and documentation for Python 2.5 or lower versions,
+ including removal of the obsolete ``_pytest.assertion.oldinterpret`` module.
+ Thanks `@nicoddemus`_ for the PR (`#1226`_).
+
+* Comparisons now always show up in full when ``CI`` or ``BUILD_NUMBER`` is
+ found in the environment, even when ``-vv`` isn't used.
+ Thanks `@The-Compiler`_ for the PR.
+
+* ``--lf`` and ``--ff`` now support long names: ``--last-failed`` and
+ ``--failed-first`` respectively.
+ Thanks `@MichaelAquilina`_ for the PR.
+
+* Added expected exceptions to ``pytest.raises`` fail message.
+
+* Collection only displays progress ("collecting X items") when in a terminal.
+ This avoids cluttering the output when using ``--color=yes`` to obtain
+ colors in CI integrations systems (`#1397`_).
+
+**Bug Fixes**
+
+* The ``-s`` and ``-c`` options should now work under ``xdist``;
+ ``Config.fromdictargs`` now represents its input much more faithfully.
+ Thanks to `@bukzor`_ for the complete PR (`#680`_).
+
+* Fix (`#1290`_): support Python 3.5's ``@`` operator in assertion rewriting.
+ Thanks `@Shinkenjoe`_ for report with test case and `@tomviner`_ for the PR.
+
+* Fix formatting utf-8 explanation messages (`#1379`_).
+ Thanks `@biern`_ for the PR.
+
+* Fix `traceback style docs`_ to describe all of the available options
+ (auto/long/short/line/native/no), with ``auto`` being the default since v2.6.
+ Thanks `@hackebrot`_ for the PR.
+
+* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records
+ with same name.
+
+.. _`traceback style docs`: https://pytest.org/latest/usage.html#modifying-python-traceback-printing
+
+.. _#1609: https://github.com/pytest-dev/pytest/issues/1609
+.. _#1422: https://github.com/pytest-dev/pytest/issues/1422
+.. _#1379: https://github.com/pytest-dev/pytest/issues/1379
+.. _#1366: https://github.com/pytest-dev/pytest/issues/1366
+.. _#1040: https://github.com/pytest-dev/pytest/pull/1040
+.. _#680: https://github.com/pytest-dev/pytest/issues/680
+.. _#1287: https://github.com/pytest-dev/pytest/pull/1287
+.. _#1226: https://github.com/pytest-dev/pytest/pull/1226
+.. _#1290: https://github.com/pytest-dev/pytest/pull/1290
+.. _#1355: https://github.com/pytest-dev/pytest/pull/1355
+.. _#1397: https://github.com/pytest-dev/pytest/issues/1397
+.. _@biern: https://github.com/biern
+.. _@MichaelAquilina: https://github.com/MichaelAquilina
+.. _@bukzor: https://github.com/bukzor
+.. _@hpk42: https://github.com/hpk42
+.. _@nicoddemus: https://github.com/nicoddemus
+.. _@jab: https://github.com/jab
+.. _@codewarrior0: https://github.com/codewarrior0
+.. _@jaraco: https://github.com/jaraco
+.. _@The-Compiler: https://github.com/The-Compiler
+.. _@Shinkenjoe: https://github.com/Shinkenjoe
+.. _@tomviner: https://github.com/tomviner
+.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
+.. _@rabbbit: https://github.com/rabbbit
+.. _@hackebrot: https://github.com/hackebrot
+.. _@pquentin: https://github.com/pquentin
+.. _@ioggstream: https://github.com/ioggstream
+
+2.8.7 (2016-01-24)
+==================
+
+- fix #1338: use predictable object resolution for monkeypatch
+
+2.8.6 (2016-01-21)
+==================
+
+- fix #1259: allow for double nodeids in junitxml,
+ this was a regression failing plugins combinations
+ like pytest-pep8 + pytest-flakes
+
+- Workaround for exception that occurs in pyreadline when using
+ ``--pdb`` with standard I/O capture enabled.
+ Thanks Erik M. Bray for the PR.
+
+- fix #900: Better error message in case the target of a ``monkeypatch`` call
+ raises an ``ImportError``.
+
+- fix #1292: monkeypatch calls (setattr, setenv, etc.) are now O(1).
+ Thanks David R. MacIver for the report and Bruno Oliveira for the PR.
+
+- fix #1223: captured stdout and stderr are now properly displayed before
+ entering pdb when ``--pdb`` is used instead of being thrown away.
+ Thanks Cal Leeming for the PR.
+
+- fix #1305: pytest warnings emitted during ``pytest_terminal_summary`` are now
+ properly displayed.
+ Thanks Ionel Maries Cristian for the report and Bruno Oliveira for the PR.
+
+- fix #628: fixed internal UnicodeDecodeError when doctests contain unicode.
+ Thanks Jason R. Coombs for the report and Bruno Oliveira for the PR.
+
+- fix #1334: Add captured stdout to jUnit XML report on setup error.
+ Thanks Georgy Dyuldin for the PR.
+
+
+2.8.5 (2015-12-11)
+==================
+
+- fix #1243: fixed issue where class attributes injected during collection could break pytest.
+ PR by Alexei Kozlenok, thanks Ronny Pfannschmidt and Bruno Oliveira for the review and help.
+
+- fix #1074: precompute junitxml chunks instead of storing the whole tree in objects
+ Thanks Bruno Oliveira for the report and Ronny Pfannschmidt for the PR
+
+- fix #1238: fix ``pytest.deprecated_call()`` receiving multiple arguments
+ (Regression introduced in 2.8.4). Thanks Alex Gaynor for the report and
+ Bruno Oliveira for the PR.
+
+
+2.8.4 (2015-12-06)
+==================
+
+- fix #1190: ``deprecated_call()`` now works when the deprecated
+ function has been already called by another test in the same
+ module. Thanks Mikhail Chernykh for the report and Bruno Oliveira for the
+ PR.
+
+- fix #1198: ``--pastebin`` option now works on Python 3. Thanks
+ Mehdy Khoshnoody for the PR.
+
+- fix #1219: ``--pastebin`` now works correctly when captured output contains
+ non-ascii characters. Thanks Bruno Oliveira for the PR.
+
+- fix #1204: another error when collecting with a nasty __getattr__().
+ Thanks Florian Bruhin for the PR.
+
+- fix the summary printed when no tests did run.
+ Thanks Florian Bruhin for the PR.
+- fix #1185 - ensure MANIFEST.in exactly matches what should go to a sdist
+
+- a number of documentation modernizations wrt good practices.
+ Thanks Bruno Oliveira for the PR.
+
+2.8.3 (2015-11-18)
+==================
+
+- fix #1169: add __name__ attribute to testcases in TestCaseFunction to
+ support the @unittest.skip decorator on functions and methods.
+ Thanks Lee Kamentsky for the PR.
+
+- fix #1035: collecting tests if test module level obj has __getattr__().
+ Thanks Suor for the report and Bruno Oliveira / Tom Viner for the PR.
+
+- fix #331: don't collect tests if their failure cannot be reported correctly
+ e.g. they are a callable instance of a class.
+
+- fix #1133: fixed internal error when filtering tracebacks where one entry
+ belongs to a file which is no longer available.
+ Thanks Bruno Oliveira for the PR.
+
+- enhancement made to highlight in red the name of the failing tests so
+ they stand out in the output.
+ Thanks Gabriel Reis for the PR.
+
+- add more talks to the documentation
+- extend documentation on the --ignore cli option
+- use pytest-runner for setuptools integration
+- minor fixes for interaction with OS X El Capitan
+ system integrity protection (thanks Florian)
+
+
+2.8.2 (2015-10-07)
+==================
+
+- fix #1085: proper handling of encoding errors when passing encoded byte
+ strings to pytest.parametrize in Python 2.
+ Thanks Themanwithoutaplan for the report and Bruno Oliveira for the PR.
+
+- fix #1087: handling SystemError when passing empty byte strings to
+ pytest.parametrize in Python 3.
+ Thanks Paul Kehrer for the report and Bruno Oliveira for the PR.
+
+- fix #995: fixed internal error when filtering tracebacks where one entry
+ was generated by an exec() statement.
+ Thanks Daniel Hahler, Ashley C Straw, Philippe Gauthier and Pavel Savchenko
+ for contributing and Bruno Oliveira for the PR.
+
+- fix #1100 and #1057: errors when using autouse fixtures and doctest modules.
+ Thanks Sergey B Kirpichev and Vital Kudzelka for contributing and Bruno
+ Oliveira for the PR.
+
+2.8.1 (2015-09-29)
+==================
+
+- fix #1034: Add missing nodeid on pytest_logwarning call in
+ addhook. Thanks Simon Gomizelj for the PR.
+
+- 'deprecated_call' is now only satisfied with a DeprecationWarning or
+ PendingDeprecationWarning. Before 2.8.0, it accepted any warning, and 2.8.0
+ made it accept only DeprecationWarning (but not PendingDeprecationWarning).
+ Thanks Alex Gaynor for the issue and Eric Hunsberger for the PR.
+
+- fix issue #1073: avoid calling __getattr__ on potential plugin objects.
+ This fixes an incompatibility with pytest-django. Thanks Andreas Pelme,
+ Bruno Oliveira and Ronny Pfannschmidt for contributing and Holger Krekel
+ for the fix.
+
+- Fix issue #704: handle versionconflict during plugin loading more
+ gracefully. Thanks Bruno Oliveira for the PR.
+
+- Fix issue #1064: ""--junitxml" regression when used with the
+ "pytest-xdist" plugin, with test reports being assigned to the wrong tests.
+ Thanks Daniel Grunwald for the report and Bruno Oliveira for the PR.
+
+- (experimental) adapt more SEMVER style versioning and change meaning of
+ master branch in git repo: "master" branch now keeps the bugfixes, changes
+ aimed for micro releases. "features" branch will only be released
+ with minor or major pytest releases.
+
+- Fix issue #766 by removing documentation references to distutils.
+ Thanks Russel Winder.
+
+- Fix issue #1030: now byte-strings are escaped to produce item node ids
+ to make them always serializable.
+ Thanks Andy Freeland for the report and Bruno Oliveira for the PR.
+
+- Python 2: if unicode parametrized values are convertible to ascii, their
+ ascii representation is used for the node id.
+
+- Fix issue #411: Add __eq__ method to assertion comparison example.
+ Thanks Ben Webb.
+- Fix issue #653: deprecated_call can be used as context manager.
+
+- fix issue 877: properly handle assertion explanations with non-ascii repr
+ Thanks Mathieu Agopian for the report and Ronny Pfannschmidt for the PR.
+
+- fix issue 1029: transform errors when writing cache values into pytest-warnings
+
+2.8.0 (2015-09-18)
+==================
+
+- new ``--lf`` and ``-ff`` options to run only the last failing tests or
+ "failing tests first" from the last run. This functionality is provided
+ through porting the formerly external pytest-cache plugin into pytest core.
+ BACKWARD INCOMPAT: if you used pytest-cache's functionality to persist
+ data between test runs be aware that we don't serialize sets anymore.
+ Thanks Ronny Pfannschmidt for most of the merging work.
+
+- "-r" option now accepts "a" to include all possible reports, similar
+ to passing "fEsxXw" explicitly (isse960).
+ Thanks Abhijeet Kasurde for the PR.
+
+- avoid python3.5 deprecation warnings by introducing version
+ specific inspection helpers, thanks Michael Droettboom.
+
+- fix issue562: @nose.tools.istest now fully respected.
+
+- fix issue934: when string comparison fails and a diff is too large to display
+ without passing -vv, still show a few lines of the diff.
+ Thanks Florian Bruhin for the report and Bruno Oliveira for the PR.
+
+- fix issue736: Fix a bug where fixture params would be discarded when combined
+ with parametrization markers.
+ Thanks to Markus Unterwaditzer for the PR.
+
+- fix issue710: introduce ALLOW_UNICODE doctest option: when enabled, the
+ ``u`` prefix is stripped from unicode strings in expected doctest output. This
+ allows doctests which use unicode to run in Python 2 and 3 unchanged.
+ Thanks Jason R. Coombs for the report and Bruno Oliveira for the PR.
+
+- parametrize now also generates meaningful test IDs for enum, regex and class
+ objects (as opposed to class instances).
+ Thanks to Florian Bruhin for the PR.
+
+- Add 'warns' to assert that warnings are thrown (like 'raises').
+ Thanks to Eric Hunsberger for the PR.
+
+- Fix issue683: Do not apply an already applied mark. Thanks ojake for the PR.
+
+- Deal with capturing failures better so fewer exceptions get lost to
+ /dev/null. Thanks David Szotten for the PR.
+
+- fix issue730: deprecate and warn about the --genscript option.
+ Thanks Ronny Pfannschmidt for the report and Christian Pommranz for the PR.
+
+- fix issue751: multiple parametrize with ids bug if it parametrizes class with
+ two or more test methods. Thanks Sergey Chipiga for reporting and Jan
+ Bednarik for PR.
+
+- fix issue82: avoid loading conftest files from setup.cfg/pytest.ini/tox.ini
+ files and upwards by default (--confcutdir can still be set to override this).
+ Thanks Bruno Oliveira for the PR.
+
+- fix issue768: docstrings found in python modules were not setting up session
+ fixtures. Thanks Jason R. Coombs for reporting and Bruno Oliveira for the PR.
+
+- added ``tmpdir_factory``, a session-scoped fixture that can be used to create
+ directories under the base temporary directory. Previously this object was
+ installed as a ``_tmpdirhandler`` attribute of the ``config`` object, but now it
+ is part of the official API and using ``config._tmpdirhandler`` is
+ deprecated.
+ Thanks Bruno Oliveira for the PR.
+
+- fix issue808: pytest's internal assertion rewrite hook now implements the
+ optional PEP302 get_data API so tests can access data files next to them.
+ Thanks xmo-odoo for request and example and Bruno Oliveira for
+ the PR.
+
+- rootdir and inifile are now displayed during usage errors to help
+ users diagnose problems such as unexpected ini files which add
+ unknown options being picked up by pytest. Thanks to Pavel Savchenko for
+ bringing the problem to attention in #821 and Bruno Oliveira for the PR.
+
+- Summary bar now is colored yellow for warning
+ situations such as: all tests either were skipped or xpass/xfailed,
+ or no tests were run at all (this is a partial fix for issue500).
+
+- fix issue812: pytest now exits with status code 5 in situations where no
+ tests were run at all, such as the directory given in the command line does
+ not contain any tests or as result of a command line option filters
+ all out all tests (-k for example).
+ Thanks Eric Siegerman (issue812) and Bruno Oliveira for the PR.
+
+- Summary bar now is colored yellow for warning
+ situations such as: all tests either were skipped or xpass/xfailed,
+ or no tests were run at all (related to issue500).
+ Thanks Eric Siegerman.
+
+- New ``testpaths`` ini option: list of directories to search for tests
+ when executing pytest from the root directory. This can be used
+ to speed up test collection when a project has well specified directories
+ for tests, being usually more practical than configuring norecursedirs for
+ all directories that do not contain tests.
+ Thanks to Adrian for idea (#694) and Bruno Oliveira for the PR.
+
+- fix issue713: JUnit XML reports for doctest failures.
+ Thanks Punyashloka Biswal.
+
+- fix issue970: internal pytest warnings now appear as "pytest-warnings" in
+ the terminal instead of "warnings", so it is clear for users that those
+ warnings are from pytest and not from the builtin "warnings" module.
+ Thanks Bruno Oliveira.
+
+- Include setup and teardown in junitxml test durations.
+ Thanks Janne Vanhala.
+
+- fix issue735: assertion failures on debug versions of Python 3.4+
+
+- new option ``--import-mode`` to allow to change test module importing
+ behaviour to append to sys.path instead of prepending. This better allows
+ to run test modules against installed versions of a package even if the
+ package under test has the same import root. In this example::
+
+ testing/__init__.py
+ testing/test_pkg_under_test.py
+ pkg_under_test/
+
+ the tests will run against the installed version
+ of pkg_under_test when ``--import-mode=append`` is used whereas
+ by default they would always pick up the local version. Thanks Holger Krekel.
+
+- pytester: add method ``TmpTestdir.delete_loaded_modules()``, and call it
+ from ``inline_run()`` to allow temporary modules to be reloaded.
+ Thanks Eduardo Schettino.
+
+- internally refactor pluginmanager API and code so that there
+ is a clear distinction between a pytest-agnostic rather simple
+ pluginmanager and the PytestPluginManager which adds a lot of
+ behaviour, among it handling of the local conftest files.
+ In terms of documented methods this is a backward compatible
+ change but it might still break 3rd party plugins which relied on
+ details like especially the pluginmanager.add_shutdown() API.
+ Thanks Holger Krekel.
+
+- pluginmanagement: introduce ``pytest.hookimpl`` and
+ ``pytest.hookspec`` decorators for setting impl/spec
+ specific parameters. This substitutes the previous
+ now deprecated use of ``pytest.mark`` which is meant to
+ contain markers for test functions only.
+
+- write/refine docs for "writing plugins" which now have their
+ own page and are separate from the "using/installing plugins`` page.
+
+- fix issue732: properly unregister plugins from any hook calling
+ sites allowing to have temporary plugins during test execution.
+
+- deprecate and warn about ``__multicall__`` argument in hook
+ implementations. Use the ``hookwrapper`` mechanism instead already
+ introduced with pytest-2.7.
+
+- speed up pytest's own test suite considerably by using inprocess
+ tests by default (testrun can be modified with --runpytest=subprocess
+ to create subprocesses in many places instead). The main
+ APIs to run pytest in a test is "runpytest()" or "runpytest_subprocess"
+ and "runpytest_inprocess" if you need a particular way of running
+ the test. In all cases you get back a RunResult but the inprocess
+ one will also have a "reprec" attribute with the recorded events/reports.
+
+- fix monkeypatch.setattr("x.y", raising=False) to actually not raise
+ if "y" is not a pre-existing attribute. Thanks Florian Bruhin.
+
+- fix issue741: make running output from testdir.run copy/pasteable
+ Thanks Bruno Oliveira.
+
+- add a new ``--noconftest`` argument which ignores all ``conftest.py`` files.
+
+- add ``file`` and ``line`` attributes to JUnit-XML output.
+
+- fix issue890: changed extension of all documentation files from ``txt`` to
+ ``rst``. Thanks to Abhijeet for the PR.
+
+- fix issue714: add ability to apply indirect=True parameter on particular argnames.
+ Thanks Elizaveta239.
+
+- fix issue890: changed extension of all documentation files from ``txt`` to
+ ``rst``. Thanks to Abhijeet for the PR.
+
+- fix issue957: "# doctest: SKIP" option will now register doctests as SKIPPED
+ rather than PASSED.
+ Thanks Thomas Grainger for the report and Bruno Oliveira for the PR.
+
+- issue951: add new record_xml_property fixture, that supports logging
+ additional information on xml output. Thanks David Diaz for the PR.
+
+- issue949: paths after normal options (for example ``-s``, ``-v``, etc) are now
+ properly used to discover ``rootdir`` and ``ini`` files.
+ Thanks Peter Lauri for the report and Bruno Oliveira for the PR.
+
+2.7.3 (2015-09-15)
+==================
+
+- Allow 'dev', 'rc', or other non-integer version strings in ``importorskip``.
+ Thanks to Eric Hunsberger for the PR.
+
+- fix issue856: consider --color parameter in all outputs (for example
+ --fixtures). Thanks Barney Gale for the report and Bruno Oliveira for the PR.
+
+- fix issue855: passing str objects as ``plugins`` argument to pytest.main
+ is now interpreted as a module name to be imported and registered as a
+ plugin, instead of silently having no effect.
+ Thanks xmo-odoo for the report and Bruno Oliveira for the PR.
+
+- fix issue744: fix for ast.Call changes in Python 3.5+. Thanks
+ Guido van Rossum, Matthias Bussonnier, Stefan Zimmermann and
+ Thomas Kluyver.
+
+- fix issue842: applying markers in classes no longer propagate this markers
+ to superclasses which also have markers.
+ Thanks xmo-odoo for the report and Bruno Oliveira for the PR.
+
+- preserve warning functions after call to pytest.deprecated_call. Thanks
+ Pieter Mulder for PR.
+
+- fix issue854: autouse yield_fixtures defined as class members of
+ unittest.TestCase subclasses now work as expected.
+ Thannks xmo-odoo for the report and Bruno Oliveira for the PR.
+
+- fix issue833: --fixtures now shows all fixtures of collected test files, instead of just the
+ fixtures declared on the first one.
+ Thanks Florian Bruhin for reporting and Bruno Oliveira for the PR.
+
+- fix issue863: skipped tests now report the correct reason when a skip/xfail
+ condition is met when using multiple markers.
+ Thanks Raphael Pierzina for reporting and Bruno Oliveira for the PR.
+
+- optimized tmpdir fixture initialization, which should make test sessions
+ faster (specially when using pytest-xdist). The only visible effect
+ is that now pytest uses a subdirectory in the $TEMP directory for all
+ directories created by this fixture (defaults to $TEMP/pytest-$USER).
+ Thanks Bruno Oliveira for the PR.
+
+2.7.2 (2015-06-23)
+==================
+
+- fix issue767: pytest.raises value attribute does not contain the exception
+ instance on Python 2.6. Thanks Eric Siegerman for providing the test
+ case and Bruno Oliveira for PR.
+
+- Automatically create directory for junitxml and results log.
+ Thanks Aron Curzon.
+
+- fix issue713: JUnit XML reports for doctest failures.
+ Thanks Punyashloka Biswal.
+
+- fix issue735: assertion failures on debug versions of Python 3.4+
+ Thanks Benjamin Peterson.
+
+- fix issue114: skipif marker reports to internal skipping plugin;
+ Thanks Floris Bruynooghe for reporting and Bruno Oliveira for the PR.
+
+- fix issue748: unittest.SkipTest reports to internal pytest unittest plugin.
+ Thanks Thomas De Schampheleire for reporting and Bruno Oliveira for the PR.
+
+- fix issue718: failed to create representation of sets containing unsortable
+ elements in python 2. Thanks Edison Gustavo Muenz.
+
+- fix issue756, fix issue752 (and similar issues): depend on py-1.4.29
+ which has a refined algorithm for traceback generation.
+
+
+2.7.1 (2015-05-19)
+==================
+
+- fix issue731: do not get confused by the braces which may be present
+ and unbalanced in an object's repr while collapsing False
+ explanations. Thanks Carl Meyer for the report and test case.
+
+- fix issue553: properly handling inspect.getsourcelines failures in
+ FixtureLookupError which would lead to an internal error,
+ obfuscating the original problem. Thanks talljosh for initial
+ diagnose/patch and Bruno Oliveira for final patch.
+
+- fix issue660: properly report scope-mismatch-access errors
+ independently from ordering of fixture arguments. Also
+ avoid the pytest internal traceback which does not provide
+ information to the user. Thanks Holger Krekel.
+
+- streamlined and documented release process. Also all versions
+ (in setup.py and documentation generation) are now read
+ from _pytest/__init__.py. Thanks Holger Krekel.
+
+- fixed docs to remove the notion that yield-fixtures are experimental.
+ They are here to stay :) Thanks Bruno Oliveira.
+
+- Support building wheels by using environment markers for the
+ requirements. Thanks Ionel Maries Cristian.
+
+- fixed regression to 2.6.4 which surfaced e.g. in lost stdout capture printing
+ when tests raised SystemExit. Thanks Holger Krekel.
+
+- reintroduced _pytest fixture of the pytester plugin which is used
+ at least by pytest-xdist.
+
+2.7.0 (2015-03-26)
+==================
+
+- fix issue435: make reload() work when assert rewriting is active.
+ Thanks Daniel Hahler.
+
+- fix issue616: conftest.py files and their contained fixutres are now
+ properly considered for visibility, independently from the exact
+ current working directory and test arguments that are used.
+ Many thanks to Eric Siegerman and his PR235 which contains
+ systematic tests for conftest visibility and now passes.
+ This change also introduces the concept of a ``rootdir`` which
+ is printed as a new pytest header and documented in the pytest
+ customize web page.
+
+- change reporting of "diverted" tests, i.e. tests that are collected
+ in one file but actually come from another (e.g. when tests in a test class
+ come from a base class in a different file). We now show the nodeid
+ and indicate via a postfix the other file.
+
+- add ability to set command line options by environment variable PYTEST_ADDOPTS.
+
+- added documentation on the new pytest-dev teams on bitbucket and
+ github. See https://pytest.org/latest/contributing.html .
+ Thanks to Anatoly for pushing and initial work on this.
+
+- fix issue650: new option ``--docttest-ignore-import-errors`` which
+ will turn import errors in doctests into skips. Thanks Charles Cloud
+ for the complete PR.
+
+- fix issue655: work around different ways that cause python2/3
+ to leak sys.exc_info into fixtures/tests causing failures in 3rd party code
+
+- fix issue615: assertion rewriting did not correctly escape % signs
+ when formatting boolean operations, which tripped over mixing
+ booleans with modulo operators. Thanks to Tom Viner for the report,
+ triaging and fix.
+
+- implement issue351: add ability to specify parametrize ids as a callable
+ to generate custom test ids. Thanks Brianna Laugher for the idea and
+ implementation.
+
+- introduce and document new hookwrapper mechanism useful for plugins
+ which want to wrap the execution of certain hooks for their purposes.
+ This supersedes the undocumented ``__multicall__`` protocol which
+ pytest itself and some external plugins use. Note that pytest-2.8
+ is scheduled to drop supporting the old ``__multicall__``
+ and only support the hookwrapper protocol.
+
+- majorly speed up invocation of plugin hooks
+
+- use hookwrapper mechanism in builtin pytest plugins.
+
+- add a doctest ini option for doctest flags, thanks Holger Peters.
+
+- add note to docs that if you want to mark a parameter and the
+ parameter is a callable, you also need to pass in a reason to disambiguate
+ it from the "decorator" case. Thanks Tom Viner.
+
+- "python_classes" and "python_functions" options now support glob-patterns
+ for test discovery, as discussed in issue600. Thanks Ldiary Translations.
+
+- allow to override parametrized fixtures with non-parametrized ones and vice versa (bubenkoff).
+
+- fix issue463: raise specific error for 'parameterize' misspelling (pfctdayelise).
+
+- On failure, the ``sys.last_value``, ``sys.last_type`` and
+ ``sys.last_traceback`` are set, so that a user can inspect the error
+ via postmortem debugging (almarklein).
+
+2.6.4 (2014-10-24)
+==================
+
+- Improve assertion failure reporting on iterables, by using ndiff and
+ pprint.
+
+- removed outdated japanese docs from source tree.
+
+- docs for "pytest_addhooks" hook. Thanks Bruno Oliveira.
+
+- updated plugin index docs. Thanks Bruno Oliveira.
+
+- fix issue557: with "-k" we only allow the old style "-" for negation
+ at the beginning of strings and even that is deprecated. Use "not" instead.
+ This should allow to pick parametrized tests where "-" appeared in the parameter.
+
+- fix issue604: Escape % character in the assertion message.
+
+- fix issue620: add explanation in the --genscript target about what
+ the binary blob means. Thanks Dinu Gherman.
+
+- fix issue614: fixed pastebin support.
+
+
+- fix issue620: add explanation in the --genscript target about what
+ the binary blob means. Thanks Dinu Gherman.
+
+- fix issue614: fixed pastebin support.
+
+2.6.3 (2014-09-24)
+==================
+
+- fix issue575: xunit-xml was reporting collection errors as failures
+ instead of errors, thanks Oleg Sinyavskiy.
+
+- fix issue582: fix setuptools example, thanks Laszlo Papp and Ronny
+ Pfannschmidt.
+
+- Fix infinite recursion bug when pickling capture.EncodedFile, thanks
+ Uwe Schmitt.
+
+- fix issue589: fix bad interaction with numpy and others when showing
+ exceptions. Check for precise "maximum recursion depth exceed" exception
+ instead of presuming any RuntimeError is that one (implemented in py
+ dep). Thanks Charles Cloud for analysing the issue.
+
+- fix conftest related fixture visibility issue: when running with a
+ CWD outside of a test package pytest would get fixture discovery wrong.
+ Thanks to Wolfgang Schnerring for figuring out a reproducible example.
+
+- Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the
+ timeout when interactively entering pdb). Thanks Wolfgang Schnerring.
+
+- check xfail/skip also with non-python function test items. Thanks
+ Floris Bruynooghe.
+
+2.6.2 (2014-09-05)
+==================
+
+- Added function pytest.freeze_includes(), which makes it easy to embed
+ pytest into executables using tools like cx_freeze.
+ See docs for examples and rationale. Thanks Bruno Oliveira.
+
+- Improve assertion rewriting cache invalidation precision.
+
+- fixed issue561: adapt autouse fixture example for python3.
+
+- fixed issue453: assertion rewriting issue with __repr__ containing
+ "\n{", "\n}" and "\n~".
+
+- fix issue560: correctly display code if an "else:" or "finally:" is
+ followed by statements on the same line.
+
+- Fix example in monkeypatch documentation, thanks t-8ch.
+
+- fix issue572: correct tmpdir doc example for python3.
+
+- Do not mark as universal wheel because Python 2.6 is different from
+ other builds due to the extra argparse dependency. Fixes issue566.
+ Thanks sontek.
+
+- Implement issue549: user-provided assertion messages now no longer
+ replace the py.test introspection message but are shown in addition
+ to them.
+
+2.6.1 (2014-08-07)
+==================
+
+- No longer show line numbers in the --verbose output, the output is now
+ purely the nodeid. The line number is still shown in failure reports.
+ Thanks Floris Bruynooghe.
+
+- fix issue437 where assertion rewriting could cause pytest-xdist slaves
+ to collect different tests. Thanks Bruno Oliveira.
+
+- fix issue555: add "errors" attribute to capture-streams to satisfy
+ some distutils and possibly other code accessing sys.stdout.errors.
+
+- fix issue547 capsys/capfd also work when output capturing ("-s") is disabled.
+
+- address issue170: allow pytest.mark.xfail(...) to specify expected exceptions via
+ an optional "raises=EXC" argument where EXC can be a single exception
+ or a tuple of exception classes. Thanks David Mohr for the complete
+ PR.
+
+- fix integration of pytest with unittest.mock.patch decorator when
+ it uses the "new" argument. Thanks Nicolas Delaby for test and PR.
+
+- fix issue with detecting conftest files if the arguments contain
+ "::" node id specifications (copy pasted from "-v" output)
+
+- fix issue544 by only removing "@NUM" at the end of "::" separated parts
+ and if the part has an ".py" extension
+
+- don't use py.std import helper, rather import things directly.
+ Thanks Bruno Oliveira.
+
+2.6
+===
+
+- Cache exceptions from fixtures according to their scope (issue 467).
+
+- fix issue537: Avoid importing old assertion reinterpretation code by default.
+
+- fix issue364: shorten and enhance tracebacks representation by default.
+ The new "--tb=auto" option (default) will only display long tracebacks
+ for the first and last entry. You can get the old behaviour of printing
+ all entries as long entries with "--tb=long". Also short entries by
+ default are now printed very similarly to "--tb=native" ones.
+
+- fix issue514: teach assertion reinterpretation about private class attributes
+
+- change -v output to include full node IDs of tests. Users can copy
+ a node ID from a test run, including line number, and use it as a
+ positional argument in order to run only a single test.
+
+- fix issue 475: fail early and comprehensible if calling
+ pytest.raises with wrong exception type.
+
+- fix issue516: tell in getting-started about current dependencies.
+
+- cleanup setup.py a bit and specify supported versions. Thanks Jurko
+ Gospodnetic for the PR.
+
+- change XPASS colour to yellow rather then red when tests are run
+ with -v.
+
+- fix issue473: work around mock putting an unbound method into a class
+ dict when double-patching.
+
+- fix issue498: if a fixture finalizer fails, make sure that
+ the fixture is still invalidated.
+
+- fix issue453: the result of the pytest_assertrepr_compare hook now gets
+ it's newlines escaped so that format_exception does not blow up.
+
+- internal new warning system: pytest will now produce warnings when
+ it detects oddities in your test collection or execution.
+ Warnings are ultimately sent to a new pytest_logwarning hook which is
+ currently only implemented by the terminal plugin which displays
+ warnings in the summary line and shows more details when -rw (report on
+ warnings) is specified.
+
+- change skips into warnings for test classes with an __init__ and
+ callables in test modules which look like a test but are not functions.
+
+- fix issue436: improved finding of initial conftest files from command
+ line arguments by using the result of parse_known_args rather than
+ the previous flaky heuristics. Thanks Marc Abramowitz for tests
+ and initial fixing approaches in this area.
+
+- fix issue #479: properly handle nose/unittest(2) SkipTest exceptions
+ during collection/loading of test modules. Thanks to Marc Schlaich
+ for the complete PR.
+
+- fix issue490: include pytest_load_initial_conftests in documentation
+ and improve docstring.
+
+- fix issue472: clarify that ``pytest.config.getvalue()`` cannot work
+ if it's triggered ahead of command line parsing.
+
+- merge PR123: improved integration with mock.patch decorator on tests.
+
+- fix issue412: messing with stdout/stderr FD-level streams is now
+ captured without crashes.
+
+- fix issue483: trial/py33 works now properly. Thanks Daniel Grana for PR.
+
+- improve example for pytest integration with "python setup.py test"
+ which now has a generic "-a" or "--pytest-args" option where you
+ can pass additional options as a quoted string. Thanks Trevor Bekolay.
+
+- simplified internal capturing mechanism and made it more robust
+ against tests or setups changing FD1/FD2, also better integrated
+ now with pytest.pdb() in single tests.
+
+- improvements to pytest's own test-suite leakage detection, courtesy of PRs
+ from Marc Abramowitz
+
+- fix issue492: avoid leak in test_writeorg. Thanks Marc Abramowitz.
+
+- fix issue493: don't run tests in doc directory with ``python setup.py test``
+ (use tox -e doctesting for that)
+
+- fix issue486: better reporting and handling of early conftest loading failures
+
+- some cleanup and simplification of internal conftest handling.
+
+- work a bit harder to break reference cycles when catching exceptions.
+ Thanks Jurko Gospodnetic.
+
+- fix issue443: fix skip examples to use proper comparison. Thanks Alex
+ Groenholm.
+
+- support nose-style ``__test__`` attribute on modules, classes and
+ functions, including unittest-style Classes. If set to False, the
+ test will not be collected.
+
+- fix issue512: show "<notset>" for arguments which might not be set
+ in monkeypatch plugin. Improves output in documentation.
+
+
+2.5.2 (2014-01-29)
+==================
+
+- fix issue409 -- better interoperate with cx_freeze by not
+ trying to import from collections.abc which causes problems
+ for py27/cx_freeze. Thanks Wolfgang L. for reporting and tracking it down.
+
+- fixed docs and code to use "pytest" instead of "py.test" almost everywhere.
+ Thanks Jurko Gospodnetic for the complete PR.
+
+- fix issue425: mention at end of "py.test -h" that --markers
+ and --fixtures work according to specified test path (or current dir)
+
+- fix issue413: exceptions with unicode attributes are now printed
+ correctly also on python2 and with pytest-xdist runs. (the fix
+ requires py-1.4.20)
+
+- copy, cleanup and integrate py.io capture
+ from pylib 1.4.20.dev2 (rev 13d9af95547e)
+
+- address issue416: clarify docs as to conftest.py loading semantics
+
+- fix issue429: comparing byte strings with non-ascii chars in assert
+ expressions now work better. Thanks Floris Bruynooghe.
+
+- make capfd/capsys.capture private, its unused and shouldn't be exposed
+
+
+2.5.1 (2013-12-17)
+==================
+
+- merge new documentation styling PR from Tobias Bieniek.
+
+- fix issue403: allow parametrize of multiple same-name functions within
+ a collection node. Thanks Andreas Kloeckner and Alex Gaynor for reporting
+ and analysis.
+
+- Allow parameterized fixtures to specify the ID of the parameters by
+ adding an ids argument to pytest.fixture() and pytest.yield_fixture().
+ Thanks Floris Bruynooghe.
+
+- fix issue404 by always using the binary xml escape in the junitxml
+ plugin. Thanks Ronny Pfannschmidt.
+
+- fix issue407: fix addoption docstring to point to argparse instead of
+ optparse. Thanks Daniel D. Wright.
+
+
+
+2.5.0 (2013-12-12)
+==================
+
+- dropped python2.5 from automated release testing of pytest itself
+ which means it's probably going to break soon (but still works
+ with this release we believe).
+
+- simplified and fixed implementation for calling finalizers when
+ parametrized fixtures or function arguments are involved. finalization
+ is now performed lazily at setup time instead of in the "teardown phase".
+ While this might sound odd at first, it helps to ensure that we are
+ correctly handling setup/teardown even in complex code. User-level code
+ should not be affected unless it's implementing the pytest_runtest_teardown
+ hook and expecting certain fixture instances are torn down within (very
+ unlikely and would have been unreliable anyway).
+
+- PR90: add --color=yes|no|auto option to force terminal coloring
+ mode ("auto" is default). Thanks Marc Abramowitz.
+
+- fix issue319 - correctly show unicode in assertion errors. Many
+ thanks to Floris Bruynooghe for the complete PR. Also means
+ we depend on py>=1.4.19 now.
+
+- fix issue396 - correctly sort and finalize class-scoped parametrized
+ tests independently from number of methods on the class.
+
+- refix issue323 in a better way -- parametrization should now never
+ cause Runtime Recursion errors because the underlying algorithm
+ for re-ordering tests per-scope/per-fixture is not recursive
+ anymore (it was tail-call recursive before which could lead
+ to problems for more than >966 non-function scoped parameters).
+
+- fix issue290 - there is preliminary support now for parametrizing
+ with repeated same values (sometimes useful to test if calling
+ a second time works as with the first time).
+
+- close issue240 - document precisely how pytest module importing
+ works, discuss the two common test directory layouts, and how it
+ interacts with PEP420-namespace packages.
+
+- fix issue246 fix finalizer order to be LIFO on independent fixtures
+ depending on a parametrized higher-than-function scoped fixture.
+ (was quite some effort so please bear with the complexity of this sentence :)
+ Thanks Ralph Schmitt for the precise failure example.
+
+- fix issue244 by implementing special index for parameters to only use
+ indices for paramentrized test ids
+
+- fix issue287 by running all finalizers but saving the exception
+ from the first failing finalizer and re-raising it so teardown will
+ still have failed. We reraise the first failing exception because
+ it might be the cause for other finalizers to fail.
+
+- fix ordering when mock.patch or other standard decorator-wrappings
+ are used with test methods. This fixues issue346 and should
+ help with random "xdist" collection failures. Thanks to
+ Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
+
+- fix issue357 - special case "-k" expressions to allow for
+ filtering with simple strings that are not valid python expressions.
+ Examples: "-k 1.3" matches all tests parametrized with 1.3.
+ "-k None" filters all tests that have "None" in their name
+ and conversely "-k 'not None'".
+ Previously these examples would raise syntax errors.
+
+- fix issue384 by removing the trial support code
+ since the unittest compat enhancements allow
+ trial to handle it on its own
+
+- don't hide an ImportError when importing a plugin produces one.
+ fixes issue375.
+
+- fix issue275 - allow usefixtures and autouse fixtures
+ for running doctest text files.
+
+- fix issue380 by making --resultlog only rely on longrepr instead
+ of the "reprcrash" attribute which only exists sometimes.
+
+- address issue122: allow @pytest.fixture(params=iterator) by exploding
+ into a list early on.
+
+- fix pexpect-3.0 compatibility for pytest's own tests.
+ (fixes issue386)
+
+- allow nested parametrize-value markers, thanks James Lan for the PR.
+
+- fix unicode handling with new monkeypatch.setattr(import_path, value)
+ API. Thanks Rob Dennis. Fixes issue371.
+
+- fix unicode handling with junitxml, fixes issue368.
+
+- In assertion rewriting mode on Python 2, fix the detection of coding
+ cookies. See issue #330.
+
+- make "--runxfail" turn imperative pytest.xfail calls into no ops
+ (it already did neutralize pytest.mark.xfail markers)
+
+- refine pytest / pkg_resources interactions: The AssertionRewritingHook
+ PEP302 compliant loader now registers itself with setuptools/pkg_resources
+ properly so that the pkg_resources.resource_stream method works properly.
+ Fixes issue366. Thanks for the investigations and full PR to Jason R. Coombs.
+
+- pytestconfig fixture is now session-scoped as it is the same object during the
+ whole test run. Fixes issue370.
+
+- avoid one surprising case of marker malfunction/confusion::
+
+ @pytest.mark.some(lambda arg: ...)
+ def test_function():
+
+ would not work correctly because pytest assumes @pytest.mark.some
+ gets a function to be decorated already. We now at least detect if this
+ arg is an lambda and thus the example will work. Thanks Alex Gaynor
+ for bringing it up.
+
+- xfail a test on pypy that checks wrong encoding/ascii (pypy does
+ not error out). fixes issue385.
+
+- internally make varnames() deal with classes's __init__,
+ although it's not needed by pytest itself atm. Also
+ fix caching. Fixes issue376.
+
+- fix issue221 - handle importing of namespace-package with no
+ __init__.py properly.
+
+- refactor internal FixtureRequest handling to avoid monkeypatching.
+ One of the positive user-facing effects is that the "request" object
+ can now be used in closures.
+
+- fixed version comparison in pytest.importskip(modname, minverstring)
+
+- fix issue377 by clarifying in the nose-compat docs that pytest
+ does not duplicate the unittest-API into the "plain" namespace.
+
+- fix verbose reporting for @mock'd test functions
+
+2.4.2 (2013-10-04)
+==================
+
+- on Windows require colorama and a newer py lib so that py.io.TerminalWriter()
+ now uses colorama instead of its own ctypes hacks. (fixes issue365)
+ thanks Paul Moore for bringing it up.
+
+- fix "-k" matching of tests where "repr" and "attr" and other names would
+ cause wrong matches because of an internal implementation quirk
+ (don't ask) which is now properly implemented. fixes issue345.
+
+- avoid tmpdir fixture to create too long filenames especially
+ when parametrization is used (issue354)
+
+- fix pytest-pep8 and pytest-flakes / pytest interactions
+ (collection names in mark plugin was assuming an item always
+ has a function which is not true for those plugins etc.)
+ Thanks Andi Zeidler.
+
+- introduce node.get_marker/node.add_marker API for plugins
+ like pytest-pep8 and pytest-flakes to avoid the messy
+ details of the node.keywords pseudo-dicts. Adapted
+ docs.
+
+- remove attempt to "dup" stdout at startup as it's icky.
+ the normal capturing should catch enough possibilities
+ of tests messing up standard FDs.
+
+- add pluginmanager.do_configure(config) as a link to
+ config.do_configure() for plugin-compatibility
+
+2.4.1 (2013-10-02)
+==================
+
+- When using parser.addoption() unicode arguments to the
+ "type" keyword should also be converted to the respective types.
+ thanks Floris Bruynooghe, @dnozay. (fixes issue360 and issue362)
+
+- fix dotted filename completion when using argcomplete
+ thanks Anthon van der Neuth. (fixes issue361)
+
+- fix regression when a 1-tuple ("arg",) is used for specifying
+ parametrization (the values of the parametrization were passed
+ nested in a tuple). Thanks Donald Stufft.
+
+- merge doc typo fixes, thanks Andy Dirnberger
+
+2.4
+===
+
+known incompatibilities:
+
+- if calling --genscript from python2.7 or above, you only get a
+ standalone script which works on python2.7 or above. Use Python2.6
+ to also get a python2.5 compatible version.
+
+- all xunit-style teardown methods (nose-style, pytest-style,
+ unittest-style) will not be called if the corresponding setup method failed,
+ see issue322 below.
+
+- the pytest_plugin_unregister hook wasn't ever properly called
+ and there is no known implementation of the hook - so it got removed.
+
+- pytest.fixture-decorated functions cannot be generators (i.e. use
+ yield) anymore. This change might be reversed in 2.4.1 if it causes
+ unforeseen real-life issues. However, you can always write and return
+ an inner function/generator and change the fixture consumer to iterate
+ over the returned generator. This change was done in lieu of the new
+ ``pytest.yield_fixture`` decorator, see below.
+
+new features:
+
+- experimentally introduce a new ``pytest.yield_fixture`` decorator
+ which accepts exactly the same parameters as pytest.fixture but
+ mandates a ``yield`` statement instead of a ``return statement`` from
+ fixture functions. This allows direct integration with "with-style"
+ context managers in fixture functions and generally avoids registering
+ of finalization callbacks in favour of treating the "after-yield" as
+ teardown code. Thanks Andreas Pelme, Vladimir Keleshev, Floris
+ Bruynooghe, Ronny Pfannschmidt and many others for discussions.
+
+- allow boolean expression directly with skipif/xfail
+ if a "reason" is also specified. Rework skipping documentation
+ to recommend "condition as booleans" because it prevents surprises
+ when importing markers between modules. Specifying conditions
+ as strings will remain fully supported.
+
+- reporting: color the last line red or green depending if
+ failures/errors occurred or everything passed. thanks Christian
+ Theunert.
+
+- make "import pdb ; pdb.set_trace()" work natively wrt capturing (no
+ "-s" needed anymore), making ``pytest.set_trace()`` a mere shortcut.
+
+- fix issue181: --pdb now also works on collect errors (and
+ on internal errors) . This was implemented by a slight internal
+ refactoring and the introduction of a new hook
+ ``pytest_exception_interact`` hook (see next item).
+
+- fix issue341: introduce new experimental hook for IDEs/terminals to
+ intercept debugging: ``pytest_exception_interact(node, call, report)``.
+
+- new monkeypatch.setattr() variant to provide a shorter
+ invocation for patching out classes/functions from modules:
+
+ monkeypatch.setattr("requests.get", myfunc)
+
+ will replace the "get" function of the "requests" module with ``myfunc``.
+
+- fix issue322: tearDownClass is not run if setUpClass failed. Thanks
+ Mathieu Agopian for the initial fix. Also make all of pytest/nose
+ finalizer mimic the same generic behaviour: if a setupX exists and
+ fails, don't run teardownX. This internally introduces a new method
+ "node.addfinalizer()" helper which can only be called during the setup
+ phase of a node.
+
+- simplify pytest.mark.parametrize() signature: allow to pass a
+ CSV-separated string to specify argnames. For example:
+ ``pytest.mark.parametrize("input,expected", [(1,2), (2,3)])``
+ works as well as the previous:
+ ``pytest.mark.parametrize(("input", "expected"), ...)``.
+
+- add support for setUpModule/tearDownModule detection, thanks Brian Okken.
+
+- integrate tab-completion on options through use of "argcomplete".
+ Thanks Anthon van der Neut for the PR.
+
+- change option names to be hyphen-separated long options but keep the
+ old spelling backward compatible. py.test -h will only show the
+ hyphenated version, for example "--collect-only" but "--collectonly"
+ will remain valid as well (for backward-compat reasons). Many thanks to
+ Anthon van der Neut for the implementation and to Hynek Schlawack for
+ pushing us.
+
+- fix issue 308 - allow to mark/xfail/skip individual parameter sets
+ when parametrizing. Thanks Brianna Laugher.
+
+- call new experimental pytest_load_initial_conftests hook to allow
+ 3rd party plugins to do something before a conftest is loaded.
+
+Bug fixes:
+
+- fix issue358 - capturing options are now parsed more properly
+ by using a new parser.parse_known_args method.
+
+- pytest now uses argparse instead of optparse (thanks Anthon) which
+ means that "argparse" is added as a dependency if installing into python2.6
+ environments or below.
+
+- fix issue333: fix a case of bad unittest/pytest hook interaction.
+
+- PR27: correctly handle nose.SkipTest during collection. Thanks
+ Antonio Cuni, Ronny Pfannschmidt.
+
+- fix issue355: junitxml puts name="pytest" attribute to testsuite tag.
+
+- fix issue336: autouse fixture in plugins should work again.
+
+- fix issue279: improve object comparisons on assertion failure
+ for standard datatypes and recognise collections.abc. Thanks to
+ Brianna Laugher and Mathieu Agopian.
+
+- fix issue317: assertion rewriter support for the is_package method
+
+- fix issue335: document py.code.ExceptionInfo() object returned
+ from pytest.raises(), thanks Mathieu Agopian.
+
+- remove implicit distribute_setup support from setup.py.
+
+- fix issue305: ignore any problems when writing pyc files.
+
+- SO-17664702: call fixture finalizers even if the fixture function
+ partially failed (finalizers would not always be called before)
+
+- fix issue320 - fix class scope for fixtures when mixed with
+ module-level functions. Thanks Anatloy Bubenkoff.
+
+- you can specify "-q" or "-qq" to get different levels of "quieter"
+ reporting (thanks Katarzyna Jachim)
+
+- fix issue300 - Fix order of conftest loading when starting py.test
+ in a subdirectory.
+
+- fix issue323 - sorting of many module-scoped arg parametrizations
+
+- make sessionfinish hooks execute with the same cwd-context as at
+ session start (helps fix plugin behaviour which write output files
+ with relative path such as pytest-cov)
+
+- fix issue316 - properly reference collection hooks in docs
+
+- fix issue 306 - cleanup of -k/-m options to only match markers/test
+ names/keywords respectively. Thanks Wouter van Ackooy.
+
+- improved doctest counting for doctests in python modules --
+ files without any doctest items will not show up anymore
+ and doctest examples are counted as separate test items.
+ thanks Danilo Bellini.
+
+- fix issue245 by depending on the released py-1.4.14
+ which fixes py.io.dupfile to work with files with no
+ mode. Thanks Jason R. Coombs.
+
+- fix junitxml generation when test output contains control characters,
+ addressing issue267, thanks Jaap Broekhuizen
+
+- fix issue338: honor --tb style for setup/teardown errors as well. Thanks Maho.
+
+- fix issue307 - use yaml.safe_load in example, thanks Mark Eichin.
+
+- better parametrize error messages, thanks Brianna Laugher
+
+- pytest_terminal_summary(terminalreporter) hooks can now use
+ ".section(title)" and ".line(msg)" methods to print extra
+ information at the end of a test run.
+
+2.3.5 (2013-04-30)
+==================
+
+- fix issue169: respect --tb=style with setup/teardown errors as well.
+
+- never consider a fixture function for test function collection
+
+- allow re-running of test items / helps to fix pytest-reruntests plugin
+ and also help to keep less fixture/resource references alive
+
+- put captured stdout/stderr into junitxml output even for passing tests
+ (thanks Adam Goucher)
+
+- Issue 265 - integrate nose setup/teardown with setupstate
+ so it doesn't try to teardown if it did not setup
+
+- issue 271 - don't write junitxml on slave nodes
+
+- Issue 274 - don't try to show full doctest example
+ when doctest does not know the example location
+
+- issue 280 - disable assertion rewriting on buggy CPython 2.6.0
+
+- inject "getfixture()" helper to retrieve fixtures from doctests,
+ thanks Andreas Zeidler
+
+- issue 259 - when assertion rewriting, be consistent with the default
+ source encoding of ASCII on Python 2
+
+- issue 251 - report a skip instead of ignoring classes with init
+
+- issue250 unicode/str mixes in parametrization names and values now works
+
+- issue257, assertion-triggered compilation of source ending in a
+ comment line doesn't blow up in python2.5 (fixed through py>=1.4.13.dev6)
+
+- fix --genscript option to generate standalone scripts that also
+ work with python3.3 (importer ordering)
+
+- issue171 - in assertion rewriting, show the repr of some
+ global variables
+
+- fix option help for "-k"
+
+- move long description of distribution into README.rst
+
+- improve docstring for metafunc.parametrize()
+
+- fix bug where using capsys with pytest.set_trace() in a test
+ function would break when looking at capsys.readouterr()
+
+- allow to specify prefixes starting with "_" when
+ customizing python_functions test discovery. (thanks Graham Horler)
+
+- improve PYTEST_DEBUG tracing output by putting
+ extra data on a new lines with additional indent
+
+- ensure OutcomeExceptions like skip/fail have initialized exception attributes
+
+- issue 260 - don't use nose special setup on plain unittest cases
+
+- fix issue134 - print the collect errors that prevent running specified test items
+
+- fix issue266 - accept unicode in MarkEvaluator expressions
+
+2.3.4 (2012-11-20)
+==================
+
+- yielded test functions will now have autouse-fixtures active but
+ cannot accept fixtures as funcargs - it's anyway recommended to
+ rather use the post-2.0 parametrize features instead of yield, see:
+ http://pytest.org/latest/example/parametrize.html
+- fix autouse-issue where autouse-fixtures would not be discovered
+ if defined in a a/conftest.py file and tests in a/tests/test_some.py
+- fix issue226 - LIFO ordering for fixture teardowns
+- fix issue224 - invocations with >256 char arguments now work
+- fix issue91 - add/discuss package/directory level setups in example
+- allow to dynamically define markers via
+ item.keywords[...]=assignment integrating with "-m" option
+- make "-k" accept an expressions the same as with "-m" so that one
+ can write: -k "name1 or name2" etc. This is a slight incompatibility
+ if you used special syntax like "TestClass.test_method" which you now
+ need to write as -k "TestClass and test_method" to match a certain
+ method in a certain test class.
+
+2.3.3 (2012-11-06)
+==================
+
+- fix issue214 - parse modules that contain special objects like e. g.
+ flask's request object which blows up on getattr access if no request
+ is active. thanks Thomas Waldmann.
+
+- fix issue213 - allow to parametrize with values like numpy arrays that
+ do not support an __eq__ operator
+
+- fix issue215 - split test_python.org into multiple files
+
+- fix issue148 - @unittest.skip on classes is now recognized and avoids
+ calling setUpClass/tearDownClass, thanks Pavel Repin
+
+- fix issue209 - reintroduce python2.4 support by depending on newer
+ pylib which re-introduced statement-finding for pre-AST interpreters
+
+- nose support: only call setup if it's a callable, thanks Andrew
+ Taumoefolau
+
+- fix issue219 - add py2.4-3.3 classifiers to TROVE list
+
+- in tracebacks *,** arg values are now shown next to normal arguments
+ (thanks Manuel Jacob)
+
+- fix issue217 - support mock.patch with pytest's fixtures - note that
+ you need either mock-1.0.1 or the python3.3 builtin unittest.mock.
+
+- fix issue127 - improve documentation for pytest_addoption() and
+ add a ``config.getoption(name)`` helper function for consistency.
+
+2.3.2 (2012-10-25)
+==================
+
+- fix issue208 and fix issue29 use new py version to avoid long pauses
+ when printing tracebacks in long modules
+
+- fix issue205 - conftests in subdirs customizing
+ pytest_pycollect_makemodule and pytest_pycollect_makeitem
+ now work properly
+
+- fix teardown-ordering for parametrized setups
+
+- fix issue127 - better documentation for pytest_addoption
+ and related objects.
+
+- fix unittest behaviour: TestCase.runtest only called if there are
+ test methods defined
+
+- improve trial support: don't collect its empty
+ unittest.TestCase.runTest() method
+
+- "python setup.py test" now works with pytest itself
+
+- fix/improve internal/packaging related bits:
+
+ - exception message check of test_nose.py now passes on python33 as well
+
+ - issue206 - fix test_assertrewrite.py to work when a global
+ PYTHONDONTWRITEBYTECODE=1 is present
+
+ - add tox.ini to pytest distribution so that ignore-dirs and others config
+ bits are properly distributed for maintainers who run pytest-own tests
+
+2.3.1 (2012-10-20)
+==================
+
+- fix issue202 - fix regression: using "self" from fixture functions now
+ works as expected (it's the same "self" instance that a test method
+ which uses the fixture sees)
+
+- skip pexpect using tests (test_pdb.py mostly) on freebsd* systems
+ due to pexpect not supporting it properly (hanging)
+
+- link to web pages from --markers output which provides help for
+ pytest.mark.* usage.
+
+2.3.0 (2012-10-19)
+==================
+
+- fix issue202 - better automatic names for parametrized test functions
+- fix issue139 - introduce @pytest.fixture which allows direct scoping
+ and parametrization of funcarg factories.
+- fix issue198 - conftest fixtures were not found on windows32 in some
+ circumstances with nested directory structures due to path manipulation issues
+- fix issue193 skip test functions with were parametrized with empty
+ parameter sets
+- fix python3.3 compat, mostly reporting bits that previously depended
+ on dict ordering
+- introduce re-ordering of tests by resource and parametrization setup
+ which takes precedence to the usual file-ordering
+- fix issue185 monkeypatching time.time does not cause pytest to fail
+- fix issue172 duplicate call of pytest.fixture decoratored setup_module
+ functions
+- fix junitxml=path construction so that if tests change the
+ current working directory and the path is a relative path
+ it is constructed correctly from the original current working dir.
+- fix "python setup.py test" example to cause a proper "errno" return
+- fix issue165 - fix broken doc links and mention stackoverflow for FAQ
+- catch unicode-issues when writing failure representations
+ to terminal to prevent the whole session from crashing
+- fix xfail/skip confusion: a skip-mark or an imperative pytest.skip
+ will now take precedence before xfail-markers because we
+ can't determine xfail/xpass status in case of a skip. see also:
+ http://stackoverflow.com/questions/11105828/in-py-test-when-i-explicitly-skip-a-test-that-is-marked-as-xfail-how-can-i-get
+
+- always report installed 3rd party plugins in the header of a test run
+
+- fix issue160: a failing setup of an xfail-marked tests should
+ be reported as xfail (not xpass)
+
+- fix issue128: show captured output when capsys/capfd are used
+
+- fix issue179: properly show the dependency chain of factories
+
+- pluginmanager.register(...) now raises ValueError if the
+ plugin has been already registered or the name is taken
+
+- fix issue159: improve http://pytest.org/latest/faq.html
+ especially with respect to the "magic" history, also mention
+ pytest-django, trial and unittest integration.
+
+- make request.keywords and node.keywords writable. All descendant
+ collection nodes will see keyword values. Keywords are dictionaries
+ containing markers and other info.
+
+- fix issue 178: xml binary escapes are now wrapped in py.xml.raw
+
+- fix issue 176: correctly catch the builtin AssertionError
+ even when we replaced AssertionError with a subclass on the
+ python level
+
+- factory discovery no longer fails with magic global callables
+ that provide no sane __code__ object (mock.call for example)
+
+- fix issue 182: testdir.inprocess_run now considers passed plugins
+
+- fix issue 188: ensure sys.exc_info is clear on python2
+ before calling into a test
+
+- fix issue 191: add unittest TestCase runTest method support
+- fix issue 156: monkeypatch correctly handles class level descriptors
+
+- reporting refinements:
+
+ - pytest_report_header now receives a "startdir" so that
+ you can use startdir.bestrelpath(yourpath) to show
+ nice relative path
+
+ - allow plugins to implement both pytest_report_header and
+ pytest_sessionstart (sessionstart is invoked first).
+
+ - don't show deselected reason line if there is none
+
+ - py.test -vv will show all of assert comparisons instead of truncating
+
+2.2.4 (2012-05-22)
+==================
+
+- fix error message for rewritten assertions involving the % operator
+- fix issue 126: correctly match all invalid xml characters for junitxml
+ binary escape
+- fix issue with unittest: now @unittest.expectedFailure markers should
+ be processed correctly (you can also use @pytest.mark markers)
+- document integration with the extended distribute/setuptools test commands
+- fix issue 140: properly get the real functions
+ of bound classmethods for setup/teardown_class
+- fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net
+- fix issue #143: call unconfigure/sessionfinish always when
+ configure/sessionstart where called
+- fix issue #144: better mangle test ids to junitxml classnames
+- upgrade distribute_setup.py to 0.6.27
+
+2.2.3 (2012-02-05)
+==================
+
+- fix uploaded package to only include necessary files
+
+2.2.2 (2012-02-05)
+==================
+
+- fix issue101: wrong args to unittest.TestCase test function now
+ produce better output
+- fix issue102: report more useful errors and hints for when a
+ test directory was renamed and some pyc/__pycache__ remain
+- fix issue106: allow parametrize to be applied multiple times
+ e.g. from module, class and at function level.
+- fix issue107: actually perform session scope finalization
+- don't check in parametrize if indirect parameters are funcarg names
+- add chdir method to monkeypatch funcarg
+- fix crash resulting from calling monkeypatch undo a second time
+- fix issue115: make --collectonly robust against early failure
+ (missing files/directories)
+- "-qq --collectonly" now shows only files and the number of tests in them
+- "-q --collectonly" now shows test ids
+- allow adding of attributes to test reports such that it also works
+ with distributed testing (no upgrade of pytest-xdist needed)
+
+2.2.1 (2011-12-16)
+==================
+
+- fix issue99 (in pytest and py) internallerrors with resultlog now
+ produce better output - fixed by normalizing pytest_internalerror
+ input arguments.
+- fix issue97 / traceback issues (in pytest and py) improve traceback output
+ in conjunction with jinja2 and cython which hack tracebacks
+- fix issue93 (in pytest and pytest-xdist) avoid "delayed teardowns":
+ the final test in a test node will now run its teardown directly
+ instead of waiting for the end of the session. Thanks Dave Hunt for
+ the good reporting and feedback. The pytest_runtest_protocol as well
+ as the pytest_runtest_teardown hooks now have "nextitem" available
+ which will be None indicating the end of the test run.
+- fix collection crash due to unknown-source collected items, thanks
+ to Ralf Schmitt (fixed by depending on a more recent pylib)
+
+2.2.0 (2011-11-18)
+==================
+
+- fix issue90: introduce eager tearing down of test items so that
+ teardown function are called earlier.
+- add an all-powerful metafunc.parametrize function which allows to
+ parametrize test function arguments in multiple steps and therefore
+ from independent plugins and places.
+- add a @pytest.mark.parametrize helper which allows to easily
+ call a test function with different argument values
+- Add examples to the "parametrize" example page, including a quick port
+ of Test scenarios and the new parametrize function and decorator.
+- introduce registration for "pytest.mark.*" helpers via ini-files
+ or through plugin hooks. Also introduce a "--strict" option which
+ will treat unregistered markers as errors
+ allowing to avoid typos and maintain a well described set of markers
+ for your test suite. See exaples at http://pytest.org/latest/mark.html
+ and its links.
+- issue50: introduce "-m marker" option to select tests based on markers
+ (this is a stricter and more predictable version of '-k' in that "-m"
+ only matches complete markers and has more obvious rules for and/or
+ semantics.
+- new feature to help optimizing the speed of your tests:
+ --durations=N option for displaying N slowest test calls
+ and setup/teardown methods.
+- fix issue87: --pastebin now works with python3
+- fix issue89: --pdb with unexpected exceptions in doctest work more sensibly
+- fix and cleanup pytest's own test suite to not leak FDs
+- fix issue83: link to generated funcarg list
+- fix issue74: pyarg module names are now checked against imp.find_module false positives
+- fix compatibility with twisted/trial-11.1.0 use cases
+- simplify Node.listchain
+- simplify junitxml output code by relying on py.xml
+- add support for skip properties on unittest classes and functions
+
+2.1.3 (2011-10-18)
+==================
+
+- fix issue79: assertion rewriting failed on some comparisons in boolops
+- correctly handle zero length arguments (a la pytest '')
+- fix issue67 / junitxml now contains correct test durations, thanks ronny
+- fix issue75 / skipping test failure on jython
+- fix issue77 / Allow assertrepr_compare hook to apply to a subset of tests
+
+2.1.2 (2011-09-24)
+==================
+
+- fix assertion rewriting on files with windows newlines on some Python versions
+- refine test discovery by package/module name (--pyargs), thanks Florian Mayer
+- fix issue69 / assertion rewriting fixed on some boolean operations
+- fix issue68 / packages now work with assertion rewriting
+- fix issue66: use different assertion rewriting caches when the -O option is passed
+- don't try assertion rewriting on Jython, use reinterp
+
+2.1.1
+=====
+
+- fix issue64 / pytest.set_trace now works within pytest_generate_tests hooks
+- fix issue60 / fix error conditions involving the creation of __pycache__
+- fix issue63 / assertion rewriting on inserts involving strings containing '%'
+- fix assertion rewriting on calls with a ** arg
+- don't cache rewritten modules if bytecode generation is disabled
+- fix assertion rewriting in read-only directories
+- fix issue59: provide system-out/err tags for junitxml output
+- fix issue61: assertion rewriting on boolean operations with 3 or more operands
+- you can now build a man page with "cd doc ; make man"
+
+2.1.0 (2011-07-09)
+==================
+
+- fix issue53 call nosestyle setup functions with correct ordering
+- fix issue58 and issue59: new assertion code fixes
+- merge Benjamin's assertionrewrite branch: now assertions
+ for test modules on python 2.6 and above are done by rewriting
+ the AST and saving the pyc file before the test module is imported.
+ see doc/assert.txt for more info.
+- fix issue43: improve doctests with better traceback reporting on
+ unexpected exceptions
+- fix issue47: timing output in junitxml for test cases is now correct
+- fix issue48: typo in MarkInfo repr leading to exception
+- fix issue49: avoid confusing error when initizaliation partially fails
+- fix issue44: env/username expansion for junitxml file path
+- show releaselevel information in test runs for pypy
+- reworked doc pages for better navigation and PDF generation
+- report KeyboardInterrupt even if interrupted during session startup
+- fix issue 35 - provide PDF doc version and download link from index page
+
+2.0.3 (2011-05-11)
+==================
+
+- fix issue38: nicer tracebacks on calls to hooks, particularly early
+ configure/sessionstart ones
+
+- fix missing skip reason/meta information in junitxml files, reported
+ via http://lists.idyll.org/pipermail/testing-in-python/2011-March/003928.html
+
+- fix issue34: avoid collection failure with "test" prefixed classes
+ deriving from object.
+
+- don't require zlib (and other libs) for genscript plugin without
+ --genscript actually being used.
+
+- speed up skips (by not doing a full traceback representation
+ internally)
+
+- fix issue37: avoid invalid characters in junitxml's output
+
+2.0.2 (2011-03-09)
+==================
+
+- tackle issue32 - speed up test runs of very quick test functions
+ by reducing the relative overhead
+
+- fix issue30 - extended xfail/skipif handling and improved reporting.
+ If you have a syntax error in your skip/xfail
+ expressions you now get nice error reports.
+
+ Also you can now access module globals from xfail/skipif
+ expressions so that this for example works now::
+
+ import pytest
+ import mymodule
+ @pytest.mark.skipif("mymodule.__version__[0] == "1")
+ def test_function():
+ pass
+
+ This will not run the test function if the module's version string
+ does not start with a "1". Note that specifying a string instead
+ of a boolean expressions allows py.test to report meaningful information
+ when summarizing a test run as to what conditions lead to skipping
+ (or xfail-ing) tests.
+
+- fix issue28 - setup_method and pytest_generate_tests work together
+ The setup_method fixture method now gets called also for
+ test function invocations generated from the pytest_generate_tests
+ hook.
+
+- fix issue27 - collectonly and keyword-selection (-k) now work together
+ Also, if you do "py.test --collectonly -q" you now get a flat list
+ of test ids that you can use to paste to the py.test commandline
+ in order to execute a particular test.
+
+- fix issue25 avoid reported problems with --pdb and python3.2/encodings output
+
+- fix issue23 - tmpdir argument now works on Python3.2 and WindowsXP
+ Starting with Python3.2 os.symlink may be supported. By requiring
+ a newer py lib version the py.path.local() implementation acknowledges
+ this.
+
+- fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular
+ thanks to Laura Creighton who also reviewed parts of the documentation.
+
+- fix slightly wrong output of verbose progress reporting for classes
+ (thanks Amaury)
+
+- more precise (avoiding of) deprecation warnings for node.Class|Function accesses
+
+- avoid std unittest assertion helper code in tracebacks (thanks Ronny)
+
+2.0.1 (2011-02-07)
+==================
+
+- refine and unify initial capturing so that it works nicely
+ even if the logging module is used on an early-loaded conftest.py
+ file or plugin.
+- allow to omit "()" in test ids to allow for uniform test ids
+ as produced by Alfredo's nice pytest.vim plugin.
+- fix issue12 - show plugin versions with "--version" and
+ "--traceconfig" and also document how to add extra information
+ to reporting test header
+- fix issue17 (import-* reporting issue on python3) by
+ requiring py>1.4.0 (1.4.1 is going to include it)
+- fix issue10 (numpy arrays truth checking) by refining
+ assertion interpretation in py lib
+- fix issue15: make nose compatibility tests compatible
+ with python3 (now that nose-1.0 supports python3)
+- remove somewhat surprising "same-conftest" detection because
+ it ignores conftest.py when they appear in several subdirs.
+- improve assertions ("not in"), thanks Floris Bruynooghe
+- improve behaviour/warnings when running on top of "python -OO"
+ (assertions and docstrings are turned off, leading to potential
+ false positives)
+- introduce a pytest_cmdline_processargs(args) hook
+ to allow dynamic computation of command line arguments.
+ This fixes a regression because py.test prior to 2.0
+ allowed to set command line options from conftest.py
+ files which so far pytest-2.0 only allowed from ini-files now.
+- fix issue7: assert failures in doctest modules.
+ unexpected failures in doctests will not generally
+ show nicer, i.e. within the doctest failing context.
+- fix issue9: setup/teardown functions for an xfail-marked
+ test will report as xfail if they fail but report as normally
+ passing (not xpassing) if they succeed. This only is true
+ for "direct" setup/teardown invocations because teardown_class/
+ teardown_module cannot closely relate to a single test.
+- fix issue14: no logging errors at process exit
+- refinements to "collecting" output on non-ttys
+- refine internal plugin registration and --traceconfig output
+- introduce a mechanism to prevent/unregister plugins from the
+ command line, see http://pytest.org/plugins.html#cmdunregister
+- activate resultlog plugin by default
+- fix regression wrt yielded tests which due to the
+ collection-before-running semantics were not
+ setup as with pytest 1.3.4. Note, however, that
+ the recommended and much cleaner way to do test
+ parametraization remains the "pytest_generate_tests"
+ mechanism, see the docs.
+
+2.0.0 (2010-11-25)
+==================
+
+- pytest-2.0 is now its own package and depends on pylib-2.0
+- new ability: python -m pytest / python -m pytest.main ability
+- new python invocation: pytest.main(args, plugins) to load
+ some custom plugins early.
+- try harder to run unittest test suites in a more compatible manner
+ by deferring setup/teardown semantics to the unittest package.
+ also work harder to run twisted/trial and Django tests which
+ should now basically work by default.
+- introduce a new way to set config options via ini-style files,
+ by default setup.cfg and tox.ini files are searched. The old
+ ways (certain environment variables, dynamic conftest.py reading
+ is removed).
+- add a new "-q" option which decreases verbosity and prints a more
+ nose/unittest-style "dot" output.
+- fix issue135 - marks now work with unittest test cases as well
+- fix issue126 - introduce py.test.set_trace() to trace execution via
+ PDB during the running of tests even if capturing is ongoing.
+- fix issue123 - new "python -m py.test" invocation for py.test
+ (requires Python 2.5 or above)
+- fix issue124 - make reporting more resilient against tests opening
+ files on filedescriptor 1 (stdout).
+- fix issue109 - sibling conftest.py files will not be loaded.
+ (and Directory collectors cannot be customized anymore from a Directory's
+ conftest.py - this needs to happen at least one level up).
+- introduce (customizable) assertion failure representations and enhance
+ output on assertion failures for comparisons and other cases (Floris Bruynooghe)
+- nose-plugin: pass through type-signature failures in setup/teardown
+ functions instead of not calling them (Ed Singleton)
+- remove py.test.collect.Directory (follows from a major refactoring
+ and simplification of the collection process)
+- majorly reduce py.test core code, shift function/python testing to own plugin
+- fix issue88 (finding custom test nodes from command line arg)
+- refine 'tmpdir' creation, will now create basenames better associated
+ with test names (thanks Ronny)
+- "xpass" (unexpected pass) tests don't cause exitcode!=0
+- fix issue131 / issue60 - importing doctests in __init__ files used as namespace packages
+- fix issue93 stdout/stderr is captured while importing conftest.py
+- fix bug: unittest collected functions now also can have "pytestmark"
+ applied at class/module level
+- add ability to use "class" level for cached_setup helper
+- fix strangeness: mark.* objects are now immutable, create new instances
+
+1.3.4 (2010-09-14)
+==================
+
+- fix issue111: improve install documentation for windows
+- fix issue119: fix custom collectability of __init__.py as a module
+- fix issue116: --doctestmodules work with __init__.py files as well
+- fix issue115: unify internal exception passthrough/catching/GeneratorExit
+- fix issue118: new --tb=native for presenting cpython-standard exceptions
+
+1.3.3 (2010-07-30)
+==================
+
+- fix issue113: assertion representation problem with triple-quoted strings
+ (and possibly other cases)
+- make conftest loading detect that a conftest file with the same
+ content was already loaded, avoids surprises in nested directory structures
+ which can be produced e.g. by Hudson. It probably removes the need to use
+ --confcutdir in most cases.
+- fix terminal coloring for win32
+ (thanks Michael Foord for reporting)
+- fix weirdness: make terminal width detection work on stdout instead of stdin
+ (thanks Armin Ronacher for reporting)
+- remove trailing whitespace in all py/text distribution files
+
+1.3.2 (2010-07-08)
+==================
+
+**New features**
+
+- fix issue103: introduce py.test.raises as context manager, examples::
+
+ with py.test.raises(ZeroDivisionError):
+ x = 0
+ 1 / x
+
+ with py.test.raises(RuntimeError) as excinfo:
+ call_something()
+
+ # you may do extra checks on excinfo.value|type|traceback here
+
+ (thanks Ronny Pfannschmidt)
+
+- Funcarg factories can now dynamically apply a marker to a
+ test invocation. This is for example useful if a factory
+ provides parameters to a test which are expected-to-fail::
+
+ def pytest_funcarg__arg(request):
+ request.applymarker(py.test.mark.xfail(reason="flaky config"))
+ ...
+
+ def test_function(arg):
+ ...
+
+- improved error reporting on collection and import errors. This makes
+ use of a more general mechanism, namely that for custom test item/collect
+ nodes ``node.repr_failure(excinfo)`` is now uniformly called so that you can
+ override it to return a string error representation of your choice
+ which is going to be reported as a (red) string.
+
+- introduce '--junitprefix=STR' option to prepend a prefix
+ to all reports in the junitxml file.
+
+**Bug fixes**
+
+- make tests and the ``pytest_recwarn`` plugin in particular fully compatible
+ to Python2.7 (if you use the ``recwarn`` funcarg warnings will be enabled so that
+ you can properly check for their existence in a cross-python manner).
+- refine --pdb: ignore xfailed tests, unify its TB-reporting and
+ don't display failures again at the end.
+- fix assertion interpretation with the ** operator (thanks Benjamin Peterson)
+- fix issue105 assignment on the same line as a failing assertion (thanks Benjamin Peterson)
+- fix issue104 proper escaping for test names in junitxml plugin (thanks anonymous)
+- fix issue57 -f|--looponfail to work with xpassing tests (thanks Ronny)
+- fix issue92 collectonly reporter and --pastebin (thanks Benjamin Peterson)
+- fix py.code.compile(source) to generate unique filenames
+- fix assertion re-interp problems on PyPy, by defering code
+ compilation to the (overridable) Frame.eval class. (thanks Amaury Forgeot)
+- fix py.path.local.pyimport() to work with directories
+- streamline py.path.local.mkdtemp implementation and usage
+- don't print empty lines when showing junitxml-filename
+- add optional boolean ignore_errors parameter to py.path.local.remove
+- fix terminal writing on win32/python2.4
+- py.process.cmdexec() now tries harder to return properly encoded unicode objects
+ on all python versions
+- install plain py.test/py.which scripts also for Jython, this helps to
+ get canonical script paths in virtualenv situations
+- make path.bestrelpath(path) return ".", note that when calling
+ X.bestrelpath the assumption is that X is a directory.
+- make initial conftest discovery ignore "--" prefixed arguments
+- fix resultlog plugin when used in an multicpu/multihost xdist situation
+ (thanks Jakub Gustak)
+- perform distributed testing related reporting in the xdist-plugin
+ rather than having dist-related code in the generic py.test
+ distribution
+- fix homedir detection on Windows
+- ship distribute_setup.py version 0.6.13
+
+1.3.1 (2010-05-25)
+==================
+
+**New features**
+
+- issue91: introduce new py.test.xfail(reason) helper
+ to imperatively mark a test as expected to fail. Can
+ be used from within setup and test functions. This is
+ useful especially for parametrized tests when certain
+ configurations are expected-to-fail. In this case the
+ declarative approach with the @py.test.mark.xfail cannot
+ be used as it would mark all configurations as xfail.
+
+- issue102: introduce new --maxfail=NUM option to stop
+ test runs after NUM failures. This is a generalization
+ of the '-x' or '--exitfirst' option which is now equivalent
+ to '--maxfail=1'. Both '-x' and '--maxfail' will
+ now also print a line near the end indicating the Interruption.
+
+- issue89: allow py.test.mark decorators to be used on classes
+ (class decorators were introduced with python2.6) and
+ also allow to have multiple markers applied at class/module level
+ by specifying a list.
+
+- improve and refine letter reporting in the progress bar:
+ . pass
+ f failed test
+ s skipped tests (reminder: use for dependency/platform mismatch only)
+ x xfailed test (test that was expected to fail)
+ X xpassed test (test that was expected to fail but passed)
+
+ You can use any combination of 'fsxX' with the '-r' extended
+ reporting option. The xfail/xpass results will show up as
+ skipped tests in the junitxml output - which also fixes
+ issue99.
+
+- make py.test.cmdline.main() return the exitstatus instead of raising
+ SystemExit and also allow it to be called multiple times. This of
+ course requires that your application and tests are properly teared
+ down and don't have global state.
+
+**Bug Fixes**
+
+- improved traceback presentation:
+ - improved and unified reporting for "--tb=short" option
+ - Errors during test module imports are much shorter, (using --tb=short style)
+ - raises shows shorter more relevant tracebacks
+ - --fulltrace now more systematically makes traces longer / inhibits cutting
+
+- improve support for raises and other dynamically compiled code by
+ manipulating python's linecache.cache instead of the previous
+ rather hacky way of creating custom code objects. This makes
+ it seemlessly work on Jython and PyPy where it previously didn't.
+
+- fix issue96: make capturing more resilient against Control-C
+ interruptions (involved somewhat substantial refactoring
+ to the underlying capturing functionality to avoid race
+ conditions).
+
+- fix chaining of conditional skipif/xfail decorators - so it works now
+ as expected to use multiple @py.test.mark.skipif(condition) decorators,
+ including specific reporting which of the conditions lead to skipping.
+
+- fix issue95: late-import zlib so that it's not required
+ for general py.test startup.
+
+- fix issue94: make reporting more robust against bogus source code
+ (and internally be more careful when presenting unexpected byte sequences)
+
+
+1.3.0 (2010-05-05)
+==================
+
+- deprecate --report option in favour of a new shorter and easier to
+ remember -r option: it takes a string argument consisting of any
+ combination of 'xfsX' characters. They relate to the single chars
+ you see during the dotted progress printing and will print an extra line
+ per test at the end of the test run. This extra line indicates the exact
+ position or test ID that you directly paste to the py.test cmdline in order
+ to re-run a particular test.
+
+- allow external plugins to register new hooks via the new
+ pytest_addhooks(pluginmanager) hook. The new release of
+ the pytest-xdist plugin for distributed and looponfailing
+ testing requires this feature.
+
+- add a new pytest_ignore_collect(path, config) hook to allow projects and
+ plugins to define exclusion behaviour for their directory structure -
+ for example you may define in a conftest.py this method::
+
+ def pytest_ignore_collect(path):
+ return path.check(link=1)
+
+ to prevent even a collection try of any tests in symlinked dirs.
+
+- new pytest_pycollect_makemodule(path, parent) hook for
+ allowing customization of the Module collection object for a
+ matching test module.
+
+- extend and refine xfail mechanism:
+ ``@py.test.mark.xfail(run=False)`` do not run the decorated test
+ ``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries
+ specifying ``--runxfail`` on command line virtually ignores xfail markers
+
+- expose (previously internal) commonly useful methods:
+ py.io.get_terminal_with() -> return terminal width
+ py.io.ansi_print(...) -> print colored/bold text on linux/win32
+ py.io.saferepr(obj) -> return limited representation string
+
+- expose test outcome related exceptions as py.test.skip.Exception,
+ py.test.raises.Exception etc., useful mostly for plugins
+ doing special outcome interpretation/tweaking
+
+- (issue85) fix junitxml plugin to handle tests with non-ascii output
+
+- fix/refine python3 compatibility (thanks Benjamin Peterson)
+
+- fixes for making the jython/win32 combination work, note however:
+ jython2.5.1/win32 does not provide a command line launcher, see
+ http://bugs.jython.org/issue1491 . See pylib install documentation
+ for how to work around.
+
+- fixes for handling of unicode exception values and unprintable objects
+
+- (issue87) fix unboundlocal error in assertionold code
+
+- (issue86) improve documentation for looponfailing
+
+- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method
+
+- ship distribute_setup.py version 0.6.10
+
+- added links to the new capturelog and coverage plugins
+
+
+1.2.0 (2010-01-18)
+==================
+
+- refined usage and options for "py.cleanup"::
+
+ py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
+ py.cleanup -e .swp -e .cache # also remove files with these extensions
+ py.cleanup -s # remove "build" and "dist" directory next to setup.py files
+ py.cleanup -d # also remove empty directories
+ py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
+ py.cleanup -n # dry run, only show what would be removed
+
+- add a new option "py.test --funcargs" which shows available funcargs
+ and their help strings (docstrings on their respective factory function)
+ for a given test path
+
+- display a short and concise traceback if a funcarg lookup fails
+
+- early-load "conftest.py" files in non-dot first-level sub directories.
+ allows to conveniently keep and access test-related options in a ``test``
+ subdir and still add command line options.
+
+- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
+
+- fix issue78: always call python-level teardown functions even if the
+ according setup failed. This includes refinements for calling setup_module/class functions
+ which will now only be called once instead of the previous behaviour where they'd be called
+ multiple times if they raise an exception (including a Skipped exception). Any exception
+ will be re-corded and associated with all tests in the according module/class scope.
+
+- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
+
+- fix pdb debugging to be in the correct frame on raises-related errors
+
+- update apipkg.py to fix an issue where recursive imports might
+ unnecessarily break importing
+
+- fix plugin links
+
+1.1.1 (2009-11-24)
+==================
+
+- moved dist/looponfailing from py.test core into a new
+ separately released pytest-xdist plugin.
+
+- new junitxml plugin: --junitxml=path will generate a junit style xml file
+ which is processable e.g. by the Hudson CI system.
+
+- new option: --genscript=path will generate a standalone py.test script
+ which will not need any libraries installed. thanks to Ralf Schmitt.
+
+- new option: --ignore will prevent specified path from collection.
+ Can be specified multiple times.
+
+- new option: --confcutdir=dir will make py.test only consider conftest
+ files that are relative to the specified dir.
+
+- new funcarg: "pytestconfig" is the pytest config object for access
+ to command line args and can now be easily used in a test.
+
+- install ``py.test`` and ``py.which`` with a ``-$VERSION`` suffix to
+ disambiguate between Python3, python2.X, Jython and PyPy installed versions.
+
+- new "pytestconfig" funcarg allows access to test config object
+
+- new "pytest_report_header" hook can return additional lines
+ to be displayed at the header of a test run.
+
+- (experimental) allow "py.test path::name1::name2::..." for pointing
+ to a test within a test collection directly. This might eventually
+ evolve as a full substitute to "-k" specifications.
+
+- streamlined plugin loading: order is now as documented in
+ customize.html: setuptools, ENV, commandline, conftest.
+ also setuptools entry point names are turned to canonical namees ("pytest_*")
+
+- automatically skip tests that need 'capfd' but have no os.dup
+
+- allow pytest_generate_tests to be defined in classes as well
+
+- deprecate usage of 'disabled' attribute in favour of pytestmark
+- deprecate definition of Directory, Module, Class and Function nodes
+ in conftest.py files. Use pytest collect hooks instead.
+
+- collection/item node specific runtest/collect hooks are only called exactly
+ on matching conftest.py files, i.e. ones which are exactly below
+ the filesystem path of an item
+
+- change: the first pytest_collect_directory hook to return something
+ will now prevent further hooks to be called.
+
+- change: figleaf plugin now requires --figleaf to run. Also
+ change its long command line options to be a bit shorter (see py.test -h).
+
+- change: pytest doctest plugin is now enabled by default and has a
+ new option --doctest-glob to set a pattern for file matches.
+
+- change: remove internal py._* helper vars, only keep py._pydir
+
+- robustify capturing to survive if custom pytest_runtest_setup
+ code failed and prevented the capturing setup code from running.
+
+- make py.test.* helpers provided by default plugins visible early -
+ works transparently both for pydoc and for interactive sessions
+ which will regularly see e.g. py.test.mark and py.test.importorskip.
+
+- simplify internal plugin manager machinery
+- simplify internal collection tree by introducing a RootCollector node
+
+- fix assert reinterpreation that sees a call containing "keyword=..."
+
+- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
+ hooks on slaves during dist-testing, report module/session teardown
+ hooks correctly.
+
+- fix issue65: properly handle dist-testing if no
+ execnet/py lib installed remotely.
+
+- skip some install-tests if no execnet is available
+
+- fix docs, fix internal bin/ script generation
+
+
+1.1.0 (2009-11-05)
+==================
+
+- introduce automatic plugin registration via 'pytest11'
+ entrypoints via setuptools' pkg_resources.iter_entry_points
+
+- fix py.test dist-testing to work with execnet >= 1.0.0b4
+
+- re-introduce py.test.cmdline.main() for better backward compatibility
+
+- svn paths: fix a bug with path.check(versioned=True) for svn paths,
+ allow '%' in svn paths, make svnwc.update() default to interactive mode
+ like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
+
+- refine distributed tarball to contain test and no pyc files
+
+- try harder to have deprecation warnings for py.compat.* accesses
+ report a correct location
+
+1.0.3
+=====
+
+* adjust and improve docs
+
+* remove py.rest tool and internal namespace - it was
+ never really advertised and can still be used with
+ the old release if needed. If there is interest
+ it could be revived into its own tool i guess.
+
+* fix issue48 and issue59: raise an Error if the module
+ from an imported test file does not seem to come from
+ the filepath - avoids "same-name" confusion that has
+ been reported repeatedly
+
+* merged Ronny's nose-compatibility hacks: now
+ nose-style setup_module() and setup() functions are
+ supported
+
+* introduce generalized py.test.mark function marking
+
+* reshuffle / refine command line grouping
+
+* deprecate parser.addgroup in favour of getgroup which creates option group
+
+* add --report command line option that allows to control showing of skipped/xfailed sections
+
+* generalized skipping: a new way to mark python functions with skipif or xfail
+ at function, class and modules level based on platform or sys-module attributes.
+
+* extend py.test.mark decorator to allow for positional args
+
+* introduce and test "py.cleanup -d" to remove empty directories
+
+* fix issue #59 - robustify unittest test collection
+
+* make bpython/help interaction work by adding an __all__ attribute
+ to ApiModule, cleanup initpkg
+
+* use MIT license for pylib, add some contributors
+
+* remove py.execnet code and substitute all usages with 'execnet' proper
+
+* fix issue50 - cached_setup now caches more to expectations
+ for test functions with multiple arguments.
+
+* merge Jarko's fixes, issue #45 and #46
+
+* add the ability to specify a path for py.lookup to search in
+
+* fix a funcarg cached_setup bug probably only occurring
+ in distributed testing and "module" scope with teardown.
+
+* many fixes and changes for making the code base python3 compatible,
+ many thanks to Benjamin Peterson for helping with this.
+
+* consolidate builtins implementation to be compatible with >=2.3,
+ add helpers to ease keeping 2 and 3k compatible code
+
+* deprecate py.compat.doctest|subprocess|textwrap|optparse
+
+* deprecate py.magic.autopath, remove py/magic directory
+
+* move pytest assertion handling to py/code and a pytest_assertion
+ plugin, add "--no-assert" option, deprecate py.magic namespaces
+ in favour of (less) py.code ones.
+
+* consolidate and cleanup py/code classes and files
+
+* cleanup py/misc, move tests to bin-for-dist
+
+* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
+
+* consolidate py.log implementation, remove old approach.
+
+* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
+ text/unicode and byte-streams (uses underlying standard lib io.*
+ if available)
+
+* make py.unittest_convert helper script available which converts "unittest.py"
+ style files into the simpler assert/direct-test-classes py.test/nosetests
+ style. The script was written by Laura Creighton.
+
+* simplified internal localpath implementation
+
+1.0.2 (2009-08-27)
+==================
+
+* fixing packaging issues, triggered by fedora redhat packaging,
+ also added doc, examples and contrib dirs to the tarball.
+
+* added a documentation link to the new django plugin.
+
+1.0.1 (2009-08-19)
+==================
+
+* added a 'pytest_nose' plugin which handles nose.SkipTest,
+ nose-style function/method/generator setup/teardown and
+ tries to report functions correctly.
+
+* capturing of unicode writes or encoded strings to sys.stdout/err
+ work better, also terminalwriting was adapted and somewhat
+ unified between windows and linux.
+
+* improved documentation layout and content a lot
+
+* added a "--help-config" option to show conftest.py / ENV-var names for
+ all longopt cmdline options, and some special conftest.py variables.
+ renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
+
+* fix issue #27: better reporting on non-collectable items given on commandline
+ (e.g. pyc files)
+
+* fix issue #33: added --version flag (thanks Benjamin Peterson)
+
+* fix issue #32: adding support for "incomplete" paths to wcpath.status()
+
+* "Test" prefixed classes are *not* collected by default anymore if they
+ have an __init__ method
+
+* monkeypatch setenv() now accepts a "prepend" parameter
+
+* improved reporting of collection error tracebacks
+
+* simplified multicall mechanism and plugin architecture,
+ renamed some internal methods and argnames
+
+1.0.0 (2009-08-04)
+==================
+
+* more terse reporting try to show filesystem path relatively to current dir
+* improve xfail output a bit
+
+1.0.0b9 (2009-07-31)
+====================
+
+* cleanly handle and report final teardown of test setup
+
+* fix svn-1.6 compat issue with py.path.svnwc().versioned()
+ (thanks Wouter Vanden Hove)
+
+* setup/teardown or collection problems now show as ERRORs
+ or with big "E"'s in the progress lines. they are reported
+ and counted separately.
+
+* dist-testing: properly handle test items that get locally
+ collected but cannot be collected on the remote side - often
+ due to platform/dependency reasons
+
+* simplified py.test.mark API - see keyword plugin documentation
+
+* integrate better with logging: capturing now by default captures
+ test functions and their immediate setup/teardown in a single stream
+
+* capsys and capfd funcargs now have a readouterr() and a close() method
+ (underlyingly py.io.StdCapture/FD objects are used which grew a
+ readouterr() method as well to return snapshots of captured out/err)
+
+* make assert-reinterpretation work better with comparisons not
+ returning bools (reported with numpy from thanks maciej fijalkowski)
+
+* reworked per-test output capturing into the pytest_iocapture.py plugin
+ and thus removed capturing code from config object
+
+* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
+
+
+1.0.0b8 (2009-07-22)
+====================
+
+* pytest_unittest-plugin is now enabled by default
+
+* introduced pytest_keyboardinterrupt hook and
+ refined pytest_sessionfinish hooked, added tests.
+
+* workaround a buggy logging module interaction ("closing already closed
+ files"). Thanks to Sridhar Ratnakumar for triggering.
+
+* if plugins use "py.test.importorskip" for importing
+ a dependency only a warning will be issued instead
+ of exiting the testing process.
+
+* many improvements to docs:
+ - refined funcargs doc , use the term "factory" instead of "provider"
+ - added a new talk/tutorial doc page
+ - better download page
+ - better plugin docstrings
+ - added new plugins page and automatic doc generation script
+
+* fixed teardown problem related to partially failing funcarg setups
+ (thanks MrTopf for reporting), "pytest_runtest_teardown" is now
+ always invoked even if the "pytest_runtest_setup" failed.
+
+* tweaked doctest output for docstrings in py modules,
+ thanks Radomir.
+
+1.0.0b7
+=======
+
+* renamed py.test.xfail back to py.test.mark.xfail to avoid
+ two ways to decorate for xfail
+
+* re-added py.test.mark decorator for setting keywords on functions
+ (it was actually documented so removing it was not nice)
+
+* remove scope-argument from request.addfinalizer() because
+ request.cached_setup has the scope arg. TOOWTDI.
+
+* perform setup finalization before reporting failures
+
+* apply modified patches from Andreas Kloeckner to allow
+ test functions to have no func_code (#22) and to make
+ "-k" and function keywords work (#20)
+
+* apply patch from Daniel Peolzleithner (issue #23)
+
+* resolve issue #18, multiprocessing.Manager() and
+ redirection clash
+
+* make __name__ == "__channelexec__" for remote_exec code
+
+1.0.0b3 (2009-06-19)
+====================
+
+* plugin classes are removed: one now defines
+ hooks directly in conftest.py or global pytest_*.py
+ files.
+
+* added new pytest_namespace(config) hook that allows
+ to inject helpers directly to the py.test.* namespace.
+
+* documented and refined many hooks
+
+* added new style of generative tests via
+ pytest_generate_tests hook that integrates
+ well with function arguments.
+
+
+1.0.0b1
+=======
+
+* introduced new "funcarg" setup method,
+ see doc/test/funcarg.txt
+
+* introduced plugin architecture and many
+ new py.test plugins, see
+ doc/test/plugins.txt
+
+* teardown_method is now guaranteed to get
+ called after a test method has run.
+
+* new method: py.test.importorskip(mod,minversion)
+ will either import or call py.test.skip()
+
+* completely revised internal py.test architecture
+
+* new py.process.ForkedFunc object allowing to
+ fork execution of a function to a sub process
+ and getting a result back.
+
+XXX lots of things missing here XXX
+
+0.9.2
+=====
+
+* refined installation and metadata, created new setup.py,
+ now based on setuptools/ez_setup (thanks to Ralf Schmitt
+ for his support).
+
+* improved the way of making py.* scripts available in
+ windows environments, they are now added to the
+ Scripts directory as ".cmd" files.
+
+* py.path.svnwc.status() now is more complete and
+ uses xml output from the 'svn' command if available
+ (Guido Wesdorp)
+
+* fix for py.path.svn* to work with svn 1.5
+ (Chris Lamb)
+
+* fix path.relto(otherpath) method on windows to
+ use normcase for checking if a path is relative.
+
+* py.test's traceback is better parseable from editors
+ (follows the filenames:LINENO: MSG convention)
+ (thanks to Osmo Salomaa)
+
+* fix to javascript-generation, "py.test --runbrowser"
+ should work more reliably now
+
+* removed previously accidentally added
+ py.test.broken and py.test.notimplemented helpers.
+
+* there now is a py.__version__ attribute
+
+0.9.1
+=====
+
+This is a fairly complete list of v0.9.1, which can
+serve as a reference for developers.
+
+* allowing + signs in py.path.svn urls [39106]
+* fixed support for Failed exceptions without excinfo in py.test [39340]
+* added support for killing processes for Windows (as well as platforms that
+ support os.kill) in py.misc.killproc [39655]
+* added setup/teardown for generative tests to py.test [40702]
+* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739]
+* fixed problem with calling .remove() on wcpaths of non-versioned files in
+ py.path [44248]
+* fixed some import and inheritance issues in py.test [41480, 44648, 44655]
+* fail to run greenlet tests when pypy is available, but without stackless
+ [45294]
+* small fixes in rsession tests [45295]
+* fixed issue with 2.5 type representations in py.test [45483, 45484]
+* made that internal reporting issues displaying is done atomically in py.test
+ [45518]
+* made that non-existing files are ignored by the py.lookup script [45519]
+* improved exception name creation in py.test [45535]
+* made that less threads are used in execnet [merge in 45539]
+* removed lock required for atomic reporting issue displaying in py.test
+ [45545]
+* removed globals from execnet [45541, 45547]
+* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit
+ get called in 2.5 (py.execnet) [45548]
+* fixed bug in joining threads in py.execnet's servemain [45549]
+* refactored py.test.rsession tests to not rely on exact output format anymore
+ [45646]
+* using repr() on test outcome [45647]
+* added 'Reason' classes for py.test.skip() [45648, 45649]
+* killed some unnecessary sanity check in py.test.collect [45655]
+* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only
+ usable by Administrators [45901]
+* added support for locking and non-recursive commits to py.path.svnwc [45994]
+* locking files in py.execnet to prevent CPython from segfaulting [46010]
+* added export() method to py.path.svnurl
+* fixed -d -x in py.test [47277]
+* fixed argument concatenation problem in py.path.svnwc [49423]
+* restore py.test behaviour that it exits with code 1 when there are failures
+ [49974]
+* don't fail on html files that don't have an accompanying .txt file [50606]
+* fixed 'utestconvert.py < input' [50645]
+* small fix for code indentation in py.code.source [50755]
+* fix _docgen.py documentation building [51285]
+* improved checks for source representation of code blocks in py.test [51292]
+* added support for passing authentication to py.path.svn* objects [52000,
+ 52001]
+* removed sorted() call for py.apigen tests in favour of [].sort() to support
+ Python 2.3 [52481]
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/CONTRIBUTING.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/CONTRIBUTING.rst
new file mode 100644
index 00000000000..d85a894b910
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/CONTRIBUTING.rst
@@ -0,0 +1,278 @@
+============================
+Contribution getting started
+============================
+
+Contributions are highly welcomed and appreciated. Every little help counts,
+so do not hesitate!
+
+.. contents:: Contribution links
+ :depth: 2
+
+
+.. _submitfeedback:
+
+Feature requests and feedback
+-----------------------------
+
+Do you like pytest? Share some love on Twitter or in your blog posts!
+
+We'd also like to hear about your propositions and suggestions. Feel free to
+`submit them as issues <https://github.com/pytest-dev/pytest/issues>`_ and:
+
+* Explain in detail how they should work.
+* Keep the scope as narrow as possible. This will make it easier to implement.
+
+
+.. _reportbugs:
+
+Report bugs
+-----------
+
+Report bugs for pytest in the `issue tracker <https://github.com/pytest-dev/pytest/issues>`_.
+
+If you are reporting a bug, please include:
+
+* Your operating system name and version.
+* Any details about your local setup that might be helpful in troubleshooting,
+ specifically the Python interpreter version, installed libraries, and pytest
+ version.
+* Detailed steps to reproduce the bug.
+
+If you can write a demonstration test that currently fails but should pass
+(xfail), that is a very useful commit to make as well, even if you cannot
+fix the bug itself.
+
+
+.. _fixbugs:
+
+Fix bugs
+--------
+
+Look through the GitHub issues for bugs. Here is a filter you can use:
+https://github.com/pytest-dev/pytest/labels/type%3A%20bug
+
+:ref:`Talk <contact>` to developers to find out how you can fix specific bugs.
+
+Don't forget to check the issue trackers of your favourite plugins, too!
+
+.. _writeplugins:
+
+Implement features
+------------------
+
+Look through the GitHub issues for enhancements. Here is a filter you can use:
+https://github.com/pytest-dev/pytest/labels/enhancement
+
+:ref:`Talk <contact>` to developers to find out how you can implement specific
+features.
+
+Write documentation
+-------------------
+
+Pytest could always use more documentation. What exactly is needed?
+
+* More complementary documentation. Have you perhaps found something unclear?
+* Documentation translations. We currently have only English.
+* Docstrings. There can never be too many of them.
+* Blog posts, articles and such -- they're all very appreciated.
+
+You can also edit documentation files directly in the GitHub web interface,
+without using a local copy. This can be convenient for small fixes.
+
+.. note::
+ Build the documentation locally with the following command:
+
+ .. code:: bash
+
+ $ tox -e docs
+
+ The built documentation should be available in the ``doc/en/_build/``.
+
+ Where 'en' refers to the documentation language.
+
+.. _submitplugin:
+
+Submitting Plugins to pytest-dev
+--------------------------------
+
+Pytest development of the core, some plugins and support code happens
+in repositories living under the ``pytest-dev`` organisations:
+
+- `pytest-dev on GitHub <https://github.com/pytest-dev>`_
+
+- `pytest-dev on Bitbucket <https://bitbucket.org/pytest-dev>`_
+
+All pytest-dev Contributors team members have write access to all contained
+repositories. Pytest core and plugins are generally developed
+using `pull requests`_ to respective repositories.
+
+The objectives of the ``pytest-dev`` organisation are:
+
+* Having a central location for popular pytest plugins
+* Sharing some of the maintenance responsibility (in case a maintainer no
+ longer wishes to maintain a plugin)
+
+You can submit your plugin by subscribing to the `pytest-dev mail list
+<https://mail.python.org/mailman/listinfo/pytest-dev>`_ and writing a
+mail pointing to your existing pytest plugin repository which must have
+the following:
+
+- PyPI presence with a ``setup.py`` that contains a license, ``pytest-``
+ prefixed name, version number, authors, short and long description.
+
+- a ``tox.ini`` for running tests using `tox <https://tox.readthedocs.io>`_.
+
+- a ``README.txt`` describing how to use the plugin and on which
+ platforms it runs.
+
+- a ``LICENSE.txt`` file or equivalent containing the licensing
+ information, with matching info in ``setup.py``.
+
+- an issue tracker for bug reports and enhancement requests.
+
+- a `changelog <http://keepachangelog.com/>`_
+
+If no contributor strongly objects and two agree, the repository can then be
+transferred to the ``pytest-dev`` organisation.
+
+Here's a rundown of how a repository transfer usually proceeds
+(using a repository named ``joedoe/pytest-xyz`` as example):
+
+* ``joedoe`` transfers repository ownership to ``pytest-dev`` administrator ``calvin``.
+* ``calvin`` creates ``pytest-xyz-admin`` and ``pytest-xyz-developers`` teams, inviting ``joedoe`` to both as **maintainer**.
+* ``calvin`` transfers repository to ``pytest-dev`` and configures team access:
+
+ - ``pytest-xyz-admin`` **admin** access;
+ - ``pytest-xyz-developers`` **write** access;
+
+The ``pytest-dev/Contributors`` team has write access to all projects, and
+every project administrator is in it. We recommend that each plugin has at least three
+people who have the right to release to PyPI.
+
+Repository owners can rest assured that no ``pytest-dev`` administrator will ever make
+releases of your repository or take ownership in any way, except in rare cases
+where someone becomes unresponsive after months of contact attempts.
+As stated, the objective is to share maintenance and avoid "plugin-abandon".
+
+
+.. _`pull requests`:
+.. _pull-requests:
+
+Preparing Pull Requests
+-----------------------
+
+Short version
+~~~~~~~~~~~~~
+
+#. Fork the repository;
+#. Target ``master`` for bugfixes and doc changes;
+#. Target ``features`` for new features or functionality changes.
+#. Follow **PEP-8**. There's a ``tox`` command to help fixing it: ``tox -e fix-lint``.
+#. Tests are run using ``tox``::
+
+ tox -e linting,py27,py36
+
+ The test environments above are usually enough to cover most cases locally.
+
+#. Write a ``changelog`` entry: ``changelog/2574.bugfix``, use issue id number
+ and one of ``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or
+ ``trivial`` for the issue type.
+#. Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please
+ add yourself to the ``AUTHORS`` file, in alphabetical order;
+
+
+Long version
+~~~~~~~~~~~~
+
+What is a "pull request"? It informs the project's core developers about the
+changes you want to review and merge. Pull requests are stored on
+`GitHub servers <https://github.com/pytest-dev/pytest/pulls>`_.
+Once you send a pull request, we can discuss its potential modifications and
+even add more commits to it later on. There's an excellent tutorial on how Pull
+Requests work in the
+`GitHub Help Center <https://help.github.com/articles/using-pull-requests/>`_.
+
+Here is a simple overview, with pytest-specific bits:
+
+#. Fork the
+ `pytest GitHub repository <https://github.com/pytest-dev/pytest>`__. It's
+ fine to use ``pytest`` as your fork repository name because it will live
+ under your user.
+
+#. Clone your fork locally using `git <https://git-scm.com/>`_ and create a branch::
+
+ $ git clone git@github.com:YOUR_GITHUB_USERNAME/pytest.git
+ $ cd pytest
+ # now, to fix a bug create your own branch off "master":
+
+ $ git checkout -b your-bugfix-branch-name master
+
+ # or to instead add a feature create your own branch off "features":
+
+ $ git checkout -b your-feature-branch-name features
+
+ Given we have "major.minor.micro" version numbers, bugfixes will usually
+ be released in micro releases whereas features will be released in
+ minor releases and incompatible changes in major releases.
+
+ If you need some help with Git, follow this quick start
+ guide: https://git.wiki.kernel.org/index.php/QuickStart
+
+#. Install tox
+
+ Tox is used to run all the tests and will automatically setup virtualenvs
+ to run the tests in.
+ (will implicitly use http://www.virtualenv.org/en/latest/)::
+
+ $ pip install tox
+
+#. Run all the tests
+
+ You need to have Python 2.7 and 3.6 available in your system. Now
+ running tests is as simple as issuing this command::
+
+ $ tox -e linting,py27,py36
+
+ This command will run tests via the "tox" tool against Python 2.7 and 3.6
+ and also perform "lint" coding-style checks.
+
+#. You can now edit your local working copy. Please follow PEP-8.
+
+ You can now make the changes you want and run the tests again as necessary.
+
+ If you have too much linting errors, try running::
+
+ $ tox -e fix-lint
+
+ To fix pep8 related errors.
+
+ You can pass different options to ``tox``. For example, to run tests on Python 2.7 and pass options to pytest
+ (e.g. enter pdb on failure) to pytest you can do::
+
+ $ tox -e py27 -- --pdb
+
+ Or to only run tests in a particular test module on Python 3.6::
+
+ $ tox -e py36 -- testing/test_config.py
+
+#. Commit and push once your tests pass and you are happy with your change(s)::
+
+ $ git commit -a -m "<commit message>"
+ $ git push -u
+
+#. Create a new changelog entry in ``changelog``. The file should be named ``<issueid>.<type>``,
+ where *issueid* is the number of the issue related to the change and *type* is one of
+ ``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or ``trivial``.
+
+#. Add yourself to ``AUTHORS`` file if not there yet, in alphabetical order.
+
+#. Finally, submit a pull request through the GitHub website using this data::
+
+ head-fork: YOUR_GITHUB_USERNAME/pytest
+ compare: your-branch-name
+
+ base-fork: pytest-dev/pytest
+ base: master # if it's a bugfix
+ base: features # if it's a feature
+
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/HOWTORELEASE.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/HOWTORELEASE.rst
new file mode 100644
index 00000000000..48a3461d4bc
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/HOWTORELEASE.rst
@@ -0,0 +1,65 @@
+Release Procedure
+-----------------
+
+Our current policy for releasing is to aim for a bugfix every few weeks and a minor release every 2-3 months. The idea
+is to get fixes and new features out instead of trying to cram a ton of features into a release and by consequence
+taking a lot of time to make a new one.
+
+.. important::
+
+ pytest releases must be prepared on **Linux** because the docs and examples expect
+ to be executed in that platform.
+
+#. Install development dependencies in a virtual environment with::
+
+ pip3 install -r tasks/requirements.txt
+
+#. Create a branch ``release-X.Y.Z`` with the version for the release.
+
+ * **patch releases**: from the latest ``master``;
+
+ * **minor releases**: from the latest ``features``; then merge with the latest ``master``;
+
+ Ensure your are in a clean work tree.
+
+#. Generate docs, changelog, announcements and upload a package to
+ your ``devpi`` staging server::
+
+ invoke generate.pre-release <VERSION> <DEVPI USER> --password <DEVPI PASSWORD>
+
+ If ``--password`` is not given, it is assumed the user is already logged in ``devpi``.
+ If you don't have an account, please ask for one.
+
+#. Open a PR for this branch targeting ``master``.
+
+#. Test the package
+
+ * **Manual method**
+
+ Run from multiple machines::
+
+ devpi use https://devpi.net/USER/dev
+ devpi test pytest==VERSION
+
+ Check that tests pass for relevant combinations with::
+
+ devpi list pytest
+
+ * **CI servers**
+
+ Configure a repository as per-instructions on
+ devpi-cloud-test_ to test the package on Travis_ and AppVeyor_.
+ All test environments should pass.
+
+#. Publish to PyPI::
+
+ invoke generate.publish-release <VERSION> <DEVPI USER> <PYPI_NAME>
+
+ where PYPI_NAME is the name of pypi.python.org as configured in your ``~/.pypirc``
+ file `for devpi <http://doc.devpi.net/latest/quickstart-releaseprocess.html?highlight=pypirc#devpi-push-releasing-to-an-external-index>`_.
+
+#. After a minor/major release, merge ``release-X.Y.Z`` into ``master`` and push (or open a PR).
+
+.. _devpi-cloud-test: https://github.com/obestwalter/devpi-cloud-test
+.. _AppVeyor: https://www.appveyor.com/
+.. _Travis: https://travis-ci.org
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/LICENSE b/tests/wpt/web-platform-tests/tools/third_party/pytest/LICENSE
new file mode 100644
index 00000000000..629df45ac40
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2004-2017 Holger Krekel and others
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/README.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/README.rst
new file mode 100644
index 00000000000..3630dd4c62a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/README.rst
@@ -0,0 +1,109 @@
+.. image:: http://docs.pytest.org/en/latest/_static/pytest1.png
+ :target: http://docs.pytest.org
+ :align: center
+ :alt: pytest
+
+------
+
+.. image:: https://img.shields.io/pypi/v/pytest.svg
+ :target: https://pypi.python.org/pypi/pytest
+
+.. image:: https://anaconda.org/conda-forge/pytest/badges/version.svg
+ :target: https://anaconda.org/conda-forge/pytest
+
+.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
+ :target: https://pypi.python.org/pypi/pytest
+
+.. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg
+ :target: https://coveralls.io/r/pytest-dev/pytest
+
+.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master
+ :target: https://travis-ci.org/pytest-dev/pytest
+
+.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
+ :target: https://ci.appveyor.com/project/pytestbot/pytest
+
+The ``pytest`` framework makes it easy to write small tests, yet
+scales to support complex functional testing for applications and libraries.
+
+An example of a simple test:
+
+.. code-block:: python
+
+ # content of test_sample.py
+ def inc(x):
+ return x + 1
+
+ def test_answer():
+ assert inc(3) == 5
+
+
+To execute it::
+
+ $ pytest
+ ============================= test session starts =============================
+ collected 1 items
+
+ test_sample.py F
+
+ ================================== FAILURES ===================================
+ _________________________________ test_answer _________________________________
+
+ def test_answer():
+ > assert inc(3) == 5
+ E assert 4 == 5
+ E + where 4 = inc(3)
+
+ test_sample.py:5: AssertionError
+ ========================== 1 failed in 0.04 seconds ===========================
+
+
+Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples.
+
+
+Features
+--------
+
+- Detailed info on failing `assert statements <http://docs.pytest.org/en/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
+
+- `Auto-discovery
+ <http://docs.pytest.org/en/latest/goodpractices.html#python-test-discovery>`_
+ of test modules and functions;
+
+- `Modular fixtures <http://docs.pytest.org/en/latest/fixture.html>`_ for
+ managing small or parametrized long-lived test resources;
+
+- Can run `unittest <http://docs.pytest.org/en/latest/unittest.html>`_ (or trial),
+ `nose <http://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box;
+
+- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested);
+
+- Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community;
+
+
+Documentation
+-------------
+
+For full documentation, including installation, tutorials and PDF documents, please see http://docs.pytest.org.
+
+
+Bugs/Requests
+-------------
+
+Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features.
+
+
+Changelog
+---------
+
+Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`__ page for fixes and enhancements of each version.
+
+
+License
+-------
+
+Copyright Holger Krekel and others, 2004-2017.
+
+Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
+
+.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/__init__.py
new file mode 100644
index 00000000000..6e41f0504e4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/__init__.py
@@ -0,0 +1,8 @@
+__all__ = ['__version__']
+
+try:
+ from ._version import version as __version__
+except ImportError:
+ # broken installation, we don't even try
+ # unknown only works because we do poor mans version compare
+ __version__ = 'unknown'
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_argcomplete.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_argcomplete.py
new file mode 100644
index 00000000000..0625a75f9f1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_argcomplete.py
@@ -0,0 +1,103 @@
+
+"""allow bash-completion for argparse with argcomplete if installed
+needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail
+to find the magic string, so _ARGCOMPLETE env. var is never set, and
+this does not need special code.
+
+Function try_argcomplete(parser) should be called directly before
+the call to ArgumentParser.parse_args().
+
+The filescompleter is what you normally would use on the positional
+arguments specification, in order to get "dirname/" after "dirn<TAB>"
+instead of the default "dirname ":
+
+ optparser.add_argument(Config._file_or_dir, nargs='*'
+ ).completer=filescompleter
+
+Other, application specific, completers should go in the file
+doing the add_argument calls as they need to be specified as .completer
+attributes as well. (If argcomplete is not installed, the function the
+attribute points to will not be used).
+
+SPEEDUP
+=======
+The generic argcomplete script for bash-completion
+(/etc/bash_completion.d/python-argcomplete.sh )
+uses a python program to determine startup script generated by pip.
+You can speed up completion somewhat by changing this script to include
+ # PYTHON_ARGCOMPLETE_OK
+so the the python-argcomplete-check-easy-install-script does not
+need to be called to find the entry point of the code and see if that is
+marked with PYTHON_ARGCOMPLETE_OK
+
+INSTALL/DEBUGGING
+=================
+To include this support in another application that has setup.py generated
+scripts:
+- add the line:
+ # PYTHON_ARGCOMPLETE_OK
+ near the top of the main python entry point
+- include in the file calling parse_args():
+ from _argcomplete import try_argcomplete, filescompleter
+ , call try_argcomplete just before parse_args(), and optionally add
+ filescompleter to the positional arguments' add_argument()
+If things do not work right away:
+- switch on argcomplete debugging with (also helpful when doing custom
+ completers):
+ export _ARC_DEBUG=1
+- run:
+ python-argcomplete-check-easy-install-script $(which appname)
+ echo $?
+ will echo 0 if the magic line has been found, 1 if not
+- sometimes it helps to find early on errors using:
+ _ARGCOMPLETE=1 _ARC_DEBUG=1 appname
+ which should throw a KeyError: 'COMPLINE' (which is properly set by the
+ global argcomplete script).
+"""
+from __future__ import absolute_import, division, print_function
+import sys
+import os
+from glob import glob
+
+
+class FastFilesCompleter:
+ 'Fast file completer class'
+
+ def __init__(self, directories=True):
+ self.directories = directories
+
+ def __call__(self, prefix, **kwargs):
+ """only called on non option completions"""
+ if os.path.sep in prefix[1:]:
+ prefix_dir = len(os.path.dirname(prefix) + os.path.sep)
+ else:
+ prefix_dir = 0
+ completion = []
+ globbed = []
+ if '*' not in prefix and '?' not in prefix:
+ # we are on unix, otherwise no bash
+ if not prefix or prefix[-1] == os.path.sep:
+ globbed.extend(glob(prefix + '.*'))
+ prefix += '*'
+ globbed.extend(glob(prefix))
+ for x in sorted(globbed):
+ if os.path.isdir(x):
+ x += '/'
+ # append stripping the prefix (like bash, not like compgen)
+ completion.append(x[prefix_dir:])
+ return completion
+
+
+if os.environ.get('_ARGCOMPLETE'):
+ try:
+ import argcomplete.completers
+ except ImportError:
+ sys.exit(-1)
+ filescompleter = FastFilesCompleter()
+
+ def try_argcomplete(parser):
+ argcomplete.autocomplete(parser, always_complete_options=False)
+else:
+ def try_argcomplete(parser):
+ pass
+ filescompleter = None
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/__init__.py
new file mode 100644
index 00000000000..815c13b42c2
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/__init__.py
@@ -0,0 +1,10 @@
+""" python inspection/code generation API """
+from __future__ import absolute_import, division, print_function
+from .code import Code # noqa
+from .code import ExceptionInfo # noqa
+from .code import Frame # noqa
+from .code import Traceback # noqa
+from .code import getrawcode # noqa
+from .source import Source # noqa
+from .source import compile_ as compile # noqa
+from .source import getfslineno # noqa
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/_py2traceback.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/_py2traceback.py
new file mode 100644
index 00000000000..5aacf0a428d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/_py2traceback.py
@@ -0,0 +1,85 @@
+# copied from python-2.7.3's traceback.py
+# CHANGES:
+# - some_str is replaced, trying to create unicode strings
+#
+from __future__ import absolute_import, division, print_function
+import types
+
+
+def format_exception_only(etype, value):
+ """Format the exception part of a traceback.
+
+ The arguments are the exception type and value such as given by
+ sys.last_type and sys.last_value. The return value is a list of
+ strings, each ending in a newline.
+
+ Normally, the list contains a single string; however, for
+ SyntaxError exceptions, it contains several lines that (when
+ printed) display detailed information about where the syntax
+ error occurred.
+
+ The message indicating which exception occurred is always the last
+ string in the list.
+
+ """
+
+ # An instance should not have a meaningful value parameter, but
+ # sometimes does, particularly for string exceptions, such as
+ # >>> raise string1, string2 # deprecated
+ #
+ # Clear these out first because issubtype(string1, SyntaxError)
+ # would throw another exception and mask the original problem.
+ if (isinstance(etype, BaseException) or
+ isinstance(etype, types.InstanceType) or
+ etype is None or type(etype) is str):
+ return [_format_final_exc_line(etype, value)]
+
+ stype = etype.__name__
+
+ if not issubclass(etype, SyntaxError):
+ return [_format_final_exc_line(stype, value)]
+
+ # It was a syntax error; show exactly where the problem was found.
+ lines = []
+ try:
+ msg, (filename, lineno, offset, badline) = value.args
+ except Exception:
+ pass
+ else:
+ filename = filename or "<string>"
+ lines.append(' File "%s", line %d\n' % (filename, lineno))
+ if badline is not None:
+ if isinstance(badline, bytes): # python 2 only
+ badline = badline.decode('utf-8', 'replace')
+ lines.append(u' %s\n' % badline.strip())
+ if offset is not None:
+ caretspace = badline.rstrip('\n')[:offset].lstrip()
+ # non-space whitespace (likes tabs) must be kept for alignment
+ caretspace = ((c.isspace() and c or ' ') for c in caretspace)
+ # only three spaces to account for offset1 == pos 0
+ lines.append(' %s^\n' % ''.join(caretspace))
+ value = msg
+
+ lines.append(_format_final_exc_line(stype, value))
+ return lines
+
+
+def _format_final_exc_line(etype, value):
+ """Return a list of a single line -- normal case for format_exception_only"""
+ valuestr = _some_str(value)
+ if value is None or not valuestr:
+ line = "%s\n" % etype
+ else:
+ line = "%s: %s\n" % (etype, valuestr)
+ return line
+
+
+def _some_str(value):
+ try:
+ return unicode(value)
+ except Exception:
+ try:
+ return str(value)
+ except Exception:
+ pass
+ return '<unprintable %s object>' % type(value).__name__
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/code.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/code.py
new file mode 100644
index 00000000000..3fb232bd430
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/code.py
@@ -0,0 +1,906 @@
+from __future__ import absolute_import, division, print_function
+import sys
+from inspect import CO_VARARGS, CO_VARKEYWORDS
+import re
+from weakref import ref
+from _pytest.compat import _PY2, _PY3, PY35, safe_str
+
+import py
+builtin_repr = repr
+
+if _PY3:
+ from traceback import format_exception_only
+else:
+ from ._py2traceback import format_exception_only
+
+
+class Code(object):
+ """ wrapper around Python code objects """
+
+ def __init__(self, rawcode):
+ if not hasattr(rawcode, "co_filename"):
+ rawcode = getrawcode(rawcode)
+ try:
+ self.filename = rawcode.co_filename
+ self.firstlineno = rawcode.co_firstlineno - 1
+ self.name = rawcode.co_name
+ except AttributeError:
+ raise TypeError("not a code object: %r" % (rawcode,))
+ self.raw = rawcode
+
+ def __eq__(self, other):
+ return self.raw == other.raw
+
+ __hash__ = None
+
+ def __ne__(self, other):
+ return not self == other
+
+ @property
+ def path(self):
+ """ return a path object pointing to source code (note that it
+ might not point to an actually existing file). """
+ try:
+ p = py.path.local(self.raw.co_filename)
+ # maybe don't try this checking
+ if not p.check():
+ raise OSError("py.path check failed.")
+ except OSError:
+ # XXX maybe try harder like the weird logic
+ # in the standard lib [linecache.updatecache] does?
+ p = self.raw.co_filename
+
+ return p
+
+ @property
+ def fullsource(self):
+ """ return a _pytest._code.Source object for the full source file of the code
+ """
+ from _pytest._code import source
+ full, _ = source.findsource(self.raw)
+ return full
+
+ def source(self):
+ """ return a _pytest._code.Source object for the code object's source only
+ """
+ # return source only for that part of code
+ import _pytest._code
+ return _pytest._code.Source(self.raw)
+
+ def getargs(self, var=False):
+ """ return a tuple with the argument names for the code object
+
+ if 'var' is set True also return the names of the variable and
+ keyword arguments when present
+ """
+ # handfull shortcut for getting args
+ raw = self.raw
+ argcount = raw.co_argcount
+ if var:
+ argcount += raw.co_flags & CO_VARARGS
+ argcount += raw.co_flags & CO_VARKEYWORDS
+ return raw.co_varnames[:argcount]
+
+
+class Frame(object):
+ """Wrapper around a Python frame holding f_locals and f_globals
+ in which expressions can be evaluated."""
+
+ def __init__(self, frame):
+ self.lineno = frame.f_lineno - 1
+ self.f_globals = frame.f_globals
+ self.f_locals = frame.f_locals
+ self.raw = frame
+ self.code = Code(frame.f_code)
+
+ @property
+ def statement(self):
+ """ statement this frame is at """
+ import _pytest._code
+ if self.code.fullsource is None:
+ return _pytest._code.Source("")
+ return self.code.fullsource.getstatement(self.lineno)
+
+ def eval(self, code, **vars):
+ """ evaluate 'code' in the frame
+
+ 'vars' are optional additional local variables
+
+ returns the result of the evaluation
+ """
+ f_locals = self.f_locals.copy()
+ f_locals.update(vars)
+ return eval(code, self.f_globals, f_locals)
+
+ def exec_(self, code, **vars):
+ """ exec 'code' in the frame
+
+ 'vars' are optiona; additional local variables
+ """
+ f_locals = self.f_locals.copy()
+ f_locals.update(vars)
+ py.builtin.exec_(code, self.f_globals, f_locals)
+
+ def repr(self, object):
+ """ return a 'safe' (non-recursive, one-line) string repr for 'object'
+ """
+ return py.io.saferepr(object)
+
+ def is_true(self, object):
+ return object
+
+ def getargs(self, var=False):
+ """ return a list of tuples (name, value) for all arguments
+
+ if 'var' is set True also include the variable and keyword
+ arguments when present
+ """
+ retval = []
+ for arg in self.code.getargs(var):
+ try:
+ retval.append((arg, self.f_locals[arg]))
+ except KeyError:
+ pass # this can occur when using Psyco
+ return retval
+
+
+class TracebackEntry(object):
+ """ a single entry in a traceback """
+
+ _repr_style = None
+ exprinfo = None
+
+ def __init__(self, rawentry, excinfo=None):
+ self._excinfo = excinfo
+ self._rawentry = rawentry
+ self.lineno = rawentry.tb_lineno - 1
+
+ def set_repr_style(self, mode):
+ assert mode in ("short", "long")
+ self._repr_style = mode
+
+ @property
+ def frame(self):
+ import _pytest._code
+ return _pytest._code.Frame(self._rawentry.tb_frame)
+
+ @property
+ def relline(self):
+ return self.lineno - self.frame.code.firstlineno
+
+ def __repr__(self):
+ return "<TracebackEntry %s:%d>" % (self.frame.code.path, self.lineno + 1)
+
+ @property
+ def statement(self):
+ """ _pytest._code.Source object for the current statement """
+ source = self.frame.code.fullsource
+ return source.getstatement(self.lineno)
+
+ @property
+ def path(self):
+ """ path to the source code """
+ return self.frame.code.path
+
+ def getlocals(self):
+ return self.frame.f_locals
+ locals = property(getlocals, None, None, "locals of underlaying frame")
+
+ def getfirstlinesource(self):
+ # on Jython this firstlineno can be -1 apparently
+ return max(self.frame.code.firstlineno, 0)
+
+ def getsource(self, astcache=None):
+ """ return failing source code. """
+ # we use the passed in astcache to not reparse asttrees
+ # within exception info printing
+ from _pytest._code.source import getstatementrange_ast
+ source = self.frame.code.fullsource
+ if source is None:
+ return None
+ key = astnode = None
+ if astcache is not None:
+ key = self.frame.code.path
+ if key is not None:
+ astnode = astcache.get(key, None)
+ start = self.getfirstlinesource()
+ try:
+ astnode, _, end = getstatementrange_ast(self.lineno, source,
+ astnode=astnode)
+ except SyntaxError:
+ end = self.lineno + 1
+ else:
+ if key is not None:
+ astcache[key] = astnode
+ return source[start:end]
+
+ source = property(getsource)
+
+ def ishidden(self):
+ """ return True if the current frame has a var __tracebackhide__
+ resolving to True
+
+ If __tracebackhide__ is a callable, it gets called with the
+ ExceptionInfo instance and can decide whether to hide the traceback.
+
+ mostly for internal use
+ """
+ try:
+ tbh = self.frame.f_locals['__tracebackhide__']
+ except KeyError:
+ try:
+ tbh = self.frame.f_globals['__tracebackhide__']
+ except KeyError:
+ return False
+
+ if callable(tbh):
+ return tbh(None if self._excinfo is None else self._excinfo())
+ else:
+ return tbh
+
+ def __str__(self):
+ try:
+ fn = str(self.path)
+ except py.error.Error:
+ fn = '???'
+ name = self.frame.code.name
+ try:
+ line = str(self.statement).lstrip()
+ except KeyboardInterrupt:
+ raise
+ except: # noqa
+ line = "???"
+ return " File %r:%d in %s\n %s\n" % (fn, self.lineno + 1, name, line)
+
+ def name(self):
+ return self.frame.code.raw.co_name
+ name = property(name, None, None, "co_name of underlaying code")
+
+
+class Traceback(list):
+ """ Traceback objects encapsulate and offer higher level
+ access to Traceback entries.
+ """
+ Entry = TracebackEntry
+
+ def __init__(self, tb, excinfo=None):
+ """ initialize from given python traceback object and ExceptionInfo """
+ self._excinfo = excinfo
+ if hasattr(tb, 'tb_next'):
+ def f(cur):
+ while cur is not None:
+ yield self.Entry(cur, excinfo=excinfo)
+ cur = cur.tb_next
+ list.__init__(self, f(tb))
+ else:
+ list.__init__(self, tb)
+
+ def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None):
+ """ return a Traceback instance wrapping part of this Traceback
+
+ by provding any combination of path, lineno and firstlineno, the
+ first frame to start the to-be-returned traceback is determined
+
+ this allows cutting the first part of a Traceback instance e.g.
+ for formatting reasons (removing some uninteresting bits that deal
+ with handling of the exception/traceback)
+ """
+ for x in self:
+ code = x.frame.code
+ codepath = code.path
+ if ((path is None or codepath == path) and
+ (excludepath is None or not hasattr(codepath, 'relto') or
+ not codepath.relto(excludepath)) and
+ (lineno is None or x.lineno == lineno) and
+ (firstlineno is None or x.frame.code.firstlineno == firstlineno)):
+ return Traceback(x._rawentry, self._excinfo)
+ return self
+
+ def __getitem__(self, key):
+ val = super(Traceback, self).__getitem__(key)
+ if isinstance(key, type(slice(0))):
+ val = self.__class__(val)
+ return val
+
+ def filter(self, fn=lambda x: not x.ishidden()):
+ """ return a Traceback instance with certain items removed
+
+ fn is a function that gets a single argument, a TracebackEntry
+ instance, and should return True when the item should be added
+ to the Traceback, False when not
+
+ by default this removes all the TracebackEntries which are hidden
+ (see ishidden() above)
+ """
+ return Traceback(filter(fn, self), self._excinfo)
+
+ def getcrashentry(self):
+ """ return last non-hidden traceback entry that lead
+ to the exception of a traceback.
+ """
+ for i in range(-1, -len(self) - 1, -1):
+ entry = self[i]
+ if not entry.ishidden():
+ return entry
+ return self[-1]
+
+ def recursionindex(self):
+ """ return the index of the frame/TracebackEntry where recursion
+ originates if appropriate, None if no recursion occurred
+ """
+ cache = {}
+ for i, entry in enumerate(self):
+ # id for the code.raw is needed to work around
+ # the strange metaprogramming in the decorator lib from pypi
+ # which generates code objects that have hash/value equality
+ # XXX needs a test
+ key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
+ # print "checking for recursion at", key
+ values = cache.setdefault(key, [])
+ if values:
+ f = entry.frame
+ loc = f.f_locals
+ for otherloc in values:
+ if f.is_true(f.eval(co_equal,
+ __recursioncache_locals_1=loc,
+ __recursioncache_locals_2=otherloc)):
+ return i
+ values.append(entry.frame.f_locals)
+ return None
+
+
+co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2',
+ '?', 'eval')
+
+
+class ExceptionInfo(object):
+ """ wraps sys.exc_info() objects and offers
+ help for navigating the traceback.
+ """
+ _striptext = ''
+ _assert_start_repr = "AssertionError(u\'assert " if _PY2 else "AssertionError(\'assert "
+
+ def __init__(self, tup=None, exprinfo=None):
+ import _pytest._code
+ if tup is None:
+ tup = sys.exc_info()
+ if exprinfo is None and isinstance(tup[1], AssertionError):
+ exprinfo = getattr(tup[1], 'msg', None)
+ if exprinfo is None:
+ exprinfo = py.io.saferepr(tup[1])
+ if exprinfo and exprinfo.startswith(self._assert_start_repr):
+ self._striptext = 'AssertionError: '
+ self._excinfo = tup
+ #: the exception class
+ self.type = tup[0]
+ #: the exception instance
+ self.value = tup[1]
+ #: the exception raw traceback
+ self.tb = tup[2]
+ #: the exception type name
+ self.typename = self.type.__name__
+ #: the exception traceback (_pytest._code.Traceback instance)
+ self.traceback = _pytest._code.Traceback(self.tb, excinfo=ref(self))
+
+ def __repr__(self):
+ return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback))
+
+ def exconly(self, tryshort=False):
+ """ return the exception as a string
+
+ when 'tryshort' resolves to True, and the exception is a
+ _pytest._code._AssertionError, only the actual exception part of
+ the exception representation is returned (so 'AssertionError: ' is
+ removed from the beginning)
+ """
+ lines = format_exception_only(self.type, self.value)
+ text = ''.join(lines)
+ text = text.rstrip()
+ if tryshort:
+ if text.startswith(self._striptext):
+ text = text[len(self._striptext):]
+ return text
+
+ def errisinstance(self, exc):
+ """ return True if the exception is an instance of exc """
+ return isinstance(self.value, exc)
+
+ def _getreprcrash(self):
+ exconly = self.exconly(tryshort=True)
+ entry = self.traceback.getcrashentry()
+ path, lineno = entry.frame.code.raw.co_filename, entry.lineno
+ return ReprFileLocation(path, lineno + 1, exconly)
+
+ def getrepr(self, showlocals=False, style="long",
+ abspath=False, tbfilter=True, funcargs=False):
+ """ return str()able representation of this exception info.
+ showlocals: show locals per traceback entry
+ style: long|short|no|native traceback style
+ tbfilter: hide entries (where __tracebackhide__ is true)
+
+ in case of style==native, tbfilter and showlocals is ignored.
+ """
+ if style == 'native':
+ return ReprExceptionInfo(ReprTracebackNative(
+ py.std.traceback.format_exception(
+ self.type,
+ self.value,
+ self.traceback[0]._rawentry,
+ )), self._getreprcrash())
+
+ fmt = FormattedExcinfo(showlocals=showlocals, style=style,
+ abspath=abspath, tbfilter=tbfilter, funcargs=funcargs)
+ return fmt.repr_excinfo(self)
+
+ def __str__(self):
+ entry = self.traceback[-1]
+ loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
+ return str(loc)
+
+ def __unicode__(self):
+ entry = self.traceback[-1]
+ loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
+ return unicode(loc)
+
+ def match(self, regexp):
+ """
+ Match the regular expression 'regexp' on the string representation of
+ the exception. If it matches then True is returned (so that it is
+ possible to write 'assert excinfo.match()'). If it doesn't match an
+ AssertionError is raised.
+ """
+ __tracebackhide__ = True
+ if not re.search(regexp, str(self.value)):
+ assert 0, "Pattern '{0!s}' not found in '{1!s}'".format(
+ regexp, self.value)
+ return True
+
+
+class FormattedExcinfo(object):
+ """ presenting information about failing Functions and Generators. """
+ # for traceback entries
+ flow_marker = ">"
+ fail_marker = "E"
+
+ def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False):
+ self.showlocals = showlocals
+ self.style = style
+ self.tbfilter = tbfilter
+ self.funcargs = funcargs
+ self.abspath = abspath
+ self.astcache = {}
+
+ def _getindent(self, source):
+ # figure out indent for given source
+ try:
+ s = str(source.getstatement(len(source) - 1))
+ except KeyboardInterrupt:
+ raise
+ except: # noqa
+ try:
+ s = str(source[-1])
+ except KeyboardInterrupt:
+ raise
+ except: # noqa
+ return 0
+ return 4 + (len(s) - len(s.lstrip()))
+
+ def _getentrysource(self, entry):
+ source = entry.getsource(self.astcache)
+ if source is not None:
+ source = source.deindent()
+ return source
+
+ def _saferepr(self, obj):
+ return py.io.saferepr(obj)
+
+ def repr_args(self, entry):
+ if self.funcargs:
+ args = []
+ for argname, argvalue in entry.frame.getargs(var=True):
+ args.append((argname, self._saferepr(argvalue)))
+ return ReprFuncArgs(args)
+
+ def get_source(self, source, line_index=-1, excinfo=None, short=False):
+ """ return formatted and marked up source lines. """
+ import _pytest._code
+ lines = []
+ if source is None or line_index >= len(source.lines):
+ source = _pytest._code.Source("???")
+ line_index = 0
+ if line_index < 0:
+ line_index += len(source)
+ space_prefix = " "
+ if short:
+ lines.append(space_prefix + source.lines[line_index].strip())
+ else:
+ for line in source.lines[:line_index]:
+ lines.append(space_prefix + line)
+ lines.append(self.flow_marker + " " + source.lines[line_index])
+ for line in source.lines[line_index + 1:]:
+ lines.append(space_prefix + line)
+ if excinfo is not None:
+ indent = 4 if short else self._getindent(source)
+ lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
+ return lines
+
+ def get_exconly(self, excinfo, indent=4, markall=False):
+ lines = []
+ indent = " " * indent
+ # get the real exception information out
+ exlines = excinfo.exconly(tryshort=True).split('\n')
+ failindent = self.fail_marker + indent[1:]
+ for line in exlines:
+ lines.append(failindent + line)
+ if not markall:
+ failindent = indent
+ return lines
+
+ def repr_locals(self, locals):
+ if self.showlocals:
+ lines = []
+ keys = [loc for loc in locals if loc[0] != "@"]
+ keys.sort()
+ for name in keys:
+ value = locals[name]
+ if name == '__builtins__':
+ lines.append("__builtins__ = <builtins>")
+ else:
+ # This formatting could all be handled by the
+ # _repr() function, which is only reprlib.Repr in
+ # disguise, so is very configurable.
+ str_repr = self._saferepr(value)
+ # if len(str_repr) < 70 or not isinstance(value,
+ # (list, tuple, dict)):
+ lines.append("%-10s = %s" % (name, str_repr))
+ # else:
+ # self._line("%-10s =\\" % (name,))
+ # # XXX
+ # py.std.pprint.pprint(value, stream=self.excinfowriter)
+ return ReprLocals(lines)
+
+ def repr_traceback_entry(self, entry, excinfo=None):
+ import _pytest._code
+ source = self._getentrysource(entry)
+ if source is None:
+ source = _pytest._code.Source("???")
+ line_index = 0
+ else:
+ # entry.getfirstlinesource() can be -1, should be 0 on jython
+ line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
+
+ lines = []
+ style = entry._repr_style
+ if style is None:
+ style = self.style
+ if style in ("short", "long"):
+ short = style == "short"
+ reprargs = self.repr_args(entry) if not short else None
+ s = self.get_source(source, line_index, excinfo, short=short)
+ lines.extend(s)
+ if short:
+ message = "in %s" % (entry.name)
+ else:
+ message = excinfo and excinfo.typename or ""
+ path = self._makepath(entry.path)
+ filelocrepr = ReprFileLocation(path, entry.lineno + 1, message)
+ localsrepr = None
+ if not short:
+ localsrepr = self.repr_locals(entry.locals)
+ return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style)
+ if excinfo:
+ lines.extend(self.get_exconly(excinfo, indent=4))
+ return ReprEntry(lines, None, None, None, style)
+
+ def _makepath(self, path):
+ if not self.abspath:
+ try:
+ np = py.path.local().bestrelpath(path)
+ except OSError:
+ return path
+ if len(np) < len(str(path)):
+ path = np
+ return path
+
+ def repr_traceback(self, excinfo):
+ traceback = excinfo.traceback
+ if self.tbfilter:
+ traceback = traceback.filter()
+
+ if is_recursion_error(excinfo):
+ traceback, extraline = self._truncate_recursive_traceback(traceback)
+ else:
+ extraline = None
+
+ last = traceback[-1]
+ entries = []
+ for index, entry in enumerate(traceback):
+ einfo = (last == entry) and excinfo or None
+ reprentry = self.repr_traceback_entry(entry, einfo)
+ entries.append(reprentry)
+ return ReprTraceback(entries, extraline, style=self.style)
+
+ def _truncate_recursive_traceback(self, traceback):
+ """
+ Truncate the given recursive traceback trying to find the starting point
+ of the recursion.
+
+ The detection is done by going through each traceback entry and finding the
+ point in which the locals of the frame are equal to the locals of a previous frame (see ``recursionindex()``.
+
+ Handle the situation where the recursion process might raise an exception (for example
+ comparing numpy arrays using equality raises a TypeError), in which case we do our best to
+ warn the user of the error and show a limited traceback.
+ """
+ try:
+ recursionindex = traceback.recursionindex()
+ except Exception as e:
+ max_frames = 10
+ extraline = (
+ '!!! Recursion error detected, but an error occurred locating the origin of recursion.\n'
+ ' The following exception happened when comparing locals in the stack frame:\n'
+ ' {exc_type}: {exc_msg}\n'
+ ' Displaying first and last {max_frames} stack frames out of {total}.'
+ ).format(exc_type=type(e).__name__, exc_msg=safe_str(e), max_frames=max_frames, total=len(traceback))
+ traceback = traceback[:max_frames] + traceback[-max_frames:]
+ else:
+ if recursionindex is not None:
+ extraline = "!!! Recursion detected (same locals & position)"
+ traceback = traceback[:recursionindex + 1]
+ else:
+ extraline = None
+
+ return traceback, extraline
+
+ def repr_excinfo(self, excinfo):
+ if _PY2:
+ reprtraceback = self.repr_traceback(excinfo)
+ reprcrash = excinfo._getreprcrash()
+
+ return ReprExceptionInfo(reprtraceback, reprcrash)
+ else:
+ repr_chain = []
+ e = excinfo.value
+ descr = None
+ while e is not None:
+ if excinfo:
+ reprtraceback = self.repr_traceback(excinfo)
+ reprcrash = excinfo._getreprcrash()
+ else:
+ # fallback to native repr if the exception doesn't have a traceback:
+ # ExceptionInfo objects require a full traceback to work
+ reprtraceback = ReprTracebackNative(py.std.traceback.format_exception(type(e), e, None))
+ reprcrash = None
+
+ repr_chain += [(reprtraceback, reprcrash, descr)]
+ if e.__cause__ is not None:
+ e = e.__cause__
+ excinfo = ExceptionInfo((type(e), e, e.__traceback__)) if e.__traceback__ else None
+ descr = 'The above exception was the direct cause of the following exception:'
+ elif (e.__context__ is not None and not e.__suppress_context__):
+ e = e.__context__
+ excinfo = ExceptionInfo((type(e), e, e.__traceback__)) if e.__traceback__ else None
+ descr = 'During handling of the above exception, another exception occurred:'
+ else:
+ e = None
+ repr_chain.reverse()
+ return ExceptionChainRepr(repr_chain)
+
+
+class TerminalRepr(object):
+ def __str__(self):
+ s = self.__unicode__()
+ if _PY2:
+ s = s.encode('utf-8')
+ return s
+
+ def __unicode__(self):
+ # FYI this is called from pytest-xdist's serialization of exception
+ # information.
+ io = py.io.TextIO()
+ tw = py.io.TerminalWriter(file=io)
+ self.toterminal(tw)
+ return io.getvalue().strip()
+
+ def __repr__(self):
+ return "<%s instance at %0x>" % (self.__class__, id(self))
+
+
+class ExceptionRepr(TerminalRepr):
+ def __init__(self):
+ self.sections = []
+
+ def addsection(self, name, content, sep="-"):
+ self.sections.append((name, content, sep))
+
+ def toterminal(self, tw):
+ for name, content, sep in self.sections:
+ tw.sep(sep, name)
+ tw.line(content)
+
+
+class ExceptionChainRepr(ExceptionRepr):
+ def __init__(self, chain):
+ super(ExceptionChainRepr, self).__init__()
+ self.chain = chain
+ # reprcrash and reprtraceback of the outermost (the newest) exception
+ # in the chain
+ self.reprtraceback = chain[-1][0]
+ self.reprcrash = chain[-1][1]
+
+ def toterminal(self, tw):
+ for element in self.chain:
+ element[0].toterminal(tw)
+ if element[2] is not None:
+ tw.line("")
+ tw.line(element[2], yellow=True)
+ super(ExceptionChainRepr, self).toterminal(tw)
+
+
+class ReprExceptionInfo(ExceptionRepr):
+ def __init__(self, reprtraceback, reprcrash):
+ super(ReprExceptionInfo, self).__init__()
+ self.reprtraceback = reprtraceback
+ self.reprcrash = reprcrash
+
+ def toterminal(self, tw):
+ self.reprtraceback.toterminal(tw)
+ super(ReprExceptionInfo, self).toterminal(tw)
+
+
+class ReprTraceback(TerminalRepr):
+ entrysep = "_ "
+
+ def __init__(self, reprentries, extraline, style):
+ self.reprentries = reprentries
+ self.extraline = extraline
+ self.style = style
+
+ def toterminal(self, tw):
+ # the entries might have different styles
+ for i, entry in enumerate(self.reprentries):
+ if entry.style == "long":
+ tw.line("")
+ entry.toterminal(tw)
+ if i < len(self.reprentries) - 1:
+ next_entry = self.reprentries[i + 1]
+ if entry.style == "long" or \
+ entry.style == "short" and next_entry.style == "long":
+ tw.sep(self.entrysep)
+
+ if self.extraline:
+ tw.line(self.extraline)
+
+
+class ReprTracebackNative(ReprTraceback):
+ def __init__(self, tblines):
+ self.style = "native"
+ self.reprentries = [ReprEntryNative(tblines)]
+ self.extraline = None
+
+
+class ReprEntryNative(TerminalRepr):
+ style = "native"
+
+ def __init__(self, tblines):
+ self.lines = tblines
+
+ def toterminal(self, tw):
+ tw.write("".join(self.lines))
+
+
+class ReprEntry(TerminalRepr):
+ localssep = "_ "
+
+ def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style):
+ self.lines = lines
+ self.reprfuncargs = reprfuncargs
+ self.reprlocals = reprlocals
+ self.reprfileloc = filelocrepr
+ self.style = style
+
+ def toterminal(self, tw):
+ if self.style == "short":
+ self.reprfileloc.toterminal(tw)
+ for line in self.lines:
+ red = line.startswith("E ")
+ tw.line(line, bold=True, red=red)
+ # tw.line("")
+ return
+ if self.reprfuncargs:
+ self.reprfuncargs.toterminal(tw)
+ for line in self.lines:
+ red = line.startswith("E ")
+ tw.line(line, bold=True, red=red)
+ if self.reprlocals:
+ # tw.sep(self.localssep, "Locals")
+ tw.line("")
+ self.reprlocals.toterminal(tw)
+ if self.reprfileloc:
+ if self.lines:
+ tw.line("")
+ self.reprfileloc.toterminal(tw)
+
+ def __str__(self):
+ return "%s\n%s\n%s" % ("\n".join(self.lines),
+ self.reprlocals,
+ self.reprfileloc)
+
+
+class ReprFileLocation(TerminalRepr):
+ def __init__(self, path, lineno, message):
+ self.path = str(path)
+ self.lineno = lineno
+ self.message = message
+
+ def toterminal(self, tw):
+ # filename and lineno output for each entry,
+ # using an output format that most editors unterstand
+ msg = self.message
+ i = msg.find("\n")
+ if i != -1:
+ msg = msg[:i]
+ tw.write(self.path, bold=True, red=True)
+ tw.line(":%s: %s" % (self.lineno, msg))
+
+
+class ReprLocals(TerminalRepr):
+ def __init__(self, lines):
+ self.lines = lines
+
+ def toterminal(self, tw):
+ for line in self.lines:
+ tw.line(line)
+
+
+class ReprFuncArgs(TerminalRepr):
+ def __init__(self, args):
+ self.args = args
+
+ def toterminal(self, tw):
+ if self.args:
+ linesofar = ""
+ for name, value in self.args:
+ ns = "%s = %s" % (safe_str(name), safe_str(value))
+ if len(ns) + len(linesofar) + 2 > tw.fullwidth:
+ if linesofar:
+ tw.line(linesofar)
+ linesofar = ns
+ else:
+ if linesofar:
+ linesofar += ", " + ns
+ else:
+ linesofar = ns
+ if linesofar:
+ tw.line(linesofar)
+ tw.line("")
+
+
+def getrawcode(obj, trycall=True):
+ """ return code object for given function. """
+ try:
+ return obj.__code__
+ except AttributeError:
+ obj = getattr(obj, 'im_func', obj)
+ obj = getattr(obj, 'func_code', obj)
+ obj = getattr(obj, 'f_code', obj)
+ obj = getattr(obj, '__code__', obj)
+ if trycall and not hasattr(obj, 'co_firstlineno'):
+ if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj):
+ x = getrawcode(obj.__call__, trycall=False)
+ if hasattr(x, 'co_firstlineno'):
+ return x
+ return obj
+
+
+if PY35: # RecursionError introduced in 3.5
+ def is_recursion_error(excinfo):
+ return excinfo.errisinstance(RecursionError) # noqa
+else:
+ def is_recursion_error(excinfo):
+ if not excinfo.errisinstance(RuntimeError):
+ return False
+ try:
+ return "maximum recursion depth exceeded" in str(excinfo.value)
+ except UnicodeError:
+ return False
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/source.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/source.py
new file mode 100644
index 00000000000..2638c598b74
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/_code/source.py
@@ -0,0 +1,415 @@
+from __future__ import absolute_import, division, generators, print_function
+
+from bisect import bisect_right
+import sys
+import six
+import inspect
+import tokenize
+import py
+cpy_compile = compile
+
+try:
+ import _ast
+ from _ast import PyCF_ONLY_AST as _AST_FLAG
+except ImportError:
+ _AST_FLAG = 0
+ _ast = None
+
+
+class Source(object):
+ """ a immutable object holding a source code fragment,
+ possibly deindenting it.
+ """
+ _compilecounter = 0
+
+ def __init__(self, *parts, **kwargs):
+ self.lines = lines = []
+ de = kwargs.get('deindent', True)
+ rstrip = kwargs.get('rstrip', True)
+ for part in parts:
+ if not part:
+ partlines = []
+ if isinstance(part, Source):
+ partlines = part.lines
+ elif isinstance(part, (tuple, list)):
+ partlines = [x.rstrip("\n") for x in part]
+ elif isinstance(part, six.string_types):
+ partlines = part.split('\n')
+ if rstrip:
+ while partlines:
+ if partlines[-1].strip():
+ break
+ partlines.pop()
+ else:
+ partlines = getsource(part, deindent=de).lines
+ if de:
+ partlines = deindent(partlines)
+ lines.extend(partlines)
+
+ def __eq__(self, other):
+ try:
+ return self.lines == other.lines
+ except AttributeError:
+ if isinstance(other, str):
+ return str(self) == other
+ return False
+
+ __hash__ = None
+
+ def __getitem__(self, key):
+ if isinstance(key, int):
+ return self.lines[key]
+ else:
+ if key.step not in (None, 1):
+ raise IndexError("cannot slice a Source with a step")
+ newsource = Source()
+ newsource.lines = self.lines[key.start:key.stop]
+ return newsource
+
+ def __len__(self):
+ return len(self.lines)
+
+ def strip(self):
+ """ return new source object with trailing
+ and leading blank lines removed.
+ """
+ start, end = 0, len(self)
+ while start < end and not self.lines[start].strip():
+ start += 1
+ while end > start and not self.lines[end - 1].strip():
+ end -= 1
+ source = Source()
+ source.lines[:] = self.lines[start:end]
+ return source
+
+ def putaround(self, before='', after='', indent=' ' * 4):
+ """ return a copy of the source object with
+ 'before' and 'after' wrapped around it.
+ """
+ before = Source(before)
+ after = Source(after)
+ newsource = Source()
+ lines = [(indent + line) for line in self.lines]
+ newsource.lines = before.lines + lines + after.lines
+ return newsource
+
+ def indent(self, indent=' ' * 4):
+ """ return a copy of the source object with
+ all lines indented by the given indent-string.
+ """
+ newsource = Source()
+ newsource.lines = [(indent + line) for line in self.lines]
+ return newsource
+
+ def getstatement(self, lineno, assertion=False):
+ """ return Source statement which contains the
+ given linenumber (counted from 0).
+ """
+ start, end = self.getstatementrange(lineno, assertion)
+ return self[start:end]
+
+ def getstatementrange(self, lineno, assertion=False):
+ """ return (start, end) tuple which spans the minimal
+ statement region which containing the given lineno.
+ """
+ if not (0 <= lineno < len(self)):
+ raise IndexError("lineno out of range")
+ ast, start, end = getstatementrange_ast(lineno, self)
+ return start, end
+
+ def deindent(self, offset=None):
+ """ return a new source object deindented by offset.
+ If offset is None then guess an indentation offset from
+ the first non-blank line. Subsequent lines which have a
+ lower indentation offset will be copied verbatim as
+ they are assumed to be part of multilines.
+ """
+ # XXX maybe use the tokenizer to properly handle multiline
+ # strings etc.pp?
+ newsource = Source()
+ newsource.lines[:] = deindent(self.lines, offset)
+ return newsource
+
+ def isparseable(self, deindent=True):
+ """ return True if source is parseable, heuristically
+ deindenting it by default.
+ """
+ try:
+ import parser
+ except ImportError:
+ def syntax_checker(x):
+ return compile(x, 'asd', 'exec')
+ else:
+ syntax_checker = parser.suite
+
+ if deindent:
+ source = str(self.deindent())
+ else:
+ source = str(self)
+ try:
+ # compile(source+'\n', "x", "exec")
+ syntax_checker(source + '\n')
+ except KeyboardInterrupt:
+ raise
+ except Exception:
+ return False
+ else:
+ return True
+
+ def __str__(self):
+ return "\n".join(self.lines)
+
+ def compile(self, filename=None, mode='exec',
+ flag=generators.compiler_flag,
+ dont_inherit=0, _genframe=None):
+ """ return compiled code object. if filename is None
+ invent an artificial filename which displays
+ the source/line position of the caller frame.
+ """
+ if not filename or py.path.local(filename).check(file=0):
+ if _genframe is None:
+ _genframe = sys._getframe(1) # the caller
+ fn, lineno = _genframe.f_code.co_filename, _genframe.f_lineno
+ base = "<%d-codegen " % self._compilecounter
+ self.__class__._compilecounter += 1
+ if not filename:
+ filename = base + '%s:%d>' % (fn, lineno)
+ else:
+ filename = base + '%r %s:%d>' % (filename, fn, lineno)
+ source = "\n".join(self.lines) + '\n'
+ try:
+ co = cpy_compile(source, filename, mode, flag)
+ except SyntaxError:
+ ex = sys.exc_info()[1]
+ # re-represent syntax errors from parsing python strings
+ msglines = self.lines[:ex.lineno]
+ if ex.offset:
+ msglines.append(" " * ex.offset + '^')
+ msglines.append("(code was compiled probably from here: %s)" % filename)
+ newex = SyntaxError('\n'.join(msglines))
+ newex.offset = ex.offset
+ newex.lineno = ex.lineno
+ newex.text = ex.text
+ raise newex
+ else:
+ if flag & _AST_FLAG:
+ return co
+ lines = [(x + "\n") for x in self.lines]
+ py.std.linecache.cache[filename] = (1, None, lines, filename)
+ return co
+
+#
+# public API shortcut functions
+#
+
+
+def compile_(source, filename=None, mode='exec', flags=generators.compiler_flag, dont_inherit=0):
+ """ compile the given source to a raw code object,
+ and maintain an internal cache which allows later
+ retrieval of the source code for the code object
+ and any recursively created code objects.
+ """
+ if _ast is not None and isinstance(source, _ast.AST):
+ # XXX should Source support having AST?
+ return cpy_compile(source, filename, mode, flags, dont_inherit)
+ _genframe = sys._getframe(1) # the caller
+ s = Source(source)
+ co = s.compile(filename, mode, flags, _genframe=_genframe)
+ return co
+
+
+def getfslineno(obj):
+ """ Return source location (path, lineno) for the given object.
+ If the source cannot be determined return ("", -1)
+ """
+ import _pytest._code
+ try:
+ code = _pytest._code.Code(obj)
+ except TypeError:
+ try:
+ fn = (py.std.inspect.getsourcefile(obj) or
+ py.std.inspect.getfile(obj))
+ except TypeError:
+ return "", -1
+
+ fspath = fn and py.path.local(fn) or None
+ lineno = -1
+ if fspath:
+ try:
+ _, lineno = findsource(obj)
+ except IOError:
+ pass
+ else:
+ fspath = code.path
+ lineno = code.firstlineno
+ assert isinstance(lineno, int)
+ return fspath, lineno
+
+#
+# helper functions
+#
+
+
+def findsource(obj):
+ try:
+ sourcelines, lineno = py.std.inspect.findsource(obj)
+ except py.builtin._sysex:
+ raise
+ except: # noqa
+ return None, -1
+ source = Source()
+ source.lines = [line.rstrip() for line in sourcelines]
+ return source, lineno
+
+
+def getsource(obj, **kwargs):
+ import _pytest._code
+ obj = _pytest._code.getrawcode(obj)
+ try:
+ strsrc = inspect.getsource(obj)
+ except IndentationError:
+ strsrc = "\"Buggy python version consider upgrading, cannot get source\""
+ assert isinstance(strsrc, str)
+ return Source(strsrc, **kwargs)
+
+
+def deindent(lines, offset=None):
+ if offset is None:
+ for line in lines:
+ line = line.expandtabs()
+ s = line.lstrip()
+ if s:
+ offset = len(line) - len(s)
+ break
+ else:
+ offset = 0
+ if offset == 0:
+ return list(lines)
+ newlines = []
+
+ def readline_generator(lines):
+ for line in lines:
+ yield line + '\n'
+ while True:
+ yield ''
+
+ it = readline_generator(lines)
+
+ try:
+ for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(lambda: next(it)):
+ if sline > len(lines):
+ break # End of input reached
+ if sline > len(newlines):
+ line = lines[sline - 1].expandtabs()
+ if line.lstrip() and line[:offset].isspace():
+ line = line[offset:] # Deindent
+ newlines.append(line)
+
+ for i in range(sline, eline):
+ # Don't deindent continuing lines of
+ # multiline tokens (i.e. multiline strings)
+ newlines.append(lines[i])
+ except (IndentationError, tokenize.TokenError):
+ pass
+ # Add any lines we didn't see. E.g. if an exception was raised.
+ newlines.extend(lines[len(newlines):])
+ return newlines
+
+
+def get_statement_startend2(lineno, node):
+ import ast
+ # flatten all statements and except handlers into one lineno-list
+ # AST's line numbers start indexing at 1
+ values = []
+ for x in ast.walk(node):
+ if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler):
+ values.append(x.lineno - 1)
+ for name in "finalbody", "orelse":
+ val = getattr(x, name, None)
+ if val:
+ # treat the finally/orelse part as its own statement
+ values.append(val[0].lineno - 1 - 1)
+ values.sort()
+ insert_index = bisect_right(values, lineno)
+ start = values[insert_index - 1]
+ if insert_index >= len(values):
+ end = None
+ else:
+ end = values[insert_index]
+ return start, end
+
+
+def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
+ if astnode is None:
+ content = str(source)
+ try:
+ astnode = compile(content, "source", "exec", 1024) # 1024 for AST
+ except ValueError:
+ start, end = getstatementrange_old(lineno, source, assertion)
+ return None, start, end
+ start, end = get_statement_startend2(lineno, astnode)
+ # we need to correct the end:
+ # - ast-parsing strips comments
+ # - there might be empty lines
+ # - we might have lesser indented code blocks at the end
+ if end is None:
+ end = len(source.lines)
+
+ if end > start + 1:
+ # make sure we don't span differently indented code blocks
+ # by using the BlockFinder helper used which inspect.getsource() uses itself
+ block_finder = inspect.BlockFinder()
+ # if we start with an indented line, put blockfinder to "started" mode
+ block_finder.started = source.lines[start][0].isspace()
+ it = ((x + "\n") for x in source.lines[start:end])
+ try:
+ for tok in tokenize.generate_tokens(lambda: next(it)):
+ block_finder.tokeneater(*tok)
+ except (inspect.EndOfBlock, IndentationError):
+ end = block_finder.last + start
+ except Exception:
+ pass
+
+ # the end might still point to a comment or empty line, correct it
+ while end:
+ line = source.lines[end - 1].lstrip()
+ if line.startswith("#") or not line:
+ end -= 1
+ else:
+ break
+ return astnode, start, end
+
+
+def getstatementrange_old(lineno, source, assertion=False):
+ """ return (start, end) tuple which spans the minimal
+ statement region which containing the given lineno.
+ raise an IndexError if no such statementrange can be found.
+ """
+ # XXX this logic is only used on python2.4 and below
+ # 1. find the start of the statement
+ from codeop import compile_command
+ for start in range(lineno, -1, -1):
+ if assertion:
+ line = source.lines[start]
+ # the following lines are not fully tested, change with care
+ if 'super' in line and 'self' in line and '__init__' in line:
+ raise IndexError("likely a subclass")
+ if "assert" not in line and "raise" not in line:
+ continue
+ trylines = source.lines[start:lineno + 1]
+ # quick hack to prepare parsing an indented line with
+ # compile_command() (which errors on "return" outside defs)
+ trylines.insert(0, 'def xxx():')
+ trysource = '\n '.join(trylines)
+ # ^ space here
+ try:
+ compile_command(trysource)
+ except (SyntaxError, OverflowError, ValueError):
+ continue
+
+ # 2. find the end of the statement
+ for end in range(lineno + 1, len(source) + 1):
+ trysource = source[start:end]
+ if trysource.isparseable():
+ return start, end
+ raise SyntaxError("no valid source range around line %d " % (lineno,))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/__init__.py
new file mode 100644
index 00000000000..a48e98c85aa
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/__init__.py
@@ -0,0 +1,146 @@
+"""
+support for presenting detailed information in failing assertions.
+"""
+from __future__ import absolute_import, division, print_function
+import sys
+import six
+
+from _pytest.assertion import util
+from _pytest.assertion import rewrite
+from _pytest.assertion import truncate
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("debugconfig")
+ group.addoption('--assert',
+ action="store",
+ dest="assertmode",
+ choices=("rewrite", "plain",),
+ default="rewrite",
+ metavar="MODE",
+ help="""Control assertion debugging tools. 'plain'
+ performs no assertion debugging. 'rewrite'
+ (the default) rewrites assert statements in
+ test modules on import to provide assert
+ expression information.""")
+
+
+def register_assert_rewrite(*names):
+ """Register one or more module names to be rewritten on import.
+
+ This function will make sure that this module or all modules inside
+ the package will get their assert statements rewritten.
+ Thus you should make sure to call this before the module is
+ actually imported, usually in your __init__.py if you are a plugin
+ using a package.
+
+ :raise TypeError: if the given module names are not strings.
+ """
+ for name in names:
+ if not isinstance(name, str):
+ msg = 'expected module names as *args, got {0} instead'
+ raise TypeError(msg.format(repr(names)))
+ for hook in sys.meta_path:
+ if isinstance(hook, rewrite.AssertionRewritingHook):
+ importhook = hook
+ break
+ else:
+ importhook = DummyRewriteHook()
+ importhook.mark_rewrite(*names)
+
+
+class DummyRewriteHook(object):
+ """A no-op import hook for when rewriting is disabled."""
+
+ def mark_rewrite(self, *names):
+ pass
+
+
+class AssertionState:
+ """State for the assertion plugin."""
+
+ def __init__(self, config, mode):
+ self.mode = mode
+ self.trace = config.trace.root.get("assertion")
+ self.hook = None
+
+
+def install_importhook(config):
+ """Try to install the rewrite hook, raise SystemError if it fails."""
+ # Jython has an AST bug that make the assertion rewriting hook malfunction.
+ if (sys.platform.startswith('java')):
+ raise SystemError('rewrite not supported')
+
+ config._assertstate = AssertionState(config, 'rewrite')
+ config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config)
+ sys.meta_path.insert(0, hook)
+ config._assertstate.trace('installed rewrite import hook')
+
+ def undo():
+ hook = config._assertstate.hook
+ if hook is not None and hook in sys.meta_path:
+ sys.meta_path.remove(hook)
+
+ config.add_cleanup(undo)
+ return hook
+
+
+def pytest_collection(session):
+ # this hook is only called when test modules are collected
+ # so for example not in the master process of pytest-xdist
+ # (which does not collect test modules)
+ assertstate = getattr(session.config, '_assertstate', None)
+ if assertstate:
+ if assertstate.hook is not None:
+ assertstate.hook.set_session(session)
+
+
+def pytest_runtest_setup(item):
+ """Setup the pytest_assertrepr_compare hook
+
+ The newinterpret and rewrite modules will use util._reprcompare if
+ it exists to use custom reporting via the
+ pytest_assertrepr_compare hook. This sets up this custom
+ comparison for the test.
+ """
+ def callbinrepr(op, left, right):
+ """Call the pytest_assertrepr_compare hook and prepare the result
+
+ This uses the first result from the hook and then ensures the
+ following:
+ * Overly verbose explanations are truncated unless configured otherwise
+ (eg. if running in verbose mode).
+ * Embedded newlines are escaped to help util.format_explanation()
+ later.
+ * If the rewrite mode is used embedded %-characters are replaced
+ to protect later % formatting.
+
+ The result can be formatted by util.format_explanation() for
+ pretty printing.
+ """
+ hook_result = item.ihook.pytest_assertrepr_compare(
+ config=item.config, op=op, left=left, right=right)
+ for new_expl in hook_result:
+ if new_expl:
+ new_expl = truncate.truncate_if_required(new_expl, item)
+ new_expl = [line.replace("\n", "\\n") for line in new_expl]
+ res = six.text_type("\n~").join(new_expl)
+ if item.config.getvalue("assertmode") == "rewrite":
+ res = res.replace("%", "%%")
+ return res
+ util._reprcompare = callbinrepr
+
+
+def pytest_runtest_teardown(item):
+ util._reprcompare = None
+
+
+def pytest_sessionfinish(session):
+ assertstate = getattr(session.config, '_assertstate', None)
+ if assertstate:
+ if assertstate.hook is not None:
+ assertstate.hook.set_session(None)
+
+
+# Expose this plugin's implementation for the pytest_assertrepr_compare hook
+pytest_assertrepr_compare = util.assertrepr_compare
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/rewrite.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/rewrite.py
new file mode 100644
index 00000000000..f64358f490b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/rewrite.py
@@ -0,0 +1,948 @@
+"""Rewrite assertion AST to produce nice error messages"""
+from __future__ import absolute_import, division, print_function
+import ast
+import _ast
+import errno
+import itertools
+import imp
+import marshal
+import os
+import re
+import six
+import struct
+import sys
+import types
+
+import py
+from _pytest.assertion import util
+
+
+# pytest caches rewritten pycs in __pycache__.
+if hasattr(imp, "get_tag"):
+ PYTEST_TAG = imp.get_tag() + "-PYTEST"
+else:
+ if hasattr(sys, "pypy_version_info"):
+ impl = "pypy"
+ elif sys.platform == "java":
+ impl = "jython"
+ else:
+ impl = "cpython"
+ ver = sys.version_info
+ PYTEST_TAG = "%s-%s%s-PYTEST" % (impl, ver[0], ver[1])
+ del ver, impl
+
+PYC_EXT = ".py" + (__debug__ and "c" or "o")
+PYC_TAIL = "." + PYTEST_TAG + PYC_EXT
+
+ASCII_IS_DEFAULT_ENCODING = sys.version_info[0] < 3
+
+if sys.version_info >= (3, 5):
+ ast_Call = ast.Call
+else:
+ def ast_Call(a, b, c):
+ return ast.Call(a, b, c, None, None)
+
+
+class AssertionRewritingHook(object):
+ """PEP302 Import hook which rewrites asserts."""
+
+ def __init__(self, config):
+ self.config = config
+ self.fnpats = config.getini("python_files")
+ self.session = None
+ self.modules = {}
+ self._rewritten_names = set()
+ self._register_with_pkg_resources()
+ self._must_rewrite = set()
+
+ def set_session(self, session):
+ self.session = session
+
+ def find_module(self, name, path=None):
+ state = self.config._assertstate
+ state.trace("find_module called for: %s" % name)
+ names = name.rsplit(".", 1)
+ lastname = names[-1]
+ pth = None
+ if path is not None:
+ # Starting with Python 3.3, path is a _NamespacePath(), which
+ # causes problems if not converted to list.
+ path = list(path)
+ if len(path) == 1:
+ pth = path[0]
+ if pth is None:
+ try:
+ fd, fn, desc = imp.find_module(lastname, path)
+ except ImportError:
+ return None
+ if fd is not None:
+ fd.close()
+ tp = desc[2]
+ if tp == imp.PY_COMPILED:
+ if hasattr(imp, "source_from_cache"):
+ try:
+ fn = imp.source_from_cache(fn)
+ except ValueError:
+ # Python 3 doesn't like orphaned but still-importable
+ # .pyc files.
+ fn = fn[:-1]
+ else:
+ fn = fn[:-1]
+ elif tp != imp.PY_SOURCE:
+ # Don't know what this is.
+ return None
+ else:
+ fn = os.path.join(pth, name.rpartition(".")[2] + ".py")
+
+ fn_pypath = py.path.local(fn)
+ if not self._should_rewrite(name, fn_pypath, state):
+ return None
+
+ self._rewritten_names.add(name)
+
+ # The requested module looks like a test file, so rewrite it. This is
+ # the most magical part of the process: load the source, rewrite the
+ # asserts, and load the rewritten source. We also cache the rewritten
+ # module code in a special pyc. We must be aware of the possibility of
+ # concurrent pytest processes rewriting and loading pycs. To avoid
+ # tricky race conditions, we maintain the following invariant: The
+ # cached pyc is always a complete, valid pyc. Operations on it must be
+ # atomic. POSIX's atomic rename comes in handy.
+ write = not sys.dont_write_bytecode
+ cache_dir = os.path.join(fn_pypath.dirname, "__pycache__")
+ if write:
+ try:
+ os.mkdir(cache_dir)
+ except OSError:
+ e = sys.exc_info()[1].errno
+ if e == errno.EEXIST:
+ # Either the __pycache__ directory already exists (the
+ # common case) or it's blocked by a non-dir node. In the
+ # latter case, we'll ignore it in _write_pyc.
+ pass
+ elif e in [errno.ENOENT, errno.ENOTDIR]:
+ # One of the path components was not a directory, likely
+ # because we're in a zip file.
+ write = False
+ elif e in [errno.EACCES, errno.EROFS, errno.EPERM]:
+ state.trace("read only directory: %r" % fn_pypath.dirname)
+ write = False
+ else:
+ raise
+ cache_name = fn_pypath.basename[:-3] + PYC_TAIL
+ pyc = os.path.join(cache_dir, cache_name)
+ # Notice that even if we're in a read-only directory, I'm going
+ # to check for a cached pyc. This may not be optimal...
+ co = _read_pyc(fn_pypath, pyc, state.trace)
+ if co is None:
+ state.trace("rewriting %r" % (fn,))
+ source_stat, co = _rewrite_test(self.config, fn_pypath)
+ if co is None:
+ # Probably a SyntaxError in the test.
+ return None
+ if write:
+ _make_rewritten_pyc(state, source_stat, pyc, co)
+ else:
+ state.trace("found cached rewritten pyc for %r" % (fn,))
+ self.modules[name] = co, pyc
+ return self
+
+ def _should_rewrite(self, name, fn_pypath, state):
+ # always rewrite conftest files
+ fn = str(fn_pypath)
+ if fn_pypath.basename == 'conftest.py':
+ state.trace("rewriting conftest file: %r" % (fn,))
+ return True
+
+ if self.session is not None:
+ if self.session.isinitpath(fn):
+ state.trace("matched test file (was specified on cmdline): %r" %
+ (fn,))
+ return True
+
+ # modules not passed explicitly on the command line are only
+ # rewritten if they match the naming convention for test files
+ for pat in self.fnpats:
+ if fn_pypath.fnmatch(pat):
+ state.trace("matched test file %r" % (fn,))
+ return True
+
+ for marked in self._must_rewrite:
+ if name == marked or name.startswith(marked + '.'):
+ state.trace("matched marked file %r (from %r)" % (name, marked))
+ return True
+
+ return False
+
+ def mark_rewrite(self, *names):
+ """Mark import names as needing to be rewritten.
+
+ The named module or package as well as any nested modules will
+ be rewritten on import.
+ """
+ already_imported = set(names).intersection(set(sys.modules))
+ if already_imported:
+ for name in already_imported:
+ if name not in self._rewritten_names:
+ self._warn_already_imported(name)
+ self._must_rewrite.update(names)
+
+ def _warn_already_imported(self, name):
+ self.config.warn(
+ 'P1',
+ 'Module already imported so cannot be rewritten: %s' % name)
+
+ def load_module(self, name):
+ # If there is an existing module object named 'fullname' in
+ # sys.modules, the loader must use that existing module. (Otherwise,
+ # the reload() builtin will not work correctly.)
+ if name in sys.modules:
+ return sys.modules[name]
+
+ co, pyc = self.modules.pop(name)
+ # I wish I could just call imp.load_compiled here, but __file__ has to
+ # be set properly. In Python 3.2+, this all would be handled correctly
+ # by load_compiled.
+ mod = sys.modules[name] = imp.new_module(name)
+ try:
+ mod.__file__ = co.co_filename
+ # Normally, this attribute is 3.2+.
+ mod.__cached__ = pyc
+ mod.__loader__ = self
+ py.builtin.exec_(co, mod.__dict__)
+ except: # noqa
+ if name in sys.modules:
+ del sys.modules[name]
+ raise
+ return sys.modules[name]
+
+ def is_package(self, name):
+ try:
+ fd, fn, desc = imp.find_module(name)
+ except ImportError:
+ return False
+ if fd is not None:
+ fd.close()
+ tp = desc[2]
+ return tp == imp.PKG_DIRECTORY
+
+ @classmethod
+ def _register_with_pkg_resources(cls):
+ """
+ Ensure package resources can be loaded from this loader. May be called
+ multiple times, as the operation is idempotent.
+ """
+ try:
+ import pkg_resources
+ # access an attribute in case a deferred importer is present
+ pkg_resources.__name__
+ except ImportError:
+ return
+
+ # Since pytest tests are always located in the file system, the
+ # DefaultProvider is appropriate.
+ pkg_resources.register_loader_type(cls, pkg_resources.DefaultProvider)
+
+ def get_data(self, pathname):
+ """Optional PEP302 get_data API.
+ """
+ with open(pathname, 'rb') as f:
+ return f.read()
+
+
+def _write_pyc(state, co, source_stat, pyc):
+ # Technically, we don't have to have the same pyc format as
+ # (C)Python, since these "pycs" should never be seen by builtin
+ # import. However, there's little reason deviate, and I hope
+ # sometime to be able to use imp.load_compiled to load them. (See
+ # the comment in load_module above.)
+ try:
+ fp = open(pyc, "wb")
+ except IOError:
+ err = sys.exc_info()[1].errno
+ state.trace("error writing pyc file at %s: errno=%s" % (pyc, err))
+ # we ignore any failure to write the cache file
+ # there are many reasons, permission-denied, __pycache__ being a
+ # file etc.
+ return False
+ try:
+ fp.write(imp.get_magic())
+ mtime = int(source_stat.mtime)
+ size = source_stat.size & 0xFFFFFFFF
+ fp.write(struct.pack("<ll", mtime, size))
+ marshal.dump(co, fp)
+ finally:
+ fp.close()
+ return True
+
+
+RN = "\r\n".encode("utf-8")
+N = "\n".encode("utf-8")
+
+cookie_re = re.compile(r"^[ \t\f]*#.*coding[:=][ \t]*[-\w.]+")
+BOM_UTF8 = '\xef\xbb\xbf'
+
+
+def _rewrite_test(config, fn):
+ """Try to read and rewrite *fn* and return the code object."""
+ state = config._assertstate
+ try:
+ stat = fn.stat()
+ source = fn.read("rb")
+ except EnvironmentError:
+ return None, None
+ if ASCII_IS_DEFAULT_ENCODING:
+ # ASCII is the default encoding in Python 2. Without a coding
+ # declaration, Python 2 will complain about any bytes in the file
+ # outside the ASCII range. Sadly, this behavior does not extend to
+ # compile() or ast.parse(), which prefer to interpret the bytes as
+ # latin-1. (At least they properly handle explicit coding cookies.) To
+ # preserve this error behavior, we could force ast.parse() to use ASCII
+ # as the encoding by inserting a coding cookie. Unfortunately, that
+ # messes up line numbers. Thus, we have to check ourselves if anything
+ # is outside the ASCII range in the case no encoding is explicitly
+ # declared. For more context, see issue #269. Yay for Python 3 which
+ # gets this right.
+ end1 = source.find("\n")
+ end2 = source.find("\n", end1 + 1)
+ if (not source.startswith(BOM_UTF8) and
+ cookie_re.match(source[0:end1]) is None and
+ cookie_re.match(source[end1 + 1:end2]) is None):
+ if hasattr(state, "_indecode"):
+ # encodings imported us again, so don't rewrite.
+ return None, None
+ state._indecode = True
+ try:
+ try:
+ source.decode("ascii")
+ except UnicodeDecodeError:
+ # Let it fail in real import.
+ return None, None
+ finally:
+ del state._indecode
+ try:
+ tree = ast.parse(source)
+ except SyntaxError:
+ # Let this pop up again in the real import.
+ state.trace("failed to parse: %r" % (fn,))
+ return None, None
+ rewrite_asserts(tree, fn, config)
+ try:
+ co = compile(tree, fn.strpath, "exec", dont_inherit=True)
+ except SyntaxError:
+ # It's possible that this error is from some bug in the
+ # assertion rewriting, but I don't know of a fast way to tell.
+ state.trace("failed to compile: %r" % (fn,))
+ return None, None
+ return stat, co
+
+
+def _make_rewritten_pyc(state, source_stat, pyc, co):
+ """Try to dump rewritten code to *pyc*."""
+ if sys.platform.startswith("win"):
+ # Windows grants exclusive access to open files and doesn't have atomic
+ # rename, so just write into the final file.
+ _write_pyc(state, co, source_stat, pyc)
+ else:
+ # When not on windows, assume rename is atomic. Dump the code object
+ # into a file specific to this process and atomically replace it.
+ proc_pyc = pyc + "." + str(os.getpid())
+ if _write_pyc(state, co, source_stat, proc_pyc):
+ os.rename(proc_pyc, pyc)
+
+
+def _read_pyc(source, pyc, trace=lambda x: None):
+ """Possibly read a pytest pyc containing rewritten code.
+
+ Return rewritten code if successful or None if not.
+ """
+ try:
+ fp = open(pyc, "rb")
+ except IOError:
+ return None
+ with fp:
+ try:
+ mtime = int(source.mtime())
+ size = source.size()
+ data = fp.read(12)
+ except EnvironmentError as e:
+ trace('_read_pyc(%s): EnvironmentError %s' % (source, e))
+ return None
+ # Check for invalid or out of date pyc file.
+ if (len(data) != 12 or data[:4] != imp.get_magic() or
+ struct.unpack("<ll", data[4:]) != (mtime, size)):
+ trace('_read_pyc(%s): invalid or out of date pyc' % source)
+ return None
+ try:
+ co = marshal.load(fp)
+ except Exception as e:
+ trace('_read_pyc(%s): marshal.load error %s' % (source, e))
+ return None
+ if not isinstance(co, types.CodeType):
+ trace('_read_pyc(%s): not a code object' % source)
+ return None
+ return co
+
+
+def rewrite_asserts(mod, module_path=None, config=None):
+ """Rewrite the assert statements in mod."""
+ AssertionRewriter(module_path, config).run(mod)
+
+
+def _saferepr(obj):
+ """Get a safe repr of an object for assertion error messages.
+
+ The assertion formatting (util.format_explanation()) requires
+ newlines to be escaped since they are a special character for it.
+ Normally assertion.util.format_explanation() does this but for a
+ custom repr it is possible to contain one of the special escape
+ sequences, especially '\n{' and '\n}' are likely to be present in
+ JSON reprs.
+
+ """
+ repr = py.io.saferepr(obj)
+ if isinstance(repr, six.text_type):
+ t = six.text_type
+ else:
+ t = six.binary_type
+ return repr.replace(t("\n"), t("\\n"))
+
+
+from _pytest.assertion.util import format_explanation as _format_explanation # noqa
+
+
+def _format_assertmsg(obj):
+ """Format the custom assertion message given.
+
+ For strings this simply replaces newlines with '\n~' so that
+ util.format_explanation() will preserve them instead of escaping
+ newlines. For other objects py.io.saferepr() is used first.
+
+ """
+ # reprlib appears to have a bug which means that if a string
+ # contains a newline it gets escaped, however if an object has a
+ # .__repr__() which contains newlines it does not get escaped.
+ # However in either case we want to preserve the newline.
+ if isinstance(obj, six.text_type) or isinstance(obj, six.binary_type):
+ s = obj
+ is_repr = False
+ else:
+ s = py.io.saferepr(obj)
+ is_repr = True
+ if isinstance(s, six.text_type):
+ t = six.text_type
+ else:
+ t = six.binary_type
+ s = s.replace(t("\n"), t("\n~")).replace(t("%"), t("%%"))
+ if is_repr:
+ s = s.replace(t("\\n"), t("\n~"))
+ return s
+
+
+def _should_repr_global_name(obj):
+ return not hasattr(obj, "__name__") and not callable(obj)
+
+
+def _format_boolop(explanations, is_or):
+ explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")"
+ if isinstance(explanation, six.text_type):
+ t = six.text_type
+ else:
+ t = six.binary_type
+ return explanation.replace(t('%'), t('%%'))
+
+
+def _call_reprcompare(ops, results, expls, each_obj):
+ for i, res, expl in zip(range(len(ops)), results, expls):
+ try:
+ done = not res
+ except Exception:
+ done = True
+ if done:
+ break
+ if util._reprcompare is not None:
+ custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1])
+ if custom is not None:
+ return custom
+ return expl
+
+
+unary_map = {
+ ast.Not: "not %s",
+ ast.Invert: "~%s",
+ ast.USub: "-%s",
+ ast.UAdd: "+%s"
+}
+
+binop_map = {
+ ast.BitOr: "|",
+ ast.BitXor: "^",
+ ast.BitAnd: "&",
+ ast.LShift: "<<",
+ ast.RShift: ">>",
+ ast.Add: "+",
+ ast.Sub: "-",
+ ast.Mult: "*",
+ ast.Div: "/",
+ ast.FloorDiv: "//",
+ ast.Mod: "%%", # escaped for string formatting
+ ast.Eq: "==",
+ ast.NotEq: "!=",
+ ast.Lt: "<",
+ ast.LtE: "<=",
+ ast.Gt: ">",
+ ast.GtE: ">=",
+ ast.Pow: "**",
+ ast.Is: "is",
+ ast.IsNot: "is not",
+ ast.In: "in",
+ ast.NotIn: "not in"
+}
+# Python 3.5+ compatibility
+try:
+ binop_map[ast.MatMult] = "@"
+except AttributeError:
+ pass
+
+# Python 3.4+ compatibility
+if hasattr(ast, "NameConstant"):
+ _NameConstant = ast.NameConstant
+else:
+ def _NameConstant(c):
+ return ast.Name(str(c), ast.Load())
+
+
+def set_location(node, lineno, col_offset):
+ """Set node location information recursively."""
+ def _fix(node, lineno, col_offset):
+ if "lineno" in node._attributes:
+ node.lineno = lineno
+ if "col_offset" in node._attributes:
+ node.col_offset = col_offset
+ for child in ast.iter_child_nodes(node):
+ _fix(child, lineno, col_offset)
+ _fix(node, lineno, col_offset)
+ return node
+
+
+class AssertionRewriter(ast.NodeVisitor):
+ """Assertion rewriting implementation.
+
+ The main entrypoint is to call .run() with an ast.Module instance,
+ this will then find all the assert statements and rewrite them to
+ provide intermediate values and a detailed assertion error. See
+ http://pybites.blogspot.be/2011/07/behind-scenes-of-pytests-new-assertion.html
+ for an overview of how this works.
+
+ The entry point here is .run() which will iterate over all the
+ statements in an ast.Module and for each ast.Assert statement it
+ finds call .visit() with it. Then .visit_Assert() takes over and
+ is responsible for creating new ast statements to replace the
+ original assert statement: it rewrites the test of an assertion
+ to provide intermediate values and replace it with an if statement
+ which raises an assertion error with a detailed explanation in
+ case the expression is false.
+
+ For this .visit_Assert() uses the visitor pattern to visit all the
+ AST nodes of the ast.Assert.test field, each visit call returning
+ an AST node and the corresponding explanation string. During this
+ state is kept in several instance attributes:
+
+ :statements: All the AST statements which will replace the assert
+ statement.
+
+ :variables: This is populated by .variable() with each variable
+ used by the statements so that they can all be set to None at
+ the end of the statements.
+
+ :variable_counter: Counter to create new unique variables needed
+ by statements. Variables are created using .variable() and
+ have the form of "@py_assert0".
+
+ :on_failure: The AST statements which will be executed if the
+ assertion test fails. This is the code which will construct
+ the failure message and raises the AssertionError.
+
+ :explanation_specifiers: A dict filled by .explanation_param()
+ with %-formatting placeholders and their corresponding
+ expressions to use in the building of an assertion message.
+ This is used by .pop_format_context() to build a message.
+
+ :stack: A stack of the explanation_specifiers dicts maintained by
+ .push_format_context() and .pop_format_context() which allows
+ to build another %-formatted string while already building one.
+
+ This state is reset on every new assert statement visited and used
+ by the other visitors.
+
+ """
+
+ def __init__(self, module_path, config):
+ super(AssertionRewriter, self).__init__()
+ self.module_path = module_path
+ self.config = config
+
+ def run(self, mod):
+ """Find all assert statements in *mod* and rewrite them."""
+ if not mod.body:
+ # Nothing to do.
+ return
+ # Insert some special imports at the top of the module but after any
+ # docstrings and __future__ imports.
+ aliases = [ast.alias(py.builtin.builtins.__name__, "@py_builtins"),
+ ast.alias("_pytest.assertion.rewrite", "@pytest_ar")]
+ doc = getattr(mod, "docstring", None)
+ expect_docstring = doc is None
+ if doc is not None and self.is_rewrite_disabled(doc):
+ return
+ pos = 0
+ lineno = 1
+ for item in mod.body:
+ if (expect_docstring and isinstance(item, ast.Expr) and
+ isinstance(item.value, ast.Str)):
+ doc = item.value.s
+ if self.is_rewrite_disabled(doc):
+ return
+ expect_docstring = False
+ elif (not isinstance(item, ast.ImportFrom) or item.level > 0 or
+ item.module != "__future__"):
+ lineno = item.lineno
+ break
+ pos += 1
+ else:
+ lineno = item.lineno
+ imports = [ast.Import([alias], lineno=lineno, col_offset=0)
+ for alias in aliases]
+ mod.body[pos:pos] = imports
+ # Collect asserts.
+ nodes = [mod]
+ while nodes:
+ node = nodes.pop()
+ for name, field in ast.iter_fields(node):
+ if isinstance(field, list):
+ new = []
+ for i, child in enumerate(field):
+ if isinstance(child, ast.Assert):
+ # Transform assert.
+ new.extend(self.visit(child))
+ else:
+ new.append(child)
+ if isinstance(child, ast.AST):
+ nodes.append(child)
+ setattr(node, name, new)
+ elif (isinstance(field, ast.AST) and
+ # Don't recurse into expressions as they can't contain
+ # asserts.
+ not isinstance(field, ast.expr)):
+ nodes.append(field)
+
+ def is_rewrite_disabled(self, docstring):
+ return "PYTEST_DONT_REWRITE" in docstring
+
+ def variable(self):
+ """Get a new variable."""
+ # Use a character invalid in python identifiers to avoid clashing.
+ name = "@py_assert" + str(next(self.variable_counter))
+ self.variables.append(name)
+ return name
+
+ def assign(self, expr):
+ """Give *expr* a name."""
+ name = self.variable()
+ self.statements.append(ast.Assign([ast.Name(name, ast.Store())], expr))
+ return ast.Name(name, ast.Load())
+
+ def display(self, expr):
+ """Call py.io.saferepr on the expression."""
+ return self.helper("saferepr", expr)
+
+ def helper(self, name, *args):
+ """Call a helper in this module."""
+ py_name = ast.Name("@pytest_ar", ast.Load())
+ attr = ast.Attribute(py_name, "_" + name, ast.Load())
+ return ast_Call(attr, list(args), [])
+
+ def builtin(self, name):
+ """Return the builtin called *name*."""
+ builtin_name = ast.Name("@py_builtins", ast.Load())
+ return ast.Attribute(builtin_name, name, ast.Load())
+
+ def explanation_param(self, expr):
+ """Return a new named %-formatting placeholder for expr.
+
+ This creates a %-formatting placeholder for expr in the
+ current formatting context, e.g. ``%(py0)s``. The placeholder
+ and expr are placed in the current format context so that it
+ can be used on the next call to .pop_format_context().
+
+ """
+ specifier = "py" + str(next(self.variable_counter))
+ self.explanation_specifiers[specifier] = expr
+ return "%(" + specifier + ")s"
+
+ def push_format_context(self):
+ """Create a new formatting context.
+
+ The format context is used for when an explanation wants to
+ have a variable value formatted in the assertion message. In
+ this case the value required can be added using
+ .explanation_param(). Finally .pop_format_context() is used
+ to format a string of %-formatted values as added by
+ .explanation_param().
+
+ """
+ self.explanation_specifiers = {}
+ self.stack.append(self.explanation_specifiers)
+
+ def pop_format_context(self, expl_expr):
+ """Format the %-formatted string with current format context.
+
+ The expl_expr should be an ast.Str instance constructed from
+ the %-placeholders created by .explanation_param(). This will
+ add the required code to format said string to .on_failure and
+ return the ast.Name instance of the formatted string.
+
+ """
+ current = self.stack.pop()
+ if self.stack:
+ self.explanation_specifiers = self.stack[-1]
+ keys = [ast.Str(key) for key in current.keys()]
+ format_dict = ast.Dict(keys, list(current.values()))
+ form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
+ name = "@py_format" + str(next(self.variable_counter))
+ self.on_failure.append(ast.Assign([ast.Name(name, ast.Store())], form))
+ return ast.Name(name, ast.Load())
+
+ def generic_visit(self, node):
+ """Handle expressions we don't have custom code for."""
+ assert isinstance(node, ast.expr)
+ res = self.assign(node)
+ return res, self.explanation_param(self.display(res))
+
+ def visit_Assert(self, assert_):
+ """Return the AST statements to replace the ast.Assert instance.
+
+ This rewrites the test of an assertion to provide
+ intermediate values and replace it with an if statement which
+ raises an assertion error with a detailed explanation in case
+ the expression is false.
+
+ """
+ if isinstance(assert_.test, ast.Tuple) and self.config is not None:
+ fslocation = (self.module_path, assert_.lineno)
+ self.config.warn('R1', 'assertion is always true, perhaps '
+ 'remove parentheses?', fslocation=fslocation)
+ self.statements = []
+ self.variables = []
+ self.variable_counter = itertools.count()
+ self.stack = []
+ self.on_failure = []
+ self.push_format_context()
+ # Rewrite assert into a bunch of statements.
+ top_condition, explanation = self.visit(assert_.test)
+ # Create failure message.
+ body = self.on_failure
+ negation = ast.UnaryOp(ast.Not(), top_condition)
+ self.statements.append(ast.If(negation, body, []))
+ if assert_.msg:
+ assertmsg = self.helper('format_assertmsg', assert_.msg)
+ explanation = "\n>assert " + explanation
+ else:
+ assertmsg = ast.Str("")
+ explanation = "assert " + explanation
+ template = ast.BinOp(assertmsg, ast.Add(), ast.Str(explanation))
+ msg = self.pop_format_context(template)
+ fmt = self.helper("format_explanation", msg)
+ err_name = ast.Name("AssertionError", ast.Load())
+ exc = ast_Call(err_name, [fmt], [])
+ if sys.version_info[0] >= 3:
+ raise_ = ast.Raise(exc, None)
+ else:
+ raise_ = ast.Raise(exc, None, None)
+ body.append(raise_)
+ # Clear temporary variables by setting them to None.
+ if self.variables:
+ variables = [ast.Name(name, ast.Store())
+ for name in self.variables]
+ clear = ast.Assign(variables, _NameConstant(None))
+ self.statements.append(clear)
+ # Fix line numbers.
+ for stmt in self.statements:
+ set_location(stmt, assert_.lineno, assert_.col_offset)
+ return self.statements
+
+ def visit_Name(self, name):
+ # Display the repr of the name if it's a local variable or
+ # _should_repr_global_name() thinks it's acceptable.
+ locs = ast_Call(self.builtin("locals"), [], [])
+ inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs])
+ dorepr = self.helper("should_repr_global_name", name)
+ test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
+ expr = ast.IfExp(test, self.display(name), ast.Str(name.id))
+ return name, self.explanation_param(expr)
+
+ def visit_BoolOp(self, boolop):
+ res_var = self.variable()
+ expl_list = self.assign(ast.List([], ast.Load()))
+ app = ast.Attribute(expl_list, "append", ast.Load())
+ is_or = int(isinstance(boolop.op, ast.Or))
+ body = save = self.statements
+ fail_save = self.on_failure
+ levels = len(boolop.values) - 1
+ self.push_format_context()
+ # Process each operand, short-circuting if needed.
+ for i, v in enumerate(boolop.values):
+ if i:
+ fail_inner = []
+ # cond is set in a prior loop iteration below
+ self.on_failure.append(ast.If(cond, fail_inner, [])) # noqa
+ self.on_failure = fail_inner
+ self.push_format_context()
+ res, expl = self.visit(v)
+ body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
+ expl_format = self.pop_format_context(ast.Str(expl))
+ call = ast_Call(app, [expl_format], [])
+ self.on_failure.append(ast.Expr(call))
+ if i < levels:
+ cond = res
+ if is_or:
+ cond = ast.UnaryOp(ast.Not(), cond)
+ inner = []
+ self.statements.append(ast.If(cond, inner, []))
+ self.statements = body = inner
+ self.statements = save
+ self.on_failure = fail_save
+ expl_template = self.helper("format_boolop", expl_list, ast.Num(is_or))
+ expl = self.pop_format_context(expl_template)
+ return ast.Name(res_var, ast.Load()), self.explanation_param(expl)
+
+ def visit_UnaryOp(self, unary):
+ pattern = unary_map[unary.op.__class__]
+ operand_res, operand_expl = self.visit(unary.operand)
+ res = self.assign(ast.UnaryOp(unary.op, operand_res))
+ return res, pattern % (operand_expl,)
+
+ def visit_BinOp(self, binop):
+ symbol = binop_map[binop.op.__class__]
+ left_expr, left_expl = self.visit(binop.left)
+ right_expr, right_expl = self.visit(binop.right)
+ explanation = "(%s %s %s)" % (left_expl, symbol, right_expl)
+ res = self.assign(ast.BinOp(left_expr, binop.op, right_expr))
+ return res, explanation
+
+ def visit_Call_35(self, call):
+ """
+ visit `ast.Call` nodes on Python3.5 and after
+ """
+ new_func, func_expl = self.visit(call.func)
+ arg_expls = []
+ new_args = []
+ new_kwargs = []
+ for arg in call.args:
+ res, expl = self.visit(arg)
+ arg_expls.append(expl)
+ new_args.append(res)
+ for keyword in call.keywords:
+ res, expl = self.visit(keyword.value)
+ new_kwargs.append(ast.keyword(keyword.arg, res))
+ if keyword.arg:
+ arg_expls.append(keyword.arg + "=" + expl)
+ else: # **args have `arg` keywords with an .arg of None
+ arg_expls.append("**" + expl)
+
+ expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
+ new_call = ast.Call(new_func, new_args, new_kwargs)
+ res = self.assign(new_call)
+ res_expl = self.explanation_param(self.display(res))
+ outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
+ return res, outer_expl
+
+ def visit_Starred(self, starred):
+ # From Python 3.5, a Starred node can appear in a function call
+ res, expl = self.visit(starred.value)
+ return starred, '*' + expl
+
+ def visit_Call_legacy(self, call):
+ """
+ visit `ast.Call nodes on 3.4 and below`
+ """
+ new_func, func_expl = self.visit(call.func)
+ arg_expls = []
+ new_args = []
+ new_kwargs = []
+ new_star = new_kwarg = None
+ for arg in call.args:
+ res, expl = self.visit(arg)
+ new_args.append(res)
+ arg_expls.append(expl)
+ for keyword in call.keywords:
+ res, expl = self.visit(keyword.value)
+ new_kwargs.append(ast.keyword(keyword.arg, res))
+ arg_expls.append(keyword.arg + "=" + expl)
+ if call.starargs:
+ new_star, expl = self.visit(call.starargs)
+ arg_expls.append("*" + expl)
+ if call.kwargs:
+ new_kwarg, expl = self.visit(call.kwargs)
+ arg_expls.append("**" + expl)
+ expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
+ new_call = ast.Call(new_func, new_args, new_kwargs,
+ new_star, new_kwarg)
+ res = self.assign(new_call)
+ res_expl = self.explanation_param(self.display(res))
+ outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
+ return res, outer_expl
+
+ # ast.Call signature changed on 3.5,
+ # conditionally change which methods is named
+ # visit_Call depending on Python version
+ if sys.version_info >= (3, 5):
+ visit_Call = visit_Call_35
+ else:
+ visit_Call = visit_Call_legacy
+
+ def visit_Attribute(self, attr):
+ if not isinstance(attr.ctx, ast.Load):
+ return self.generic_visit(attr)
+ value, value_expl = self.visit(attr.value)
+ res = self.assign(ast.Attribute(value, attr.attr, ast.Load()))
+ res_expl = self.explanation_param(self.display(res))
+ pat = "%s\n{%s = %s.%s\n}"
+ expl = pat % (res_expl, res_expl, value_expl, attr.attr)
+ return res, expl
+
+ def visit_Compare(self, comp):
+ self.push_format_context()
+ left_res, left_expl = self.visit(comp.left)
+ if isinstance(comp.left, (_ast.Compare, _ast.BoolOp)):
+ left_expl = "({0})".format(left_expl)
+ res_variables = [self.variable() for i in range(len(comp.ops))]
+ load_names = [ast.Name(v, ast.Load()) for v in res_variables]
+ store_names = [ast.Name(v, ast.Store()) for v in res_variables]
+ it = zip(range(len(comp.ops)), comp.ops, comp.comparators)
+ expls = []
+ syms = []
+ results = [left_res]
+ for i, op, next_operand in it:
+ next_res, next_expl = self.visit(next_operand)
+ if isinstance(next_operand, (_ast.Compare, _ast.BoolOp)):
+ next_expl = "({0})".format(next_expl)
+ results.append(next_res)
+ sym = binop_map[op.__class__]
+ syms.append(ast.Str(sym))
+ expl = "%s %s %s" % (left_expl, sym, next_expl)
+ expls.append(ast.Str(expl))
+ res_expr = ast.Compare(left_res, [op], [next_res])
+ self.statements.append(ast.Assign([store_names[i]], res_expr))
+ left_res, left_expl = next_res, next_expl
+ # Use pytest.assertion.util._reprcompare if that's available.
+ expl_call = self.helper("call_reprcompare",
+ ast.Tuple(syms, ast.Load()),
+ ast.Tuple(load_names, ast.Load()),
+ ast.Tuple(expls, ast.Load()),
+ ast.Tuple(results, ast.Load()))
+ if len(comp.ops) > 1:
+ res = ast.BoolOp(ast.And(), load_names)
+ else:
+ res = load_names[0]
+ return res, self.explanation_param(self.pop_format_context(expl_call))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/truncate.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/truncate.py
new file mode 100644
index 00000000000..2ed12e2e5a9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/truncate.py
@@ -0,0 +1,102 @@
+"""
+Utilities for truncating assertion output.
+
+Current default behaviour is to truncate assertion explanations at
+~8 terminal lines, unless running in "-vv" mode or running on CI.
+"""
+from __future__ import absolute_import, division, print_function
+import os
+
+import six
+
+
+DEFAULT_MAX_LINES = 8
+DEFAULT_MAX_CHARS = 8 * 80
+USAGE_MSG = "use '-vv' to show"
+
+
+def truncate_if_required(explanation, item, max_length=None):
+ """
+ Truncate this assertion explanation if the given test item is eligible.
+ """
+ if _should_truncate_item(item):
+ return _truncate_explanation(explanation)
+ return explanation
+
+
+def _should_truncate_item(item):
+ """
+ Whether or not this test item is eligible for truncation.
+ """
+ verbose = item.config.option.verbose
+ return verbose < 2 and not _running_on_ci()
+
+
+def _running_on_ci():
+ """Check if we're currently running on a CI system."""
+ env_vars = ['CI', 'BUILD_NUMBER']
+ return any(var in os.environ for var in env_vars)
+
+
+def _truncate_explanation(input_lines, max_lines=None, max_chars=None):
+ """
+ Truncate given list of strings that makes up the assertion explanation.
+
+ Truncates to either 8 lines, or 640 characters - whichever the input reaches
+ first. The remaining lines will be replaced by a usage message.
+ """
+
+ if max_lines is None:
+ max_lines = DEFAULT_MAX_LINES
+ if max_chars is None:
+ max_chars = DEFAULT_MAX_CHARS
+
+ # Check if truncation required
+ input_char_count = len("".join(input_lines))
+ if len(input_lines) <= max_lines and input_char_count <= max_chars:
+ return input_lines
+
+ # Truncate first to max_lines, and then truncate to max_chars if max_chars
+ # is exceeded.
+ truncated_explanation = input_lines[:max_lines]
+ truncated_explanation = _truncate_by_char_count(truncated_explanation, max_chars)
+
+ # Add ellipsis to final line
+ truncated_explanation[-1] = truncated_explanation[-1] + "..."
+
+ # Append useful message to explanation
+ truncated_line_count = len(input_lines) - len(truncated_explanation)
+ truncated_line_count += 1 # Account for the part-truncated final line
+ msg = '...Full output truncated'
+ if truncated_line_count == 1:
+ msg += ' ({0} line hidden)'.format(truncated_line_count)
+ else:
+ msg += ' ({0} lines hidden)'.format(truncated_line_count)
+ msg += ", {0}" .format(USAGE_MSG)
+ truncated_explanation.extend([
+ six.text_type(""),
+ six.text_type(msg),
+ ])
+ return truncated_explanation
+
+
+def _truncate_by_char_count(input_lines, max_chars):
+ # Check if truncation required
+ if len("".join(input_lines)) <= max_chars:
+ return input_lines
+
+ # Find point at which input length exceeds total allowed length
+ iterated_char_count = 0
+ for iterated_index, input_line in enumerate(input_lines):
+ if iterated_char_count + len(input_line) > max_chars:
+ break
+ iterated_char_count += len(input_line)
+
+ # Create truncated explanation with modified final line
+ truncated_result = input_lines[:iterated_index]
+ final_line = input_lines[iterated_index]
+ if final_line:
+ final_line_truncate_point = max_chars - iterated_char_count
+ final_line = final_line[:final_line_truncate_point]
+ truncated_result.append(final_line)
+ return truncated_result
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/util.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/util.py
new file mode 100644
index 00000000000..511d98ef1fd
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/assertion/util.py
@@ -0,0 +1,308 @@
+"""Utilities for assertion debugging"""
+from __future__ import absolute_import, division, print_function
+import pprint
+
+import _pytest._code
+import py
+import six
+try:
+ from collections import Sequence
+except ImportError:
+ Sequence = list
+
+
+u = six.text_type
+
+# The _reprcompare attribute on the util module is used by the new assertion
+# interpretation code and assertion rewriter to detect this plugin was
+# loaded and in turn call the hooks defined here as part of the
+# DebugInterpreter.
+_reprcompare = None
+
+
+# the re-encoding is needed for python2 repr
+# with non-ascii characters (see issue 877 and 1379)
+def ecu(s):
+ try:
+ return u(s, 'utf-8', 'replace')
+ except TypeError:
+ return s
+
+
+def format_explanation(explanation):
+ """This formats an explanation
+
+ Normally all embedded newlines are escaped, however there are
+ three exceptions: \n{, \n} and \n~. The first two are intended
+ cover nested explanations, see function and attribute explanations
+ for examples (.visit_Call(), visit_Attribute()). The last one is
+ for when one explanation needs to span multiple lines, e.g. when
+ displaying diffs.
+ """
+ explanation = ecu(explanation)
+ lines = _split_explanation(explanation)
+ result = _format_lines(lines)
+ return u('\n').join(result)
+
+
+def _split_explanation(explanation):
+ """Return a list of individual lines in the explanation
+
+ This will return a list of lines split on '\n{', '\n}' and '\n~'.
+ Any other newlines will be escaped and appear in the line as the
+ literal '\n' characters.
+ """
+ raw_lines = (explanation or u('')).split('\n')
+ lines = [raw_lines[0]]
+ for values in raw_lines[1:]:
+ if values and values[0] in ['{', '}', '~', '>']:
+ lines.append(values)
+ else:
+ lines[-1] += '\\n' + values
+ return lines
+
+
+def _format_lines(lines):
+ """Format the individual lines
+
+ This will replace the '{', '}' and '~' characters of our mini
+ formatting language with the proper 'where ...', 'and ...' and ' +
+ ...' text, taking care of indentation along the way.
+
+ Return a list of formatted lines.
+ """
+ result = lines[:1]
+ stack = [0]
+ stackcnt = [0]
+ for line in lines[1:]:
+ if line.startswith('{'):
+ if stackcnt[-1]:
+ s = u('and ')
+ else:
+ s = u('where ')
+ stack.append(len(result))
+ stackcnt[-1] += 1
+ stackcnt.append(0)
+ result.append(u(' +') + u(' ') * (len(stack) - 1) + s + line[1:])
+ elif line.startswith('}'):
+ stack.pop()
+ stackcnt.pop()
+ result[stack[-1]] += line[1:]
+ else:
+ assert line[0] in ['~', '>']
+ stack[-1] += 1
+ indent = len(stack) if line.startswith('~') else len(stack) - 1
+ result.append(u(' ') * indent + line[1:])
+ assert len(stack) == 1
+ return result
+
+
+# Provide basestring in python3
+try:
+ basestring = basestring
+except NameError:
+ basestring = str
+
+
+def assertrepr_compare(config, op, left, right):
+ """Return specialised explanations for some operators/operands"""
+ width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op
+ left_repr = py.io.saferepr(left, maxsize=int(width // 2))
+ right_repr = py.io.saferepr(right, maxsize=width - len(left_repr))
+
+ summary = u('%s %s %s') % (ecu(left_repr), op, ecu(right_repr))
+
+ def issequence(x):
+ return (isinstance(x, (list, tuple, Sequence)) and not isinstance(x, basestring))
+
+ def istext(x):
+ return isinstance(x, basestring)
+
+ def isdict(x):
+ return isinstance(x, dict)
+
+ def isset(x):
+ return isinstance(x, (set, frozenset))
+
+ def isiterable(obj):
+ try:
+ iter(obj)
+ return not istext(obj)
+ except TypeError:
+ return False
+
+ verbose = config.getoption('verbose')
+ explanation = None
+ try:
+ if op == '==':
+ if istext(left) and istext(right):
+ explanation = _diff_text(left, right, verbose)
+ else:
+ if issequence(left) and issequence(right):
+ explanation = _compare_eq_sequence(left, right, verbose)
+ elif isset(left) and isset(right):
+ explanation = _compare_eq_set(left, right, verbose)
+ elif isdict(left) and isdict(right):
+ explanation = _compare_eq_dict(left, right, verbose)
+ if isiterable(left) and isiterable(right):
+ expl = _compare_eq_iterable(left, right, verbose)
+ if explanation is not None:
+ explanation.extend(expl)
+ else:
+ explanation = expl
+ elif op == 'not in':
+ if istext(left) and istext(right):
+ explanation = _notin_text(left, right, verbose)
+ except Exception:
+ explanation = [
+ u('(pytest_assertion plugin: representation of details failed. '
+ 'Probably an object has a faulty __repr__.)'),
+ u(_pytest._code.ExceptionInfo())]
+
+ if not explanation:
+ return None
+
+ return [summary] + explanation
+
+
+def _diff_text(left, right, verbose=False):
+ """Return the explanation for the diff between text or bytes
+
+ Unless --verbose is used this will skip leading and trailing
+ characters which are identical to keep the diff minimal.
+
+ If the input are bytes they will be safely converted to text.
+ """
+ from difflib import ndiff
+ explanation = []
+ if isinstance(left, six.binary_type):
+ left = u(repr(left)[1:-1]).replace(r'\n', '\n')
+ if isinstance(right, six.binary_type):
+ right = u(repr(right)[1:-1]).replace(r'\n', '\n')
+ if not verbose:
+ i = 0 # just in case left or right has zero length
+ for i in range(min(len(left), len(right))):
+ if left[i] != right[i]:
+ break
+ if i > 42:
+ i -= 10 # Provide some context
+ explanation = [u('Skipping %s identical leading '
+ 'characters in diff, use -v to show') % i]
+ left = left[i:]
+ right = right[i:]
+ if len(left) == len(right):
+ for i in range(len(left)):
+ if left[-i] != right[-i]:
+ break
+ if i > 42:
+ i -= 10 # Provide some context
+ explanation += [u('Skipping %s identical trailing '
+ 'characters in diff, use -v to show') % i]
+ left = left[:-i]
+ right = right[:-i]
+ keepends = True
+ explanation += [line.strip('\n')
+ for line in ndiff(left.splitlines(keepends),
+ right.splitlines(keepends))]
+ return explanation
+
+
+def _compare_eq_iterable(left, right, verbose=False):
+ if not verbose:
+ return [u('Use -v to get the full diff')]
+ # dynamic import to speedup pytest
+ import difflib
+
+ try:
+ left_formatting = pprint.pformat(left).splitlines()
+ right_formatting = pprint.pformat(right).splitlines()
+ explanation = [u('Full diff:')]
+ except Exception:
+ # hack: PrettyPrinter.pformat() in python 2 fails when formatting items that can't be sorted(), ie, calling
+ # sorted() on a list would raise. See issue #718.
+ # As a workaround, the full diff is generated by using the repr() string of each item of each container.
+ left_formatting = sorted(repr(x) for x in left)
+ right_formatting = sorted(repr(x) for x in right)
+ explanation = [u('Full diff (fallback to calling repr on each item):')]
+ explanation.extend(line.strip() for line in difflib.ndiff(left_formatting, right_formatting))
+ return explanation
+
+
+def _compare_eq_sequence(left, right, verbose=False):
+ explanation = []
+ for i in range(min(len(left), len(right))):
+ if left[i] != right[i]:
+ explanation += [u('At index %s diff: %r != %r')
+ % (i, left[i], right[i])]
+ break
+ if len(left) > len(right):
+ explanation += [u('Left contains more items, first extra item: %s')
+ % py.io.saferepr(left[len(right)],)]
+ elif len(left) < len(right):
+ explanation += [
+ u('Right contains more items, first extra item: %s') %
+ py.io.saferepr(right[len(left)],)]
+ return explanation
+
+
+def _compare_eq_set(left, right, verbose=False):
+ explanation = []
+ diff_left = left - right
+ diff_right = right - left
+ if diff_left:
+ explanation.append(u('Extra items in the left set:'))
+ for item in diff_left:
+ explanation.append(py.io.saferepr(item))
+ if diff_right:
+ explanation.append(u('Extra items in the right set:'))
+ for item in diff_right:
+ explanation.append(py.io.saferepr(item))
+ return explanation
+
+
+def _compare_eq_dict(left, right, verbose=False):
+ explanation = []
+ common = set(left).intersection(set(right))
+ same = dict((k, left[k]) for k in common if left[k] == right[k])
+ if same and verbose < 2:
+ explanation += [u('Omitting %s identical items, use -vv to show') %
+ len(same)]
+ elif same:
+ explanation += [u('Common items:')]
+ explanation += pprint.pformat(same).splitlines()
+ diff = set(k for k in common if left[k] != right[k])
+ if diff:
+ explanation += [u('Differing items:')]
+ for k in diff:
+ explanation += [py.io.saferepr({k: left[k]}) + ' != ' +
+ py.io.saferepr({k: right[k]})]
+ extra_left = set(left) - set(right)
+ if extra_left:
+ explanation.append(u('Left contains more items:'))
+ explanation.extend(pprint.pformat(
+ dict((k, left[k]) for k in extra_left)).splitlines())
+ extra_right = set(right) - set(left)
+ if extra_right:
+ explanation.append(u('Right contains more items:'))
+ explanation.extend(pprint.pformat(
+ dict((k, right[k]) for k in extra_right)).splitlines())
+ return explanation
+
+
+def _notin_text(term, text, verbose=False):
+ index = text.find(term)
+ head = text[:index]
+ tail = text[index + len(term):]
+ correct_text = head + tail
+ diff = _diff_text(correct_text, text, verbose)
+ newdiff = [u('%s is contained here:') % py.io.saferepr(term, maxsize=42)]
+ for line in diff:
+ if line.startswith(u('Skipping')):
+ continue
+ if line.startswith(u('- ')):
+ continue
+ if line.startswith(u('+ ')):
+ newdiff.append(u(' ') + line[2:])
+ else:
+ newdiff.append(line)
+ return newdiff
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/cacheprovider.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/cacheprovider.py
new file mode 100755
index 00000000000..c537c14472b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/cacheprovider.py
@@ -0,0 +1,260 @@
+"""
+merged implementation of the cache provider
+
+the name cache was not chosen to ensure pluggy automatically
+ignores the external pytest-cache
+"""
+from __future__ import absolute_import, division, print_function
+import py
+import pytest
+import json
+import os
+from os.path import sep as _sep, altsep as _altsep
+
+
+class Cache(object):
+ def __init__(self, config):
+ self.config = config
+ self._cachedir = Cache.cache_dir_from_config(config)
+ self.trace = config.trace.root.get("cache")
+ if config.getvalue("cacheclear"):
+ self.trace("clearing cachedir")
+ if self._cachedir.check():
+ self._cachedir.remove()
+ self._cachedir.mkdir()
+
+ @staticmethod
+ def cache_dir_from_config(config):
+ cache_dir = config.getini("cache_dir")
+ cache_dir = os.path.expanduser(cache_dir)
+ cache_dir = os.path.expandvars(cache_dir)
+ if os.path.isabs(cache_dir):
+ return py.path.local(cache_dir)
+ else:
+ return config.rootdir.join(cache_dir)
+
+ def makedir(self, name):
+ """ return a directory path object with the given name. If the
+ directory does not yet exist, it will be created. You can use it
+ to manage files likes e. g. store/retrieve database
+ dumps across test sessions.
+
+ :param name: must be a string not containing a ``/`` separator.
+ Make sure the name contains your plugin or application
+ identifiers to prevent clashes with other cache users.
+ """
+ if _sep in name or _altsep is not None and _altsep in name:
+ raise ValueError("name is not allowed to contain path separators")
+ return self._cachedir.ensure_dir("d", name)
+
+ def _getvaluepath(self, key):
+ return self._cachedir.join('v', *key.split('/'))
+
+ def get(self, key, default):
+ """ return cached value for the given key. If no value
+ was yet cached or the value cannot be read, the specified
+ default is returned.
+
+ :param key: must be a ``/`` separated value. Usually the first
+ name is the name of your plugin or your application.
+ :param default: must be provided in case of a cache-miss or
+ invalid cache values.
+
+ """
+ path = self._getvaluepath(key)
+ if path.check():
+ try:
+ with path.open("r") as f:
+ return json.load(f)
+ except ValueError:
+ self.trace("cache-invalid at %s" % (path,))
+ return default
+
+ def set(self, key, value):
+ """ save value for the given key.
+
+ :param key: must be a ``/`` separated value. Usually the first
+ name is the name of your plugin or your application.
+ :param value: must be of any combination of basic
+ python types, including nested types
+ like e. g. lists of dictionaries.
+ """
+ path = self._getvaluepath(key)
+ try:
+ path.dirpath().ensure_dir()
+ except (py.error.EEXIST, py.error.EACCES):
+ self.config.warn(
+ code='I9', message='could not create cache path %s' % (path,)
+ )
+ return
+ try:
+ f = path.open('w')
+ except py.error.ENOTDIR:
+ self.config.warn(
+ code='I9', message='cache could not write path %s' % (path,))
+ else:
+ with f:
+ self.trace("cache-write %s: %r" % (key, value,))
+ json.dump(value, f, indent=2, sort_keys=True)
+
+
+class LFPlugin:
+ """ Plugin which implements the --lf (run last-failing) option """
+
+ def __init__(self, config):
+ self.config = config
+ active_keys = 'lf', 'failedfirst'
+ self.active = any(config.getvalue(key) for key in active_keys)
+ self.lastfailed = config.cache.get("cache/lastfailed", {})
+ self._previously_failed_count = None
+
+ def pytest_report_collectionfinish(self):
+ if self.active:
+ if not self._previously_failed_count:
+ mode = "run all (no recorded failures)"
+ else:
+ noun = 'failure' if self._previously_failed_count == 1 else 'failures'
+ suffix = " first" if self.config.getvalue("failedfirst") else ""
+ mode = "rerun previous {count} {noun}{suffix}".format(
+ count=self._previously_failed_count, suffix=suffix, noun=noun
+ )
+ return "run-last-failure: %s" % mode
+
+ def pytest_runtest_logreport(self, report):
+ if (report.when == 'call' and report.passed) or report.skipped:
+ self.lastfailed.pop(report.nodeid, None)
+ elif report.failed:
+ self.lastfailed[report.nodeid] = True
+
+ def pytest_collectreport(self, report):
+ passed = report.outcome in ('passed', 'skipped')
+ if passed:
+ if report.nodeid in self.lastfailed:
+ self.lastfailed.pop(report.nodeid)
+ self.lastfailed.update(
+ (item.nodeid, True)
+ for item in report.result)
+ else:
+ self.lastfailed[report.nodeid] = True
+
+ def pytest_collection_modifyitems(self, session, config, items):
+ if self.active and self.lastfailed:
+ previously_failed = []
+ previously_passed = []
+ for item in items:
+ if item.nodeid in self.lastfailed:
+ previously_failed.append(item)
+ else:
+ previously_passed.append(item)
+ self._previously_failed_count = len(previously_failed)
+ if not previously_failed:
+ # running a subset of all tests with recorded failures outside
+ # of the set of tests currently executing
+ return
+ if self.config.getvalue("lf"):
+ items[:] = previously_failed
+ config.hook.pytest_deselected(items=previously_passed)
+ else:
+ items[:] = previously_failed + previously_passed
+
+ def pytest_sessionfinish(self, session):
+ config = self.config
+ if config.getvalue("cacheshow") or hasattr(config, "slaveinput"):
+ return
+
+ saved_lastfailed = config.cache.get("cache/lastfailed", {})
+ if saved_lastfailed != self.lastfailed:
+ config.cache.set("cache/lastfailed", self.lastfailed)
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("general")
+ group.addoption(
+ '--lf', '--last-failed', action='store_true', dest="lf",
+ help="rerun only the tests that failed "
+ "at the last run (or all if none failed)")
+ group.addoption(
+ '--ff', '--failed-first', action='store_true', dest="failedfirst",
+ help="run all tests but run the last failures first. "
+ "This may re-order tests and thus lead to "
+ "repeated fixture setup/teardown")
+ group.addoption(
+ '--cache-show', action='store_true', dest="cacheshow",
+ help="show cache contents, don't perform collection or tests")
+ group.addoption(
+ '--cache-clear', action='store_true', dest="cacheclear",
+ help="remove all cache contents at start of test run.")
+ parser.addini(
+ "cache_dir", default='.cache',
+ help="cache directory path.")
+
+
+def pytest_cmdline_main(config):
+ if config.option.cacheshow:
+ from _pytest.main import wrap_session
+ return wrap_session(config, cacheshow)
+
+
+@pytest.hookimpl(tryfirst=True)
+def pytest_configure(config):
+ config.cache = Cache(config)
+ config.pluginmanager.register(LFPlugin(config), "lfplugin")
+
+
+@pytest.fixture
+def cache(request):
+ """
+ Return a cache object that can persist state between testing sessions.
+
+ cache.get(key, default)
+ cache.set(key, value)
+
+ Keys must be a ``/`` separated value, where the first part is usually the
+ name of your plugin or application to avoid clashes with other cache users.
+
+ Values can be any object handled by the json stdlib module.
+ """
+ return request.config.cache
+
+
+def pytest_report_header(config):
+ if config.option.verbose:
+ relpath = py.path.local().bestrelpath(config.cache._cachedir)
+ return "cachedir: %s" % relpath
+
+
+def cacheshow(config, session):
+ from pprint import pprint
+ tw = py.io.TerminalWriter()
+ tw.line("cachedir: " + str(config.cache._cachedir))
+ if not config.cache._cachedir.check():
+ tw.line("cache is empty")
+ return 0
+ dummy = object()
+ basedir = config.cache._cachedir
+ vdir = basedir.join("v")
+ tw.sep("-", "cache values")
+ for valpath in sorted(vdir.visit(lambda x: x.isfile())):
+ key = valpath.relto(vdir).replace(valpath.sep, "/")
+ val = config.cache.get(key, dummy)
+ if val is dummy:
+ tw.line("%s contains unreadable content, "
+ "will be ignored" % key)
+ else:
+ tw.line("%s contains:" % key)
+ stream = py.io.TextIO()
+ pprint(val, stream=stream)
+ for line in stream.getvalue().splitlines():
+ tw.line(" " + line)
+
+ ddir = basedir.join("d")
+ if ddir.isdir() and ddir.listdir():
+ tw.sep("-", "cache directories")
+ for p in sorted(basedir.join("d").visit()):
+ # if p.check(dir=1):
+ # print("%s/" % p.relto(basedir))
+ if p.isfile():
+ key = p.relto(basedir)
+ tw.line("%s is a file of length %d" % (
+ key, p.size()))
+ return 0
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/capture.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/capture.py
new file mode 100644
index 00000000000..f2ebe38c8c0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/capture.py
@@ -0,0 +1,683 @@
+"""
+per-test stdout/stderr capturing mechanism.
+
+"""
+from __future__ import absolute_import, division, print_function
+
+import collections
+import contextlib
+import sys
+import os
+import io
+from io import UnsupportedOperation
+from tempfile import TemporaryFile
+
+import six
+import pytest
+from _pytest.compat import CaptureIO
+
+
+patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'}
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("general")
+ group._addoption(
+ '--capture', action="store",
+ default="fd" if hasattr(os, "dup") else "sys",
+ metavar="method", choices=['fd', 'sys', 'no'],
+ help="per-test capturing method: one of fd|sys|no.")
+ group._addoption(
+ '-s', action="store_const", const="no", dest="capture",
+ help="shortcut for --capture=no.")
+
+
+@pytest.hookimpl(hookwrapper=True)
+def pytest_load_initial_conftests(early_config, parser, args):
+ ns = early_config.known_args_namespace
+ if ns.capture == "fd":
+ _py36_windowsconsoleio_workaround(sys.stdout)
+ _colorama_workaround()
+ _readline_workaround()
+ pluginmanager = early_config.pluginmanager
+ capman = CaptureManager(ns.capture)
+ pluginmanager.register(capman, "capturemanager")
+
+ # make sure that capturemanager is properly reset at final shutdown
+ early_config.add_cleanup(capman.stop_global_capturing)
+
+ # make sure logging does not raise exceptions at the end
+ def silence_logging_at_shutdown():
+ if "logging" in sys.modules:
+ sys.modules["logging"].raiseExceptions = False
+ early_config.add_cleanup(silence_logging_at_shutdown)
+
+ # finally trigger conftest loading but while capturing (issue93)
+ capman.start_global_capturing()
+ outcome = yield
+ out, err = capman.suspend_global_capture()
+ if outcome.excinfo is not None:
+ sys.stdout.write(out)
+ sys.stderr.write(err)
+
+
+class CaptureManager:
+ """
+ Capture plugin, manages that the appropriate capture method is enabled/disabled during collection and each
+ test phase (setup, call, teardown). After each of those points, the captured output is obtained and
+ attached to the collection/runtest report.
+
+ There are two levels of capture:
+ * global: which is enabled by default and can be suppressed by the ``-s`` option. This is always enabled/disabled
+ during collection and each test phase.
+ * fixture: when a test function or one of its fixture depend on the ``capsys`` or ``capfd`` fixtures. In this
+ case special handling is needed to ensure the fixtures take precedence over the global capture.
+ """
+
+ def __init__(self, method):
+ self._method = method
+ self._global_capturing = None
+
+ def _getcapture(self, method):
+ if method == "fd":
+ return MultiCapture(out=True, err=True, Capture=FDCapture)
+ elif method == "sys":
+ return MultiCapture(out=True, err=True, Capture=SysCapture)
+ elif method == "no":
+ return MultiCapture(out=False, err=False, in_=False)
+ else:
+ raise ValueError("unknown capturing method: %r" % method)
+
+ def start_global_capturing(self):
+ assert self._global_capturing is None
+ self._global_capturing = self._getcapture(self._method)
+ self._global_capturing.start_capturing()
+
+ def stop_global_capturing(self):
+ if self._global_capturing is not None:
+ self._global_capturing.pop_outerr_to_orig()
+ self._global_capturing.stop_capturing()
+ self._global_capturing = None
+
+ def resume_global_capture(self):
+ self._global_capturing.resume_capturing()
+
+ def suspend_global_capture(self, item=None, in_=False):
+ if item is not None:
+ self.deactivate_fixture(item)
+ cap = getattr(self, "_global_capturing", None)
+ if cap is not None:
+ try:
+ outerr = cap.readouterr()
+ finally:
+ cap.suspend_capturing(in_=in_)
+ return outerr
+
+ def activate_fixture(self, item):
+ """If the current item is using ``capsys`` or ``capfd``, activate them so they take precedence over
+ the global capture.
+ """
+ fixture = getattr(item, "_capture_fixture", None)
+ if fixture is not None:
+ fixture._start()
+
+ def deactivate_fixture(self, item):
+ """Deactivates the ``capsys`` or ``capfd`` fixture of this item, if any."""
+ fixture = getattr(item, "_capture_fixture", None)
+ if fixture is not None:
+ fixture.close()
+
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_make_collect_report(self, collector):
+ if isinstance(collector, pytest.File):
+ self.resume_global_capture()
+ outcome = yield
+ out, err = self.suspend_global_capture()
+ rep = outcome.get_result()
+ if out:
+ rep.sections.append(("Captured stdout", out))
+ if err:
+ rep.sections.append(("Captured stderr", err))
+ else:
+ yield
+
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_runtest_setup(self, item):
+ self.resume_global_capture()
+ # no need to activate a capture fixture because they activate themselves during creation; this
+ # only makes sense when a fixture uses a capture fixture, otherwise the capture fixture will
+ # be activated during pytest_runtest_call
+ yield
+ self.suspend_capture_item(item, "setup")
+
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_runtest_call(self, item):
+ self.resume_global_capture()
+ # it is important to activate this fixture during the call phase so it overwrites the "global"
+ # capture
+ self.activate_fixture(item)
+ yield
+ self.suspend_capture_item(item, "call")
+
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_runtest_teardown(self, item):
+ self.resume_global_capture()
+ self.activate_fixture(item)
+ yield
+ self.suspend_capture_item(item, "teardown")
+
+ @pytest.hookimpl(tryfirst=True)
+ def pytest_keyboard_interrupt(self, excinfo):
+ self.stop_global_capturing()
+
+ @pytest.hookimpl(tryfirst=True)
+ def pytest_internalerror(self, excinfo):
+ self.stop_global_capturing()
+
+ def suspend_capture_item(self, item, when, in_=False):
+ out, err = self.suspend_global_capture(item, in_=in_)
+ item.add_report_section(when, "stdout", out)
+ item.add_report_section(when, "stderr", err)
+
+
+capture_fixtures = {'capfd', 'capfdbinary', 'capsys', 'capsysbinary'}
+
+
+def _ensure_only_one_capture_fixture(request, name):
+ fixtures = set(request.fixturenames) & capture_fixtures - set((name,))
+ if fixtures:
+ fixtures = sorted(fixtures)
+ fixtures = fixtures[0] if len(fixtures) == 1 else fixtures
+ raise request.raiseerror(
+ "cannot use {0} and {1} at the same time".format(
+ fixtures, name,
+ ),
+ )
+
+
+@pytest.fixture
+def capsys(request):
+ """Enable capturing of writes to sys.stdout/sys.stderr and make
+ captured output available via ``capsys.readouterr()`` method calls
+ which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text``
+ objects.
+ """
+ _ensure_only_one_capture_fixture(request, 'capsys')
+ with _install_capture_fixture_on_item(request, SysCapture) as fixture:
+ yield fixture
+
+
+@pytest.fixture
+def capsysbinary(request):
+ """Enable capturing of writes to sys.stdout/sys.stderr and make
+ captured output available via ``capsys.readouterr()`` method calls
+ which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``bytes``
+ objects.
+ """
+ _ensure_only_one_capture_fixture(request, 'capsysbinary')
+ # Currently, the implementation uses the python3 specific `.buffer`
+ # property of CaptureIO.
+ if sys.version_info < (3,):
+ raise request.raiseerror('capsysbinary is only supported on python 3')
+ with _install_capture_fixture_on_item(request, SysCaptureBinary) as fixture:
+ yield fixture
+
+
+@pytest.fixture
+def capfd(request):
+ """Enable capturing of writes to file descriptors 1 and 2 and make
+ captured output available via ``capfd.readouterr()`` method calls
+ which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text``
+ objects.
+ """
+ _ensure_only_one_capture_fixture(request, 'capfd')
+ if not hasattr(os, 'dup'):
+ pytest.skip("capfd fixture needs os.dup function which is not available in this system")
+ with _install_capture_fixture_on_item(request, FDCapture) as fixture:
+ yield fixture
+
+
+@pytest.fixture
+def capfdbinary(request):
+ """Enable capturing of write to file descriptors 1 and 2 and make
+ captured output available via ``capfdbinary.readouterr`` method calls
+ which return a ``(out, err)`` tuple. ``out`` and ``err`` will be
+ ``bytes`` objects.
+ """
+ _ensure_only_one_capture_fixture(request, 'capfdbinary')
+ if not hasattr(os, 'dup'):
+ pytest.skip("capfdbinary fixture needs os.dup function which is not available in this system")
+ with _install_capture_fixture_on_item(request, FDCaptureBinary) as fixture:
+ yield fixture
+
+
+@contextlib.contextmanager
+def _install_capture_fixture_on_item(request, capture_class):
+ """
+ Context manager which creates a ``CaptureFixture`` instance and "installs" it on
+ the item/node of the given request. Used by ``capsys`` and ``capfd``.
+
+ The CaptureFixture is added as attribute of the item because it needs to accessed
+ by ``CaptureManager`` during its ``pytest_runtest_*`` hooks.
+ """
+ request.node._capture_fixture = fixture = CaptureFixture(capture_class, request)
+ capmanager = request.config.pluginmanager.getplugin('capturemanager')
+ # need to active this fixture right away in case it is being used by another fixture (setup phase)
+ # if this fixture is being used only by a test function (call phase), then we wouldn't need this
+ # activation, but it doesn't hurt
+ capmanager.activate_fixture(request.node)
+ yield fixture
+ fixture.close()
+ del request.node._capture_fixture
+
+
+class CaptureFixture:
+ def __init__(self, captureclass, request):
+ self.captureclass = captureclass
+ self.request = request
+
+ def _start(self):
+ self._capture = MultiCapture(out=True, err=True, in_=False,
+ Capture=self.captureclass)
+ self._capture.start_capturing()
+
+ def close(self):
+ cap = self.__dict__.pop("_capture", None)
+ if cap is not None:
+ self._outerr = cap.pop_outerr_to_orig()
+ cap.stop_capturing()
+
+ def readouterr(self):
+ try:
+ return self._capture.readouterr()
+ except AttributeError:
+ return self._outerr
+
+ @contextlib.contextmanager
+ def disabled(self):
+ self._capture.suspend_capturing()
+ capmanager = self.request.config.pluginmanager.getplugin('capturemanager')
+ capmanager.suspend_global_capture(item=None, in_=False)
+ try:
+ yield
+ finally:
+ capmanager.resume_global_capture()
+ self._capture.resume_capturing()
+
+
+def safe_text_dupfile(f, mode, default_encoding="UTF8"):
+ """ return a open text file object that's a duplicate of f on the
+ FD-level if possible.
+ """
+ encoding = getattr(f, "encoding", None)
+ try:
+ fd = f.fileno()
+ except Exception:
+ if "b" not in getattr(f, "mode", "") and hasattr(f, "encoding"):
+ # we seem to have a text stream, let's just use it
+ return f
+ else:
+ newfd = os.dup(fd)
+ if "b" not in mode:
+ mode += "b"
+ f = os.fdopen(newfd, mode, 0) # no buffering
+ return EncodedFile(f, encoding or default_encoding)
+
+
+class EncodedFile(object):
+ errors = "strict" # possibly needed by py3 code (issue555)
+
+ def __init__(self, buffer, encoding):
+ self.buffer = buffer
+ self.encoding = encoding
+
+ def write(self, obj):
+ if isinstance(obj, six.text_type):
+ obj = obj.encode(self.encoding, "replace")
+ self.buffer.write(obj)
+
+ def writelines(self, linelist):
+ data = ''.join(linelist)
+ self.write(data)
+
+ @property
+ def name(self):
+ """Ensure that file.name is a string."""
+ return repr(self.buffer)
+
+ def __getattr__(self, name):
+ return getattr(object.__getattribute__(self, "buffer"), name)
+
+
+CaptureResult = collections.namedtuple("CaptureResult", ["out", "err"])
+
+
+class MultiCapture(object):
+ out = err = in_ = None
+
+ def __init__(self, out=True, err=True, in_=True, Capture=None):
+ if in_:
+ self.in_ = Capture(0)
+ if out:
+ self.out = Capture(1)
+ if err:
+ self.err = Capture(2)
+
+ def start_capturing(self):
+ if self.in_:
+ self.in_.start()
+ if self.out:
+ self.out.start()
+ if self.err:
+ self.err.start()
+
+ def pop_outerr_to_orig(self):
+ """ pop current snapshot out/err capture and flush to orig streams. """
+ out, err = self.readouterr()
+ if out:
+ self.out.writeorg(out)
+ if err:
+ self.err.writeorg(err)
+ return out, err
+
+ def suspend_capturing(self, in_=False):
+ if self.out:
+ self.out.suspend()
+ if self.err:
+ self.err.suspend()
+ if in_ and self.in_:
+ self.in_.suspend()
+ self._in_suspended = True
+
+ def resume_capturing(self):
+ if self.out:
+ self.out.resume()
+ if self.err:
+ self.err.resume()
+ if hasattr(self, "_in_suspended"):
+ self.in_.resume()
+ del self._in_suspended
+
+ def stop_capturing(self):
+ """ stop capturing and reset capturing streams """
+ if hasattr(self, '_reset'):
+ raise ValueError("was already stopped")
+ self._reset = True
+ if self.out:
+ self.out.done()
+ if self.err:
+ self.err.done()
+ if self.in_:
+ self.in_.done()
+
+ def readouterr(self):
+ """ return snapshot unicode value of stdout/stderr capturings. """
+ return CaptureResult(self.out.snap() if self.out is not None else "",
+ self.err.snap() if self.err is not None else "")
+
+
+class NoCapture:
+ __init__ = start = done = suspend = resume = lambda *args: None
+
+
+class FDCaptureBinary:
+ """Capture IO to/from a given os-level filedescriptor.
+
+ snap() produces `bytes`
+ """
+
+ def __init__(self, targetfd, tmpfile=None):
+ self.targetfd = targetfd
+ try:
+ self.targetfd_save = os.dup(self.targetfd)
+ except OSError:
+ self.start = lambda: None
+ self.done = lambda: None
+ else:
+ if targetfd == 0:
+ assert not tmpfile, "cannot set tmpfile with stdin"
+ tmpfile = open(os.devnull, "r")
+ self.syscapture = SysCapture(targetfd)
+ else:
+ if tmpfile is None:
+ f = TemporaryFile()
+ with f:
+ tmpfile = safe_text_dupfile(f, mode="wb+")
+ if targetfd in patchsysdict:
+ self.syscapture = SysCapture(targetfd, tmpfile)
+ else:
+ self.syscapture = NoCapture()
+ self.tmpfile = tmpfile
+ self.tmpfile_fd = tmpfile.fileno()
+
+ def __repr__(self):
+ return "<FDCapture %s oldfd=%s>" % (self.targetfd, self.targetfd_save)
+
+ def start(self):
+ """ Start capturing on targetfd using memorized tmpfile. """
+ try:
+ os.fstat(self.targetfd_save)
+ except (AttributeError, OSError):
+ raise ValueError("saved filedescriptor not valid anymore")
+ os.dup2(self.tmpfile_fd, self.targetfd)
+ self.syscapture.start()
+
+ def snap(self):
+ self.tmpfile.seek(0)
+ res = self.tmpfile.read()
+ self.tmpfile.seek(0)
+ self.tmpfile.truncate()
+ return res
+
+ def done(self):
+ """ stop capturing, restore streams, return original capture file,
+ seeked to position zero. """
+ targetfd_save = self.__dict__.pop("targetfd_save")
+ os.dup2(targetfd_save, self.targetfd)
+ os.close(targetfd_save)
+ self.syscapture.done()
+ self.tmpfile.close()
+
+ def suspend(self):
+ self.syscapture.suspend()
+ os.dup2(self.targetfd_save, self.targetfd)
+
+ def resume(self):
+ self.syscapture.resume()
+ os.dup2(self.tmpfile_fd, self.targetfd)
+
+ def writeorg(self, data):
+ """ write to original file descriptor. """
+ if isinstance(data, six.text_type):
+ data = data.encode("utf8") # XXX use encoding of original stream
+ os.write(self.targetfd_save, data)
+
+
+class FDCapture(FDCaptureBinary):
+ """Capture IO to/from a given os-level filedescriptor.
+
+ snap() produces text
+ """
+ def snap(self):
+ res = FDCaptureBinary.snap(self)
+ enc = getattr(self.tmpfile, "encoding", None)
+ if enc and isinstance(res, bytes):
+ res = six.text_type(res, enc, "replace")
+ return res
+
+
+class SysCapture:
+ def __init__(self, fd, tmpfile=None):
+ name = patchsysdict[fd]
+ self._old = getattr(sys, name)
+ self.name = name
+ if tmpfile is None:
+ if name == "stdin":
+ tmpfile = DontReadFromInput()
+ else:
+ tmpfile = CaptureIO()
+ self.tmpfile = tmpfile
+
+ def start(self):
+ setattr(sys, self.name, self.tmpfile)
+
+ def snap(self):
+ res = self.tmpfile.getvalue()
+ self.tmpfile.seek(0)
+ self.tmpfile.truncate()
+ return res
+
+ def done(self):
+ setattr(sys, self.name, self._old)
+ del self._old
+ self.tmpfile.close()
+
+ def suspend(self):
+ setattr(sys, self.name, self._old)
+
+ def resume(self):
+ setattr(sys, self.name, self.tmpfile)
+
+ def writeorg(self, data):
+ self._old.write(data)
+ self._old.flush()
+
+
+class SysCaptureBinary(SysCapture):
+ def snap(self):
+ res = self.tmpfile.buffer.getvalue()
+ self.tmpfile.seek(0)
+ self.tmpfile.truncate()
+ return res
+
+
+class DontReadFromInput:
+ """Temporary stub class. Ideally when stdin is accessed, the
+ capturing should be turned off, with possibly all data captured
+ so far sent to the screen. This should be configurable, though,
+ because in automated test runs it is better to crash than
+ hang indefinitely.
+ """
+
+ encoding = None
+
+ def read(self, *args):
+ raise IOError("reading from stdin while output is captured")
+ readline = read
+ readlines = read
+ __iter__ = read
+
+ def fileno(self):
+ raise UnsupportedOperation("redirected stdin is pseudofile, "
+ "has no fileno()")
+
+ def isatty(self):
+ return False
+
+ def close(self):
+ pass
+
+ @property
+ def buffer(self):
+ if sys.version_info >= (3, 0):
+ return self
+ else:
+ raise AttributeError('redirected stdin has no attribute buffer')
+
+
+def _colorama_workaround():
+ """
+ Ensure colorama is imported so that it attaches to the correct stdio
+ handles on Windows.
+
+ colorama uses the terminal on import time. So if something does the
+ first import of colorama while I/O capture is active, colorama will
+ fail in various ways.
+ """
+
+ if not sys.platform.startswith('win32'):
+ return
+ try:
+ import colorama # noqa
+ except ImportError:
+ pass
+
+
+def _readline_workaround():
+ """
+ Ensure readline is imported so that it attaches to the correct stdio
+ handles on Windows.
+
+ Pdb uses readline support where available--when not running from the Python
+ prompt, the readline module is not imported until running the pdb REPL. If
+ running pytest with the --pdb option this means the readline module is not
+ imported until after I/O capture has been started.
+
+ This is a problem for pyreadline, which is often used to implement readline
+ support on Windows, as it does not attach to the correct handles for stdout
+ and/or stdin if they have been redirected by the FDCapture mechanism. This
+ workaround ensures that readline is imported before I/O capture is setup so
+ that it can attach to the actual stdin/out for the console.
+
+ See https://github.com/pytest-dev/pytest/pull/1281
+ """
+
+ if not sys.platform.startswith('win32'):
+ return
+ try:
+ import readline # noqa
+ except ImportError:
+ pass
+
+
+def _py36_windowsconsoleio_workaround(stream):
+ """
+ Python 3.6 implemented unicode console handling for Windows. This works
+ by reading/writing to the raw console handle using
+ ``{Read,Write}ConsoleW``.
+
+ The problem is that we are going to ``dup2`` over the stdio file
+ descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the
+ handles used by Python to write to the console. Though there is still some
+ weirdness and the console handle seems to only be closed randomly and not
+ on the first call to ``CloseHandle``, or maybe it gets reopened with the
+ same handle value when we suspend capturing.
+
+ The workaround in this case will reopen stdio with a different fd which
+ also means a different handle by replicating the logic in
+ "Py_lifecycle.c:initstdio/create_stdio".
+
+ :param stream: in practice ``sys.stdout`` or ``sys.stderr``, but given
+ here as parameter for unittesting purposes.
+
+ See https://github.com/pytest-dev/py/issues/103
+ """
+ if not sys.platform.startswith('win32') or sys.version_info[:2] < (3, 6):
+ return
+
+ # bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666)
+ if not hasattr(stream, 'buffer'):
+ return
+
+ buffered = hasattr(stream.buffer, 'raw')
+ raw_stdout = stream.buffer.raw if buffered else stream.buffer
+
+ if not isinstance(raw_stdout, io._WindowsConsoleIO):
+ return
+
+ def _reopen_stdio(f, mode):
+ if not buffered and mode[0] == 'w':
+ buffering = 0
+ else:
+ buffering = -1
+
+ return io.TextIOWrapper(
+ open(os.dup(f.fileno()), mode, buffering),
+ f.encoding,
+ f.errors,
+ f.newlines,
+ f.line_buffering)
+
+ sys.__stdin__ = sys.stdin = _reopen_stdio(sys.stdin, 'rb')
+ sys.__stdout__ = sys.stdout = _reopen_stdio(sys.stdout, 'wb')
+ sys.__stderr__ = sys.stderr = _reopen_stdio(sys.stderr, 'wb')
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/compat.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/compat.py
new file mode 100644
index 00000000000..7560fbec397
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/compat.py
@@ -0,0 +1,322 @@
+"""
+python version compatibility code
+"""
+from __future__ import absolute_import, division, print_function
+
+import codecs
+import functools
+import inspect
+import re
+import sys
+
+import py
+
+import _pytest
+from _pytest.outcomes import TEST_OUTCOME
+
+try:
+ import enum
+except ImportError: # pragma: no cover
+ # Only available in Python 3.4+ or as a backport
+ enum = None
+
+
+_PY3 = sys.version_info > (3, 0)
+_PY2 = not _PY3
+
+
+if _PY3:
+ from inspect import signature, Parameter as Parameter
+else:
+ from funcsigs import signature, Parameter as Parameter
+
+
+NoneType = type(None)
+NOTSET = object()
+
+PY35 = sys.version_info[:2] >= (3, 5)
+PY36 = sys.version_info[:2] >= (3, 6)
+MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError'
+
+
+def _format_args(func):
+ return str(signature(func))
+
+
+isfunction = inspect.isfunction
+isclass = inspect.isclass
+# used to work around a python2 exception info leak
+exc_clear = getattr(sys, 'exc_clear', lambda: None)
+# The type of re.compile objects is not exposed in Python.
+REGEX_TYPE = type(re.compile(''))
+
+
+def is_generator(func):
+ genfunc = inspect.isgeneratorfunction(func)
+ return genfunc and not iscoroutinefunction(func)
+
+
+def iscoroutinefunction(func):
+ """Return True if func is a decorated coroutine function.
+
+ Note: copied and modified from Python 3.5's builtin couroutines.py to avoid import asyncio directly,
+ which in turns also initializes the "logging" module as side-effect (see issue #8).
+ """
+ return (getattr(func, '_is_coroutine', False) or
+ (hasattr(inspect, 'iscoroutinefunction') and inspect.iscoroutinefunction(func)))
+
+
+def getlocation(function, curdir):
+ fn = py.path.local(inspect.getfile(function))
+ lineno = py.builtin._getcode(function).co_firstlineno
+ if fn.relto(curdir):
+ fn = fn.relto(curdir)
+ return "%s:%d" % (fn, lineno + 1)
+
+
+def num_mock_patch_args(function):
+ """ return number of arguments used up by mock arguments (if any) """
+ patchings = getattr(function, "patchings", None)
+ if not patchings:
+ return 0
+ mock = sys.modules.get("mock", sys.modules.get("unittest.mock", None))
+ if mock is not None:
+ return len([p for p in patchings
+ if not p.attribute_name and p.new is mock.DEFAULT])
+ return len(patchings)
+
+
+def getfuncargnames(function, is_method=False, cls=None):
+ """Returns the names of a function's mandatory arguments.
+
+ This should return the names of all function arguments that:
+ * Aren't bound to an instance or type as in instance or class methods.
+ * Don't have default values.
+ * Aren't bound with functools.partial.
+ * Aren't replaced with mocks.
+
+ The is_method and cls arguments indicate that the function should
+ be treated as a bound method even though it's not unless, only in
+ the case of cls, the function is a static method.
+
+ @RonnyPfannschmidt: This function should be refactored when we
+ revisit fixtures. The fixture mechanism should ask the node for
+ the fixture names, and not try to obtain directly from the
+ function object well after collection has occurred.
+
+ """
+ # The parameters attribute of a Signature object contains an
+ # ordered mapping of parameter names to Parameter instances. This
+ # creates a tuple of the names of the parameters that don't have
+ # defaults.
+ arg_names = tuple(p.name for p in signature(function).parameters.values()
+ if (p.kind is Parameter.POSITIONAL_OR_KEYWORD or
+ p.kind is Parameter.KEYWORD_ONLY) and
+ p.default is Parameter.empty)
+ # If this function should be treated as a bound method even though
+ # it's passed as an unbound method or function, remove the first
+ # parameter name.
+ if (is_method or
+ (cls and not isinstance(cls.__dict__.get(function.__name__, None),
+ staticmethod))):
+ arg_names = arg_names[1:]
+ # Remove any names that will be replaced with mocks.
+ if hasattr(function, "__wrapped__"):
+ arg_names = arg_names[num_mock_patch_args(function):]
+ return arg_names
+
+
+if _PY3:
+ STRING_TYPES = bytes, str
+ UNICODE_TYPES = str,
+
+ if PY35:
+ def _bytes_to_ascii(val):
+ return val.decode('ascii', 'backslashreplace')
+ else:
+ def _bytes_to_ascii(val):
+ if val:
+ # source: http://goo.gl/bGsnwC
+ encoded_bytes, _ = codecs.escape_encode(val)
+ return encoded_bytes.decode('ascii')
+ else:
+ # empty bytes crashes codecs.escape_encode (#1087)
+ return ''
+
+ def ascii_escaped(val):
+ """If val is pure ascii, returns it as a str(). Otherwise, escapes
+ bytes objects into a sequence of escaped bytes:
+
+ b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
+
+ and escapes unicode objects into a sequence of escaped unicode
+ ids, e.g.:
+
+ '4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
+
+ note:
+ the obvious "v.decode('unicode-escape')" will return
+ valid utf-8 unicode if it finds them in bytes, but we
+ want to return escaped bytes for any byte, even if they match
+ a utf-8 string.
+
+ """
+ if isinstance(val, bytes):
+ return _bytes_to_ascii(val)
+ else:
+ return val.encode('unicode_escape').decode('ascii')
+else:
+ STRING_TYPES = bytes, str, unicode
+ UNICODE_TYPES = unicode,
+
+ def ascii_escaped(val):
+ """In py2 bytes and str are the same type, so return if it's a bytes
+ object, return it unchanged if it is a full ascii string,
+ otherwise escape it into its binary form.
+
+ If it's a unicode string, change the unicode characters into
+ unicode escapes.
+
+ """
+ if isinstance(val, bytes):
+ try:
+ return val.encode('ascii')
+ except UnicodeDecodeError:
+ return val.encode('string-escape')
+ else:
+ return val.encode('unicode-escape')
+
+
+def get_real_func(obj):
+ """ gets the real function object of the (possibly) wrapped object by
+ functools.wraps or functools.partial.
+ """
+ start_obj = obj
+ for i in range(100):
+ new_obj = getattr(obj, '__wrapped__', None)
+ if new_obj is None:
+ break
+ obj = new_obj
+ else:
+ raise ValueError(
+ ("could not find real function of {start}"
+ "\nstopped at {current}").format(
+ start=py.io.saferepr(start_obj),
+ current=py.io.saferepr(obj)))
+ if isinstance(obj, functools.partial):
+ obj = obj.func
+ return obj
+
+
+def getfslineno(obj):
+ # xxx let decorators etc specify a sane ordering
+ obj = get_real_func(obj)
+ if hasattr(obj, 'place_as'):
+ obj = obj.place_as
+ fslineno = _pytest._code.getfslineno(obj)
+ assert isinstance(fslineno[1], int), obj
+ return fslineno
+
+
+def getimfunc(func):
+ try:
+ return func.__func__
+ except AttributeError:
+ return func
+
+
+def safe_getattr(object, name, default):
+ """ Like getattr but return default upon any Exception or any OutcomeException.
+
+ Attribute access can potentially fail for 'evil' Python objects.
+ See issue #214.
+ It catches OutcomeException because of #2490 (issue #580), new outcomes are derived from BaseException
+ instead of Exception (for more details check #2707)
+ """
+ try:
+ return getattr(object, name, default)
+ except TEST_OUTCOME:
+ return default
+
+
+def _is_unittest_unexpected_success_a_failure():
+ """Return if the test suite should fail if a @expectedFailure unittest test PASSES.
+
+ From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful:
+ Changed in version 3.4: Returns False if there were any
+ unexpectedSuccesses from tests marked with the expectedFailure() decorator.
+ """
+ return sys.version_info >= (3, 4)
+
+
+if _PY3:
+ def safe_str(v):
+ """returns v as string"""
+ return str(v)
+else:
+ def safe_str(v):
+ """returns v as string, converting to ascii if necessary"""
+ try:
+ return str(v)
+ except UnicodeError:
+ if not isinstance(v, unicode):
+ v = unicode(v)
+ errors = 'replace'
+ return v.encode('utf-8', errors)
+
+
+COLLECT_FAKEMODULE_ATTRIBUTES = (
+ 'Collector',
+ 'Module',
+ 'Generator',
+ 'Function',
+ 'Instance',
+ 'Session',
+ 'Item',
+ 'Class',
+ 'File',
+ '_fillfuncargs',
+)
+
+
+def _setup_collect_fakemodule():
+ from types import ModuleType
+ import pytest
+ pytest.collect = ModuleType('pytest.collect')
+ pytest.collect.__all__ = [] # used for setns
+ for attr in COLLECT_FAKEMODULE_ATTRIBUTES:
+ setattr(pytest.collect, attr, getattr(pytest, attr))
+
+
+if _PY2:
+ # Without this the test_dupfile_on_textio will fail, otherwise CaptureIO could directly inherit from StringIO.
+ from py.io import TextIO
+
+ class CaptureIO(TextIO):
+
+ @property
+ def encoding(self):
+ return getattr(self, '_encoding', 'UTF-8')
+
+else:
+ import io
+
+ class CaptureIO(io.TextIOWrapper):
+ def __init__(self):
+ super(CaptureIO, self).__init__(
+ io.BytesIO(),
+ encoding='UTF-8', newline='', write_through=True,
+ )
+
+ def getvalue(self):
+ return self.buffer.getvalue().decode('UTF-8')
+
+
+class FuncargnamesCompatAttr(object):
+ """ helper class so that Metafunc, Function and FixtureRequest
+ don't need to each define the "funcargnames" compatibility attribute.
+ """
+ @property
+ def funcargnames(self):
+ """ alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
+ return self.fixturenames
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/config.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/config.py
new file mode 100644
index 00000000000..499c8079d41
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/config.py
@@ -0,0 +1,1400 @@
+""" command line options, ini-file and conftest.py processing. """
+from __future__ import absolute_import, division, print_function
+import argparse
+import shlex
+import traceback
+import types
+import warnings
+
+import six
+import py
+# DON't import pytest here because it causes import cycle troubles
+import sys
+import os
+import _pytest._code
+import _pytest.hookspec # the extension point definitions
+import _pytest.assertion
+from pluggy import PluginManager, HookimplMarker, HookspecMarker
+from _pytest.compat import safe_str
+
+hookimpl = HookimplMarker("pytest")
+hookspec = HookspecMarker("pytest")
+
+# pytest startup
+#
+
+
+class ConftestImportFailure(Exception):
+ def __init__(self, path, excinfo):
+ Exception.__init__(self, path, excinfo)
+ self.path = path
+ self.excinfo = excinfo
+
+ def __str__(self):
+ etype, evalue, etb = self.excinfo
+ formatted = traceback.format_tb(etb)
+ # The level of the tracebacks we want to print is hand crafted :(
+ return repr(evalue) + '\n' + ''.join(formatted[2:])
+
+
+def main(args=None, plugins=None):
+ """ return exit code, after performing an in-process test run.
+
+ :arg args: list of command line arguments.
+
+ :arg plugins: list of plugin objects to be auto-registered during
+ initialization.
+ """
+ try:
+ try:
+ config = _prepareconfig(args, plugins)
+ except ConftestImportFailure as e:
+ tw = py.io.TerminalWriter(sys.stderr)
+ for line in traceback.format_exception(*e.excinfo):
+ tw.line(line.rstrip(), red=True)
+ tw.line("ERROR: could not load %s\n" % (e.path), red=True)
+ return 4
+ else:
+ try:
+ return config.hook.pytest_cmdline_main(config=config)
+ finally:
+ config._ensure_unconfigure()
+ except UsageError as e:
+ for msg in e.args:
+ sys.stderr.write("ERROR: %s\n" % (msg,))
+ return 4
+
+
+class cmdline: # compatibility namespace
+ main = staticmethod(main)
+
+
+class UsageError(Exception):
+ """ error in pytest usage or invocation"""
+
+
+class PrintHelp(Exception):
+ """Raised when pytest should print it's help to skip the rest of the
+ argument parsing and validation."""
+ pass
+
+
+def filename_arg(path, optname):
+ """ Argparse type validator for filename arguments.
+
+ :path: path of filename
+ :optname: name of the option
+ """
+ if os.path.isdir(path):
+ raise UsageError("{0} must be a filename, given: {1}".format(optname, path))
+ return path
+
+
+def directory_arg(path, optname):
+ """Argparse type validator for directory arguments.
+
+ :path: path of directory
+ :optname: name of the option
+ """
+ if not os.path.isdir(path):
+ raise UsageError("{0} must be a directory, given: {1}".format(optname, path))
+ return path
+
+
+default_plugins = (
+ "mark main terminal runner python fixtures debugging unittest capture skipping "
+ "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion "
+ "junitxml resultlog doctest cacheprovider freeze_support "
+ "setuponly setupplan warnings logging").split()
+
+
+builtin_plugins = set(default_plugins)
+builtin_plugins.add("pytester")
+
+
+def get_config():
+ # subsequent calls to main will create a fresh instance
+ pluginmanager = PytestPluginManager()
+ config = Config(pluginmanager)
+ for spec in default_plugins:
+ pluginmanager.import_plugin(spec)
+ return config
+
+
+def get_plugin_manager():
+ """
+ Obtain a new instance of the
+ :py:class:`_pytest.config.PytestPluginManager`, with default plugins
+ already loaded.
+
+ This function can be used by integration with other tools, like hooking
+ into pytest to run tests into an IDE.
+ """
+ return get_config().pluginmanager
+
+
+def _prepareconfig(args=None, plugins=None):
+ warning = None
+ if args is None:
+ args = sys.argv[1:]
+ elif isinstance(args, py.path.local):
+ args = [str(args)]
+ elif not isinstance(args, (tuple, list)):
+ if not isinstance(args, str):
+ raise ValueError("not a string or argument list: %r" % (args,))
+ args = shlex.split(args, posix=sys.platform != "win32")
+ from _pytest import deprecated
+ warning = deprecated.MAIN_STR_ARGS
+ config = get_config()
+ pluginmanager = config.pluginmanager
+ try:
+ if plugins:
+ for plugin in plugins:
+ if isinstance(plugin, six.string_types):
+ pluginmanager.consider_pluginarg(plugin)
+ else:
+ pluginmanager.register(plugin)
+ if warning:
+ config.warn('C1', warning)
+ return pluginmanager.hook.pytest_cmdline_parse(
+ pluginmanager=pluginmanager, args=args)
+ except BaseException:
+ config._ensure_unconfigure()
+ raise
+
+
+class PytestPluginManager(PluginManager):
+ """
+ Overwrites :py:class:`pluggy.PluginManager <pluggy.PluginManager>` to add pytest-specific
+ functionality:
+
+ * loading plugins from the command line, ``PYTEST_PLUGIN`` env variable and
+ ``pytest_plugins`` global variables found in plugins being loaded;
+ * ``conftest.py`` loading during start-up;
+ """
+
+ def __init__(self):
+ super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_")
+ self._conftest_plugins = set()
+
+ # state related to local conftest plugins
+ self._path2confmods = {}
+ self._conftestpath2mod = {}
+ self._confcutdir = None
+ self._noconftest = False
+ self._duplicatepaths = set()
+
+ self.add_hookspecs(_pytest.hookspec)
+ self.register(self)
+ if os.environ.get('PYTEST_DEBUG'):
+ err = sys.stderr
+ encoding = getattr(err, 'encoding', 'utf8')
+ try:
+ err = py.io.dupfile(err, encoding=encoding)
+ except Exception:
+ pass
+ self.trace.root.setwriter(err.write)
+ self.enable_tracing()
+
+ # Config._consider_importhook will set a real object if required.
+ self.rewrite_hook = _pytest.assertion.DummyRewriteHook()
+
+ def addhooks(self, module_or_class):
+ """
+ .. deprecated:: 2.8
+
+ Use :py:meth:`pluggy.PluginManager.add_hookspecs <PluginManager.add_hookspecs>`
+ instead.
+ """
+ warning = dict(code="I2",
+ fslocation=_pytest._code.getfslineno(sys._getframe(1)),
+ nodeid=None,
+ message="use pluginmanager.add_hookspecs instead of "
+ "deprecated addhooks() method.")
+ self._warn(warning)
+ return self.add_hookspecs(module_or_class)
+
+ def parse_hookimpl_opts(self, plugin, name):
+ # pytest hooks are always prefixed with pytest_
+ # so we avoid accessing possibly non-readable attributes
+ # (see issue #1073)
+ if not name.startswith("pytest_"):
+ return
+ # ignore some historic special names which can not be hooks anyway
+ if name == "pytest_plugins" or name.startswith("pytest_funcarg__"):
+ return
+
+ method = getattr(plugin, name)
+ opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
+ if opts is not None:
+ for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
+ opts.setdefault(name, hasattr(method, name))
+ return opts
+
+ def parse_hookspec_opts(self, module_or_class, name):
+ opts = super(PytestPluginManager, self).parse_hookspec_opts(
+ module_or_class, name)
+ if opts is None:
+ method = getattr(module_or_class, name)
+ if name.startswith("pytest_"):
+ opts = {"firstresult": hasattr(method, "firstresult"),
+ "historic": hasattr(method, "historic")}
+ return opts
+
+ def register(self, plugin, name=None):
+ if name == 'pytest_catchlog':
+ self._warn('pytest-catchlog plugin has been merged into the core, '
+ 'please remove it from your requirements.')
+ return
+ ret = super(PytestPluginManager, self).register(plugin, name)
+ if ret:
+ self.hook.pytest_plugin_registered.call_historic(
+ kwargs=dict(plugin=plugin, manager=self))
+
+ if isinstance(plugin, types.ModuleType):
+ self.consider_module(plugin)
+ return ret
+
+ def getplugin(self, name):
+ # support deprecated naming because plugins (xdist e.g.) use it
+ return self.get_plugin(name)
+
+ def hasplugin(self, name):
+ """Return True if the plugin with the given name is registered."""
+ return bool(self.get_plugin(name))
+
+ def pytest_configure(self, config):
+ # XXX now that the pluginmanager exposes hookimpl(tryfirst...)
+ # we should remove tryfirst/trylast as markers
+ config.addinivalue_line("markers",
+ "tryfirst: mark a hook implementation function such that the "
+ "plugin machinery will try to call it first/as early as possible.")
+ config.addinivalue_line("markers",
+ "trylast: mark a hook implementation function such that the "
+ "plugin machinery will try to call it last/as late as possible.")
+
+ def _warn(self, message):
+ kwargs = message if isinstance(message, dict) else {
+ 'code': 'I1',
+ 'message': message,
+ 'fslocation': None,
+ 'nodeid': None,
+ }
+ self.hook.pytest_logwarning.call_historic(kwargs=kwargs)
+
+ #
+ # internal API for local conftest plugin handling
+ #
+ def _set_initial_conftests(self, namespace):
+ """ load initial conftest files given a preparsed "namespace".
+ As conftest files may add their own command line options
+ which have arguments ('--my-opt somepath') we might get some
+ false positives. All builtin and 3rd party plugins will have
+ been loaded, however, so common options will not confuse our logic
+ here.
+ """
+ current = py.path.local()
+ self._confcutdir = current.join(namespace.confcutdir, abs=True) \
+ if namespace.confcutdir else None
+ self._noconftest = namespace.noconftest
+ testpaths = namespace.file_or_dir
+ foundanchor = False
+ for path in testpaths:
+ path = str(path)
+ # remove node-id syntax
+ i = path.find("::")
+ if i != -1:
+ path = path[:i]
+ anchor = current.join(path, abs=1)
+ if exists(anchor): # we found some file object
+ self._try_load_conftest(anchor)
+ foundanchor = True
+ if not foundanchor:
+ self._try_load_conftest(current)
+
+ def _try_load_conftest(self, anchor):
+ self._getconftestmodules(anchor)
+ # let's also consider test* subdirs
+ if anchor.check(dir=1):
+ for x in anchor.listdir("test*"):
+ if x.check(dir=1):
+ self._getconftestmodules(x)
+
+ def _getconftestmodules(self, path):
+ if self._noconftest:
+ return []
+ try:
+ return self._path2confmods[path]
+ except KeyError:
+ if path.isfile():
+ clist = self._getconftestmodules(path.dirpath())
+ else:
+ # XXX these days we may rather want to use config.rootdir
+ # and allow users to opt into looking into the rootdir parent
+ # directories instead of requiring to specify confcutdir
+ clist = []
+ for parent in path.parts():
+ if self._confcutdir and self._confcutdir.relto(parent):
+ continue
+ conftestpath = parent.join("conftest.py")
+ if conftestpath.isfile():
+ mod = self._importconftest(conftestpath)
+ clist.append(mod)
+
+ self._path2confmods[path] = clist
+ return clist
+
+ def _rget_with_confmod(self, name, path):
+ modules = self._getconftestmodules(path)
+ for mod in reversed(modules):
+ try:
+ return mod, getattr(mod, name)
+ except AttributeError:
+ continue
+ raise KeyError(name)
+
+ def _importconftest(self, conftestpath):
+ try:
+ return self._conftestpath2mod[conftestpath]
+ except KeyError:
+ pkgpath = conftestpath.pypkgpath()
+ if pkgpath is None:
+ _ensure_removed_sysmodule(conftestpath.purebasename)
+ try:
+ mod = conftestpath.pyimport()
+ except Exception:
+ raise ConftestImportFailure(conftestpath, sys.exc_info())
+
+ self._conftest_plugins.add(mod)
+ self._conftestpath2mod[conftestpath] = mod
+ dirpath = conftestpath.dirpath()
+ if dirpath in self._path2confmods:
+ for path, mods in self._path2confmods.items():
+ if path and path.relto(dirpath) or path == dirpath:
+ assert mod not in mods
+ mods.append(mod)
+ self.trace("loaded conftestmodule %r" % (mod))
+ self.consider_conftest(mod)
+ return mod
+
+ #
+ # API for bootstrapping plugin loading
+ #
+ #
+
+ def consider_preparse(self, args):
+ for opt1, opt2 in zip(args, args[1:]):
+ if opt1 == "-p":
+ self.consider_pluginarg(opt2)
+
+ def consider_pluginarg(self, arg):
+ if arg.startswith("no:"):
+ name = arg[3:]
+ self.set_blocked(name)
+ if not name.startswith("pytest_"):
+ self.set_blocked("pytest_" + name)
+ else:
+ self.import_plugin(arg)
+
+ def consider_conftest(self, conftestmodule):
+ self.register(conftestmodule, name=conftestmodule.__file__)
+
+ def consider_env(self):
+ self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
+
+ def consider_module(self, mod):
+ self._import_plugin_specs(getattr(mod, 'pytest_plugins', []))
+
+ def _import_plugin_specs(self, spec):
+ plugins = _get_plugin_specs_as_list(spec)
+ for import_spec in plugins:
+ self.import_plugin(import_spec)
+
+ def import_plugin(self, modname):
+ # most often modname refers to builtin modules, e.g. "pytester",
+ # "terminal" or "capture". Those plugins are registered under their
+ # basename for historic purposes but must be imported with the
+ # _pytest prefix.
+ assert isinstance(modname, (six.text_type, str)), "module name as text required, got %r" % modname
+ modname = str(modname)
+ if self.is_blocked(modname) or self.get_plugin(modname) is not None:
+ return
+ if modname in builtin_plugins:
+ importspec = "_pytest." + modname
+ else:
+ importspec = modname
+ self.rewrite_hook.mark_rewrite(importspec)
+ try:
+ __import__(importspec)
+ except ImportError as e:
+ new_exc_type = ImportError
+ new_exc_message = 'Error importing plugin "%s": %s' % (modname, safe_str(e.args[0]))
+ new_exc = new_exc_type(new_exc_message)
+
+ six.reraise(new_exc_type, new_exc, sys.exc_info()[2])
+
+ except Exception as e:
+ import pytest
+ if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
+ raise
+ self._warn("skipped plugin %r: %s" % ((modname, e.msg)))
+ else:
+ mod = sys.modules[importspec]
+ self.register(mod, modname)
+
+
+def _get_plugin_specs_as_list(specs):
+ """
+ Parses a list of "plugin specs" and returns a list of plugin names.
+
+ Plugin specs can be given as a list of strings separated by "," or already as a list/tuple in
+ which case it is returned as a list. Specs can also be `None` in which case an
+ empty list is returned.
+ """
+ if specs is not None:
+ if isinstance(specs, str):
+ specs = specs.split(',') if specs else []
+ if not isinstance(specs, (list, tuple)):
+ raise UsageError("Plugin specs must be a ','-separated string or a "
+ "list/tuple of strings for plugin names. Given: %r" % specs)
+ return list(specs)
+ return []
+
+
+class Parser:
+ """ Parser for command line arguments and ini-file values.
+
+ :ivar extra_info: dict of generic param -> value to display in case
+ there's an error processing the command line arguments.
+ """
+
+ def __init__(self, usage=None, processopt=None):
+ self._anonymous = OptionGroup("custom options", parser=self)
+ self._groups = []
+ self._processopt = processopt
+ self._usage = usage
+ self._inidict = {}
+ self._ininames = []
+ self.extra_info = {}
+
+ def processoption(self, option):
+ if self._processopt:
+ if option.dest:
+ self._processopt(option)
+
+ def getgroup(self, name, description="", after=None):
+ """ get (or create) a named option Group.
+
+ :name: name of the option group.
+ :description: long description for --help output.
+ :after: name of other group, used for ordering --help output.
+
+ The returned group object has an ``addoption`` method with the same
+ signature as :py:func:`parser.addoption
+ <_pytest.config.Parser.addoption>` but will be shown in the
+ respective group in the output of ``pytest. --help``.
+ """
+ for group in self._groups:
+ if group.name == name:
+ return group
+ group = OptionGroup(name, description, parser=self)
+ i = 0
+ for i, grp in enumerate(self._groups):
+ if grp.name == after:
+ break
+ self._groups.insert(i + 1, group)
+ return group
+
+ def addoption(self, *opts, **attrs):
+ """ register a command line option.
+
+ :opts: option names, can be short or long options.
+ :attrs: same attributes which the ``add_option()`` function of the
+ `argparse library
+ <http://docs.python.org/2/library/argparse.html>`_
+ accepts.
+
+ After command line parsing options are available on the pytest config
+ object via ``config.option.NAME`` where ``NAME`` is usually set
+ by passing a ``dest`` attribute, for example
+ ``addoption("--long", dest="NAME", ...)``.
+ """
+ self._anonymous.addoption(*opts, **attrs)
+
+ def parse(self, args, namespace=None):
+ from _pytest._argcomplete import try_argcomplete
+ self.optparser = self._getparser()
+ try_argcomplete(self.optparser)
+ return self.optparser.parse_args([str(x) for x in args], namespace=namespace)
+
+ def _getparser(self):
+ from _pytest._argcomplete import filescompleter
+ optparser = MyOptionParser(self, self.extra_info)
+ groups = self._groups + [self._anonymous]
+ for group in groups:
+ if group.options:
+ desc = group.description or group.name
+ arggroup = optparser.add_argument_group(desc)
+ for option in group.options:
+ n = option.names()
+ a = option.attrs()
+ arggroup.add_argument(*n, **a)
+ # bash like autocompletion for dirs (appending '/')
+ optparser.add_argument(FILE_OR_DIR, nargs='*').completer = filescompleter
+ return optparser
+
+ def parse_setoption(self, args, option, namespace=None):
+ parsedoption = self.parse(args, namespace=namespace)
+ for name, value in parsedoption.__dict__.items():
+ setattr(option, name, value)
+ return getattr(parsedoption, FILE_OR_DIR)
+
+ def parse_known_args(self, args, namespace=None):
+ """parses and returns a namespace object with known arguments at this
+ point.
+ """
+ return self.parse_known_and_unknown_args(args, namespace=namespace)[0]
+
+ def parse_known_and_unknown_args(self, args, namespace=None):
+ """parses and returns a namespace object with known arguments, and
+ the remaining arguments unknown at this point.
+ """
+ optparser = self._getparser()
+ args = [str(x) for x in args]
+ return optparser.parse_known_args(args, namespace=namespace)
+
+ def addini(self, name, help, type=None, default=None):
+ """ register an ini-file option.
+
+ :name: name of the ini-variable
+ :type: type of the variable, can be ``pathlist``, ``args``, ``linelist``
+ or ``bool``.
+ :default: default value if no ini-file option exists but is queried.
+
+ The value of ini-variables can be retrieved via a call to
+ :py:func:`config.getini(name) <_pytest.config.Config.getini>`.
+ """
+ assert type in (None, "pathlist", "args", "linelist", "bool")
+ self._inidict[name] = (help, type, default)
+ self._ininames.append(name)
+
+
+class ArgumentError(Exception):
+ """
+ Raised if an Argument instance is created with invalid or
+ inconsistent arguments.
+ """
+
+ def __init__(self, msg, option):
+ self.msg = msg
+ self.option_id = str(option)
+
+ def __str__(self):
+ if self.option_id:
+ return "option %s: %s" % (self.option_id, self.msg)
+ else:
+ return self.msg
+
+
+class Argument:
+ """class that mimics the necessary behaviour of optparse.Option
+
+ its currently a least effort implementation
+ and ignoring choices and integer prefixes
+ https://docs.python.org/3/library/optparse.html#optparse-standard-option-types
+ """
+ _typ_map = {
+ 'int': int,
+ 'string': str,
+ 'float': float,
+ 'complex': complex,
+ }
+
+ def __init__(self, *names, **attrs):
+ """store parms in private vars for use in add_argument"""
+ self._attrs = attrs
+ self._short_opts = []
+ self._long_opts = []
+ self.dest = attrs.get('dest')
+ if '%default' in (attrs.get('help') or ''):
+ warnings.warn(
+ 'pytest now uses argparse. "%default" should be'
+ ' changed to "%(default)s" ',
+ DeprecationWarning,
+ stacklevel=3)
+ try:
+ typ = attrs['type']
+ except KeyError:
+ pass
+ else:
+ # this might raise a keyerror as well, don't want to catch that
+ if isinstance(typ, six.string_types):
+ if typ == 'choice':
+ warnings.warn(
+ 'type argument to addoption() is a string %r.'
+ ' For parsearg this is optional and when supplied'
+ ' should be a type.'
+ ' (options: %s)' % (typ, names),
+ DeprecationWarning,
+ stacklevel=3)
+ # argparse expects a type here take it from
+ # the type of the first element
+ attrs['type'] = type(attrs['choices'][0])
+ else:
+ warnings.warn(
+ 'type argument to addoption() is a string %r.'
+ ' For parsearg this should be a type.'
+ ' (options: %s)' % (typ, names),
+ DeprecationWarning,
+ stacklevel=3)
+ attrs['type'] = Argument._typ_map[typ]
+ # used in test_parseopt -> test_parse_defaultgetter
+ self.type = attrs['type']
+ else:
+ self.type = typ
+ try:
+ # attribute existence is tested in Config._processopt
+ self.default = attrs['default']
+ except KeyError:
+ pass
+ self._set_opt_strings(names)
+ if not self.dest:
+ if self._long_opts:
+ self.dest = self._long_opts[0][2:].replace('-', '_')
+ else:
+ try:
+ self.dest = self._short_opts[0][1:]
+ except IndexError:
+ raise ArgumentError(
+ 'need a long or short option', self)
+
+ def names(self):
+ return self._short_opts + self._long_opts
+
+ def attrs(self):
+ # update any attributes set by processopt
+ attrs = 'default dest help'.split()
+ if self.dest:
+ attrs.append(self.dest)
+ for attr in attrs:
+ try:
+ self._attrs[attr] = getattr(self, attr)
+ except AttributeError:
+ pass
+ if self._attrs.get('help'):
+ a = self._attrs['help']
+ a = a.replace('%default', '%(default)s')
+ # a = a.replace('%prog', '%(prog)s')
+ self._attrs['help'] = a
+ return self._attrs
+
+ def _set_opt_strings(self, opts):
+ """directly from optparse
+
+ might not be necessary as this is passed to argparse later on"""
+ for opt in opts:
+ if len(opt) < 2:
+ raise ArgumentError(
+ "invalid option string %r: "
+ "must be at least two characters long" % opt, self)
+ elif len(opt) == 2:
+ if not (opt[0] == "-" and opt[1] != "-"):
+ raise ArgumentError(
+ "invalid short option string %r: "
+ "must be of the form -x, (x any non-dash char)" % opt,
+ self)
+ self._short_opts.append(opt)
+ else:
+ if not (opt[0:2] == "--" and opt[2] != "-"):
+ raise ArgumentError(
+ "invalid long option string %r: "
+ "must start with --, followed by non-dash" % opt,
+ self)
+ self._long_opts.append(opt)
+
+ def __repr__(self):
+ args = []
+ if self._short_opts:
+ args += ['_short_opts: ' + repr(self._short_opts)]
+ if self._long_opts:
+ args += ['_long_opts: ' + repr(self._long_opts)]
+ args += ['dest: ' + repr(self.dest)]
+ if hasattr(self, 'type'):
+ args += ['type: ' + repr(self.type)]
+ if hasattr(self, 'default'):
+ args += ['default: ' + repr(self.default)]
+ return 'Argument({0})'.format(', '.join(args))
+
+
+class OptionGroup:
+ def __init__(self, name, description="", parser=None):
+ self.name = name
+ self.description = description
+ self.options = []
+ self.parser = parser
+
+ def addoption(self, *optnames, **attrs):
+ """ add an option to this group.
+
+ if a shortened version of a long option is specified it will
+ be suppressed in the help. addoption('--twowords', '--two-words')
+ results in help showing '--two-words' only, but --twowords gets
+ accepted **and** the automatic destination is in args.twowords
+ """
+ conflict = set(optnames).intersection(
+ name for opt in self.options for name in opt.names())
+ if conflict:
+ raise ValueError("option names %s already added" % conflict)
+ option = Argument(*optnames, **attrs)
+ self._addoption_instance(option, shortupper=False)
+
+ def _addoption(self, *optnames, **attrs):
+ option = Argument(*optnames, **attrs)
+ self._addoption_instance(option, shortupper=True)
+
+ def _addoption_instance(self, option, shortupper=False):
+ if not shortupper:
+ for opt in option._short_opts:
+ if opt[0] == '-' and opt[1].islower():
+ raise ValueError("lowercase shortoptions reserved")
+ if self.parser:
+ self.parser.processoption(option)
+ self.options.append(option)
+
+
+class MyOptionParser(argparse.ArgumentParser):
+ def __init__(self, parser, extra_info=None):
+ if not extra_info:
+ extra_info = {}
+ self._parser = parser
+ argparse.ArgumentParser.__init__(self, usage=parser._usage,
+ add_help=False, formatter_class=DropShorterLongHelpFormatter)
+ # extra_info is a dict of (param -> value) to display if there's
+ # an usage error to provide more contextual information to the user
+ self.extra_info = extra_info
+
+ def parse_args(self, args=None, namespace=None):
+ """allow splitting of positional arguments"""
+ args, argv = self.parse_known_args(args, namespace)
+ if argv:
+ for arg in argv:
+ if arg and arg[0] == '-':
+ lines = ['unrecognized arguments: %s' % (' '.join(argv))]
+ for k, v in sorted(self.extra_info.items()):
+ lines.append(' %s: %s' % (k, v))
+ self.error('\n'.join(lines))
+ getattr(args, FILE_OR_DIR).extend(argv)
+ return args
+
+
+class DropShorterLongHelpFormatter(argparse.HelpFormatter):
+ """shorten help for long options that differ only in extra hyphens
+
+ - collapse **long** options that are the same except for extra hyphens
+ - special action attribute map_long_option allows surpressing additional
+ long options
+ - shortcut if there are only two options and one of them is a short one
+ - cache result on action object as this is called at least 2 times
+ """
+
+ def _format_action_invocation(self, action):
+ orgstr = argparse.HelpFormatter._format_action_invocation(self, action)
+ if orgstr and orgstr[0] != '-': # only optional arguments
+ return orgstr
+ res = getattr(action, '_formatted_action_invocation', None)
+ if res:
+ return res
+ options = orgstr.split(', ')
+ if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2):
+ # a shortcut for '-h, --help' or '--abc', '-a'
+ action._formatted_action_invocation = orgstr
+ return orgstr
+ return_list = []
+ option_map = getattr(action, 'map_long_option', {})
+ if option_map is None:
+ option_map = {}
+ short_long = {}
+ for option in options:
+ if len(option) == 2 or option[2] == ' ':
+ continue
+ if not option.startswith('--'):
+ raise ArgumentError('long optional argument without "--": [%s]'
+ % (option), self)
+ xxoption = option[2:]
+ if xxoption.split()[0] not in option_map:
+ shortened = xxoption.replace('-', '')
+ if shortened not in short_long or \
+ len(short_long[shortened]) < len(xxoption):
+ short_long[shortened] = xxoption
+ # now short_long has been filled out to the longest with dashes
+ # **and** we keep the right option ordering from add_argument
+ for option in options:
+ if len(option) == 2 or option[2] == ' ':
+ return_list.append(option)
+ if option[2:] == short_long.get(option.replace('-', '')):
+ return_list.append(option.replace(' ', '=', 1))
+ action._formatted_action_invocation = ', '.join(return_list)
+ return action._formatted_action_invocation
+
+
+def _ensure_removed_sysmodule(modname):
+ try:
+ del sys.modules[modname]
+ except KeyError:
+ pass
+
+
+class CmdOptions(object):
+ """ holds cmdline options as attributes."""
+
+ def __init__(self, values=()):
+ self.__dict__.update(values)
+
+ def __repr__(self):
+ return "<CmdOptions %r>" % (self.__dict__,)
+
+ def copy(self):
+ return CmdOptions(self.__dict__)
+
+
+class Notset:
+ def __repr__(self):
+ return "<NOTSET>"
+
+
+notset = Notset()
+FILE_OR_DIR = 'file_or_dir'
+
+
+def _iter_rewritable_modules(package_files):
+ for fn in package_files:
+ is_simple_module = '/' not in fn and fn.endswith('.py')
+ is_package = fn.count('/') == 1 and fn.endswith('__init__.py')
+ if is_simple_module:
+ module_name, _ = os.path.splitext(fn)
+ yield module_name
+ elif is_package:
+ package_name = os.path.dirname(fn)
+ yield package_name
+
+
+class Config(object):
+ """ access to configuration values, pluginmanager and plugin hooks. """
+
+ def __init__(self, pluginmanager):
+ #: access to command line option as attributes.
+ #: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
+ self.option = CmdOptions()
+ _a = FILE_OR_DIR
+ self._parser = Parser(
+ usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a),
+ processopt=self._processopt,
+ )
+ #: a pluginmanager instance
+ self.pluginmanager = pluginmanager
+ self.trace = self.pluginmanager.trace.root.get("config")
+ self.hook = self.pluginmanager.hook
+ self._inicache = {}
+ self._override_ini = ()
+ self._opt2dest = {}
+ self._cleanup = []
+ self._warn = self.pluginmanager._warn
+ self.pluginmanager.register(self, "pytestconfig")
+ self._configured = False
+
+ def do_setns(dic):
+ import pytest
+ setns(pytest, dic)
+
+ self.hook.pytest_namespace.call_historic(do_setns, {})
+ self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser))
+
+ def add_cleanup(self, func):
+ """ Add a function to be called when the config object gets out of
+ use (usually coninciding with pytest_unconfigure)."""
+ self._cleanup.append(func)
+
+ def _do_configure(self):
+ assert not self._configured
+ self._configured = True
+ self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
+
+ def _ensure_unconfigure(self):
+ if self._configured:
+ self._configured = False
+ self.hook.pytest_unconfigure(config=self)
+ self.hook.pytest_configure._call_history = []
+ while self._cleanup:
+ fin = self._cleanup.pop()
+ fin()
+
+ def warn(self, code, message, fslocation=None, nodeid=None):
+ """ generate a warning for this test session. """
+ self.hook.pytest_logwarning.call_historic(kwargs=dict(
+ code=code, message=message,
+ fslocation=fslocation, nodeid=nodeid))
+
+ def get_terminal_writer(self):
+ return self.pluginmanager.get_plugin("terminalreporter")._tw
+
+ def pytest_cmdline_parse(self, pluginmanager, args):
+ # REF1 assert self == pluginmanager.config, (self, pluginmanager.config)
+ self.parse(args)
+ return self
+
+ def notify_exception(self, excinfo, option=None):
+ if option and option.fulltrace:
+ style = "long"
+ else:
+ style = "native"
+ excrepr = excinfo.getrepr(funcargs=True,
+ showlocals=getattr(option, 'showlocals', False),
+ style=style,
+ )
+ res = self.hook.pytest_internalerror(excrepr=excrepr,
+ excinfo=excinfo)
+ if not any(res):
+ for line in str(excrepr).split("\n"):
+ sys.stderr.write("INTERNALERROR> %s\n" % line)
+ sys.stderr.flush()
+
+ def cwd_relative_nodeid(self, nodeid):
+ # nodeid's are relative to the rootpath, compute relative to cwd
+ if self.invocation_dir != self.rootdir:
+ fullpath = self.rootdir.join(nodeid)
+ nodeid = self.invocation_dir.bestrelpath(fullpath)
+ return nodeid
+
+ @classmethod
+ def fromdictargs(cls, option_dict, args):
+ """ constructor useable for subprocesses. """
+ config = get_config()
+ config.option.__dict__.update(option_dict)
+ config.parse(args, addopts=False)
+ for x in config.option.plugins:
+ config.pluginmanager.consider_pluginarg(x)
+ return config
+
+ def _processopt(self, opt):
+ for name in opt._short_opts + opt._long_opts:
+ self._opt2dest[name] = opt.dest
+
+ if hasattr(opt, 'default') and opt.dest:
+ if not hasattr(self.option, opt.dest):
+ setattr(self.option, opt.dest, opt.default)
+
+ @hookimpl(trylast=True)
+ def pytest_load_initial_conftests(self, early_config):
+ self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
+
+ def _initini(self, args):
+ ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy())
+ r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn)
+ self.rootdir, self.inifile, self.inicfg = r
+ self._parser.extra_info['rootdir'] = self.rootdir
+ self._parser.extra_info['inifile'] = self.inifile
+ self.invocation_dir = py.path.local()
+ self._parser.addini('addopts', 'extra command line options', 'args')
+ self._parser.addini('minversion', 'minimally required pytest version')
+ self._override_ini = ns.override_ini or ()
+
+ def _consider_importhook(self, args):
+ """Install the PEP 302 import hook if using assertion rewriting.
+
+ Needs to parse the --assert=<mode> option from the commandline
+ and find all the installed plugins to mark them for rewriting
+ by the importhook.
+ """
+ ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
+ mode = ns.assertmode
+ if mode == 'rewrite':
+ try:
+ hook = _pytest.assertion.install_importhook(self)
+ except SystemError:
+ mode = 'plain'
+ else:
+ self._mark_plugins_for_rewrite(hook)
+ self._warn_about_missing_assertion(mode)
+
+ def _mark_plugins_for_rewrite(self, hook):
+ """
+ Given an importhook, mark for rewrite any top-level
+ modules or packages in the distribution package for
+ all pytest plugins.
+ """
+ import pkg_resources
+ self.pluginmanager.rewrite_hook = hook
+
+ # 'RECORD' available for plugins installed normally (pip install)
+ # 'SOURCES.txt' available for plugins installed in dev mode (pip install -e)
+ # for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa
+ # so it shouldn't be an issue
+ metadata_files = 'RECORD', 'SOURCES.txt'
+
+ package_files = (
+ entry.split(',')[0]
+ for entrypoint in pkg_resources.iter_entry_points('pytest11')
+ for metadata in metadata_files
+ for entry in entrypoint.dist._get_metadata(metadata)
+ )
+
+ for name in _iter_rewritable_modules(package_files):
+ hook.mark_rewrite(name)
+
+ def _warn_about_missing_assertion(self, mode):
+ try:
+ assert False
+ except AssertionError:
+ pass
+ else:
+ if mode == 'plain':
+ sys.stderr.write("WARNING: ASSERTIONS ARE NOT EXECUTED"
+ " and FAILING TESTS WILL PASS. Are you"
+ " using python -O?")
+ else:
+ sys.stderr.write("WARNING: assertions not in test modules or"
+ " plugins will be ignored"
+ " because assert statements are not executed "
+ "by the underlying Python interpreter "
+ "(are you using python -O?)\n")
+
+ def _preparse(self, args, addopts=True):
+ if addopts:
+ args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args
+ self._initini(args)
+ if addopts:
+ args[:] = self.getini("addopts") + args
+ self._checkversion()
+ self._consider_importhook(args)
+ self.pluginmanager.consider_preparse(args)
+ self.pluginmanager.load_setuptools_entrypoints('pytest11')
+ self.pluginmanager.consider_env()
+ self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
+ if self.known_args_namespace.confcutdir is None and self.inifile:
+ confcutdir = py.path.local(self.inifile).dirname
+ self.known_args_namespace.confcutdir = confcutdir
+ try:
+ self.hook.pytest_load_initial_conftests(early_config=self,
+ args=args, parser=self._parser)
+ except ConftestImportFailure:
+ e = sys.exc_info()[1]
+ if ns.help or ns.version:
+ # we don't want to prevent --help/--version to work
+ # so just let is pass and print a warning at the end
+ self._warn("could not load initial conftests (%s)\n" % e.path)
+ else:
+ raise
+
+ def _checkversion(self):
+ import pytest
+ minver = self.inicfg.get('minversion', None)
+ if minver:
+ ver = minver.split(".")
+ myver = pytest.__version__.split(".")
+ if myver < ver:
+ raise pytest.UsageError(
+ "%s:%d: requires pytest-%s, actual pytest-%s'" % (
+ self.inicfg.config.path, self.inicfg.lineof('minversion'),
+ minver, pytest.__version__))
+
+ def parse(self, args, addopts=True):
+ # parse given cmdline arguments into this config object.
+ assert not hasattr(self, 'args'), (
+ "can only parse cmdline args at most once per Config object")
+ self._origargs = args
+ self.hook.pytest_addhooks.call_historic(
+ kwargs=dict(pluginmanager=self.pluginmanager))
+ self._preparse(args, addopts=addopts)
+ # XXX deprecated hook:
+ self.hook.pytest_cmdline_preparse(config=self, args=args)
+ self._parser.after_preparse = True
+ try:
+ args = self._parser.parse_setoption(args, self.option, namespace=self.option)
+ if not args:
+ cwd = os.getcwd()
+ if cwd == self.rootdir:
+ args = self.getini('testpaths')
+ if not args:
+ args = [cwd]
+ self.args = args
+ except PrintHelp:
+ pass
+
+ def addinivalue_line(self, name, line):
+ """ add a line to an ini-file option. The option must have been
+ declared but might not yet be set in which case the line becomes the
+ the first line in its value. """
+ x = self.getini(name)
+ assert isinstance(x, list)
+ x.append(line) # modifies the cached list inline
+
+ def getini(self, name):
+ """ return configuration value from an :ref:`ini file <inifiles>`. If the
+ specified name hasn't been registered through a prior
+ :py:func:`parser.addini <_pytest.config.Parser.addini>`
+ call (usually from a plugin), a ValueError is raised. """
+ try:
+ return self._inicache[name]
+ except KeyError:
+ self._inicache[name] = val = self._getini(name)
+ return val
+
+ def _getini(self, name):
+ try:
+ description, type, default = self._parser._inidict[name]
+ except KeyError:
+ raise ValueError("unknown configuration value: %r" % (name,))
+ value = self._get_override_ini_value(name)
+ if value is None:
+ try:
+ value = self.inicfg[name]
+ except KeyError:
+ if default is not None:
+ return default
+ if type is None:
+ return ''
+ return []
+ if type == "pathlist":
+ dp = py.path.local(self.inicfg.config.path).dirpath()
+ values = []
+ for relpath in shlex.split(value):
+ values.append(dp.join(relpath, abs=True))
+ return values
+ elif type == "args":
+ return shlex.split(value)
+ elif type == "linelist":
+ return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
+ elif type == "bool":
+ return bool(_strtobool(value.strip()))
+ else:
+ assert type is None
+ return value
+
+ def _getconftest_pathlist(self, name, path):
+ try:
+ mod, relroots = self.pluginmanager._rget_with_confmod(name, path)
+ except KeyError:
+ return None
+ modpath = py.path.local(mod.__file__).dirpath()
+ values = []
+ for relroot in relroots:
+ if not isinstance(relroot, py.path.local):
+ relroot = relroot.replace("/", py.path.local.sep)
+ relroot = modpath.join(relroot, abs=True)
+ values.append(relroot)
+ return values
+
+ def _get_override_ini_value(self, name):
+ value = None
+ # override_ini is a list of list, to support both -o foo1=bar1 foo2=bar2 and
+ # and -o foo1=bar1 -o foo2=bar2 options
+ # always use the last item if multiple value set for same ini-name,
+ # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2
+ for ini_config_list in self._override_ini:
+ for ini_config in ini_config_list:
+ try:
+ (key, user_ini_value) = ini_config.split("=", 1)
+ except ValueError:
+ raise UsageError("-o/--override-ini expects option=value style.")
+ if key == name:
+ value = user_ini_value
+ return value
+
+ def getoption(self, name, default=notset, skip=False):
+ """ return command line option value.
+
+ :arg name: name of the option. You may also specify
+ the literal ``--OPT`` option instead of the "dest" option name.
+ :arg default: default value if no option of that name exists.
+ :arg skip: if True raise pytest.skip if option does not exists
+ or has a None value.
+ """
+ name = self._opt2dest.get(name, name)
+ try:
+ val = getattr(self.option, name)
+ if val is None and skip:
+ raise AttributeError(name)
+ return val
+ except AttributeError:
+ if default is not notset:
+ return default
+ if skip:
+ import pytest
+ pytest.skip("no %r option found" % (name,))
+ raise ValueError("no option named %r" % (name,))
+
+ def getvalue(self, name, path=None):
+ """ (deprecated, use getoption()) """
+ return self.getoption(name)
+
+ def getvalueorskip(self, name, path=None):
+ """ (deprecated, use getoption(skip=True)) """
+ return self.getoption(name, skip=True)
+
+
+def exists(path, ignore=EnvironmentError):
+ try:
+ return path.check()
+ except ignore:
+ return False
+
+
+def getcfg(args, warnfunc=None):
+ """
+ Search the list of arguments for a valid ini-file for pytest,
+ and return a tuple of (rootdir, inifile, cfg-dict).
+
+ note: warnfunc is an optional function used to warn
+ about ini-files that use deprecated features.
+ This parameter should be removed when pytest
+ adopts standard deprecation warnings (#1804).
+ """
+ from _pytest.deprecated import SETUP_CFG_PYTEST
+ inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"]
+ args = [x for x in args if not str(x).startswith("-")]
+ if not args:
+ args = [py.path.local()]
+ for arg in args:
+ arg = py.path.local(arg)
+ for base in arg.parts(reverse=True):
+ for inibasename in inibasenames:
+ p = base.join(inibasename)
+ if exists(p):
+ iniconfig = py.iniconfig.IniConfig(p)
+ if 'pytest' in iniconfig.sections:
+ if inibasename == 'setup.cfg' and warnfunc:
+ warnfunc('C1', SETUP_CFG_PYTEST)
+ return base, p, iniconfig['pytest']
+ if inibasename == 'setup.cfg' and 'tool:pytest' in iniconfig.sections:
+ return base, p, iniconfig['tool:pytest']
+ elif inibasename == "pytest.ini":
+ # allowed to be empty
+ return base, p, {}
+ return None, None, None
+
+
+def get_common_ancestor(paths):
+ common_ancestor = None
+ for path in paths:
+ if not path.exists():
+ continue
+ if common_ancestor is None:
+ common_ancestor = path
+ else:
+ if path.relto(common_ancestor) or path == common_ancestor:
+ continue
+ elif common_ancestor.relto(path):
+ common_ancestor = path
+ else:
+ shared = path.common(common_ancestor)
+ if shared is not None:
+ common_ancestor = shared
+ if common_ancestor is None:
+ common_ancestor = py.path.local()
+ elif common_ancestor.isfile():
+ common_ancestor = common_ancestor.dirpath()
+ return common_ancestor
+
+
+def get_dirs_from_args(args):
+ def is_option(x):
+ return str(x).startswith('-')
+
+ def get_file_part_from_node_id(x):
+ return str(x).split('::')[0]
+
+ def get_dir_from_path(path):
+ if path.isdir():
+ return path
+ return py.path.local(path.dirname)
+
+ # These look like paths but may not exist
+ possible_paths = (
+ py.path.local(get_file_part_from_node_id(arg))
+ for arg in args
+ if not is_option(arg)
+ )
+
+ return [
+ get_dir_from_path(path)
+ for path in possible_paths
+ if path.exists()
+ ]
+
+
+def determine_setup(inifile, args, warnfunc=None):
+ dirs = get_dirs_from_args(args)
+ if inifile:
+ iniconfig = py.iniconfig.IniConfig(inifile)
+ try:
+ inicfg = iniconfig["pytest"]
+ except KeyError:
+ inicfg = None
+ rootdir = get_common_ancestor(dirs)
+ else:
+ ancestor = get_common_ancestor(dirs)
+ rootdir, inifile, inicfg = getcfg([ancestor], warnfunc=warnfunc)
+ if rootdir is None:
+ for rootdir in ancestor.parts(reverse=True):
+ if rootdir.join("setup.py").exists():
+ break
+ else:
+ rootdir, inifile, inicfg = getcfg(dirs, warnfunc=warnfunc)
+ if rootdir is None:
+ rootdir = get_common_ancestor([py.path.local(), ancestor])
+ is_fs_root = os.path.splitdrive(str(rootdir))[1] == '/'
+ if is_fs_root:
+ rootdir = ancestor
+ return rootdir, inifile, inicfg or {}
+
+
+def setns(obj, dic):
+ import pytest
+ for name, value in dic.items():
+ if isinstance(value, dict):
+ mod = getattr(obj, name, None)
+ if mod is None:
+ modname = "pytest.%s" % name
+ mod = types.ModuleType(modname)
+ sys.modules[modname] = mod
+ mod.__all__ = []
+ setattr(obj, name, mod)
+ obj.__all__.append(name)
+ setns(mod, value)
+ else:
+ setattr(obj, name, value)
+ obj.__all__.append(name)
+ # if obj != pytest:
+ # pytest.__all__.append(name)
+ setattr(pytest, name, value)
+
+
+def create_terminal_writer(config, *args, **kwargs):
+ """Create a TerminalWriter instance configured according to the options
+ in the config object. Every code which requires a TerminalWriter object
+ and has access to a config object should use this function.
+ """
+ tw = py.io.TerminalWriter(*args, **kwargs)
+ if config.option.color == 'yes':
+ tw.hasmarkup = True
+ if config.option.color == 'no':
+ tw.hasmarkup = False
+ return tw
+
+
+def _strtobool(val):
+ """Convert a string representation of truth to true (1) or false (0).
+
+ True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
+ are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
+ 'val' is anything else.
+
+ .. note:: copied from distutils.util
+ """
+ val = val.lower()
+ if val in ('y', 'yes', 't', 'true', 'on', '1'):
+ return 1
+ elif val in ('n', 'no', 'f', 'false', 'off', '0'):
+ return 0
+ else:
+ raise ValueError("invalid truth value %r" % (val,))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/debugging.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/debugging.py
new file mode 100644
index 00000000000..d7dca780956
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/debugging.py
@@ -0,0 +1,123 @@
+""" interactive debugging with PDB, the Python Debugger. """
+from __future__ import absolute_import, division, print_function
+import pdb
+import sys
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("general")
+ group._addoption(
+ '--pdb', dest="usepdb", action="store_true",
+ help="start the interactive Python debugger on errors.")
+ group._addoption(
+ '--pdbcls', dest="usepdb_cls", metavar="modulename:classname",
+ help="start a custom interactive Python debugger on errors. "
+ "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb")
+
+
+def pytest_configure(config):
+ if config.getvalue("usepdb_cls"):
+ modname, classname = config.getvalue("usepdb_cls").split(":")
+ __import__(modname)
+ pdb_cls = getattr(sys.modules[modname], classname)
+ else:
+ pdb_cls = pdb.Pdb
+
+ if config.getvalue("usepdb"):
+ config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
+
+ old = (pdb.set_trace, pytestPDB._pluginmanager)
+
+ def fin():
+ pdb.set_trace, pytestPDB._pluginmanager = old
+ pytestPDB._config = None
+ pytestPDB._pdb_cls = pdb.Pdb
+
+ pdb.set_trace = pytestPDB.set_trace
+ pytestPDB._pluginmanager = config.pluginmanager
+ pytestPDB._config = config
+ pytestPDB._pdb_cls = pdb_cls
+ config._cleanup.append(fin)
+
+
+class pytestPDB:
+ """ Pseudo PDB that defers to the real pdb. """
+ _pluginmanager = None
+ _config = None
+ _pdb_cls = pdb.Pdb
+
+ @classmethod
+ def set_trace(cls):
+ """ invoke PDB set_trace debugging, dropping any IO capturing. """
+ import _pytest.config
+ frame = sys._getframe().f_back
+ if cls._pluginmanager is not None:
+ capman = cls._pluginmanager.getplugin("capturemanager")
+ if capman:
+ capman.suspend_global_capture(in_=True)
+ tw = _pytest.config.create_terminal_writer(cls._config)
+ tw.line()
+ tw.sep(">", "PDB set_trace (IO-capturing turned off)")
+ cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config)
+ cls._pdb_cls().set_trace(frame)
+
+
+class PdbInvoke:
+ def pytest_exception_interact(self, node, call, report):
+ capman = node.config.pluginmanager.getplugin("capturemanager")
+ if capman:
+ out, err = capman.suspend_global_capture(in_=True)
+ sys.stdout.write(out)
+ sys.stdout.write(err)
+ _enter_pdb(node, call.excinfo, report)
+
+ def pytest_internalerror(self, excrepr, excinfo):
+ for line in str(excrepr).split("\n"):
+ sys.stderr.write("INTERNALERROR> %s\n" % line)
+ sys.stderr.flush()
+ tb = _postmortem_traceback(excinfo)
+ post_mortem(tb)
+
+
+def _enter_pdb(node, excinfo, rep):
+ # XXX we re-use the TerminalReporter's terminalwriter
+ # because this seems to avoid some encoding related troubles
+ # for not completely clear reasons.
+ tw = node.config.pluginmanager.getplugin("terminalreporter")._tw
+ tw.line()
+ tw.sep(">", "traceback")
+ rep.toterminal(tw)
+ tw.sep(">", "entering PDB")
+ tb = _postmortem_traceback(excinfo)
+ post_mortem(tb)
+ rep._pdbshown = True
+ return rep
+
+
+def _postmortem_traceback(excinfo):
+ # A doctest.UnexpectedException is not useful for post_mortem.
+ # Use the underlying exception instead:
+ from doctest import UnexpectedException
+ if isinstance(excinfo.value, UnexpectedException):
+ return excinfo.value.exc_info[2]
+ else:
+ return excinfo._excinfo[2]
+
+
+def _find_last_non_hidden_frame(stack):
+ i = max(0, len(stack) - 1)
+ while i and stack[i][0].f_locals.get("__tracebackhide__", False):
+ i -= 1
+ return i
+
+
+def post_mortem(t):
+ class Pdb(pytestPDB._pdb_cls):
+ def get_stack(self, f, t):
+ stack, i = pdb.Pdb.get_stack(self, f, t)
+ if f is None:
+ i = _find_last_non_hidden_frame(stack)
+ return stack, i
+ p = Pdb()
+ p.reset()
+ p.interaction(None, t)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/deprecated.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/deprecated.py
new file mode 100644
index 00000000000..9c0fbeca7bc
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/deprecated.py
@@ -0,0 +1,52 @@
+"""
+This module contains deprecation messages and bits of code used elsewhere in the codebase
+that is planned to be removed in the next pytest release.
+
+Keeping it in a central location makes it easy to track what is deprecated and should
+be removed when the time comes.
+"""
+from __future__ import absolute_import, division, print_function
+
+
+class RemovedInPytest4Warning(DeprecationWarning):
+ """warning class for features removed in pytest 4.0"""
+
+
+MAIN_STR_ARGS = 'passing a string to pytest.main() is deprecated, ' \
+ 'pass a list of arguments instead.'
+
+YIELD_TESTS = 'yield tests are deprecated, and scheduled to be removed in pytest 4.0'
+
+FUNCARG_PREFIX = (
+ '{name}: declaring fixtures using "pytest_funcarg__" prefix is deprecated '
+ 'and scheduled to be removed in pytest 4.0. '
+ 'Please remove the prefix and use the @pytest.fixture decorator instead.')
+
+SETUP_CFG_PYTEST = '[pytest] section in setup.cfg files is deprecated, use [tool:pytest] instead.'
+
+GETFUNCARGVALUE = "use of getfuncargvalue is deprecated, use getfixturevalue"
+
+RESULT_LOG = (
+ '--result-log is deprecated and scheduled for removal in pytest 4.0.\n'
+ 'See https://docs.pytest.org/en/latest/usage.html#creating-resultlog-format-files for more information.'
+)
+
+MARK_INFO_ATTRIBUTE = RemovedInPytest4Warning(
+ "MarkInfo objects are deprecated as they contain the merged marks"
+)
+
+MARK_PARAMETERSET_UNPACKING = RemovedInPytest4Warning(
+ "Applying marks directly to parameters is deprecated,"
+ " please use pytest.param(..., marks=...) instead.\n"
+ "For more details, see: https://docs.pytest.org/en/latest/parametrize.html"
+)
+
+COLLECTOR_MAKEITEM = RemovedInPytest4Warning(
+ "pycollector makeitem was removed "
+ "as it is an accidentially leaked internal api"
+)
+
+METAFUNC_ADD_CALL = (
+ "Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0.\n"
+ "Please use Metafunc.parametrize instead."
+)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/doctest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/doctest.py
new file mode 100644
index 00000000000..bba90e551c5
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/doctest.py
@@ -0,0 +1,369 @@
+""" discover and run doctests in modules and test files."""
+from __future__ import absolute_import, division, print_function
+
+import traceback
+
+import pytest
+from _pytest._code.code import ExceptionInfo, ReprFileLocation, TerminalRepr
+from _pytest.fixtures import FixtureRequest
+
+
+DOCTEST_REPORT_CHOICE_NONE = 'none'
+DOCTEST_REPORT_CHOICE_CDIFF = 'cdiff'
+DOCTEST_REPORT_CHOICE_NDIFF = 'ndiff'
+DOCTEST_REPORT_CHOICE_UDIFF = 'udiff'
+DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = 'only_first_failure'
+
+DOCTEST_REPORT_CHOICES = (
+ DOCTEST_REPORT_CHOICE_NONE,
+ DOCTEST_REPORT_CHOICE_CDIFF,
+ DOCTEST_REPORT_CHOICE_NDIFF,
+ DOCTEST_REPORT_CHOICE_UDIFF,
+ DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE,
+)
+
+
+def pytest_addoption(parser):
+ parser.addini('doctest_optionflags', 'option flags for doctests',
+ type="args", default=["ELLIPSIS"])
+ parser.addini("doctest_encoding", 'encoding used for doctest files', default="utf-8")
+ group = parser.getgroup("collect")
+ group.addoption("--doctest-modules",
+ action="store_true", default=False,
+ help="run doctests in all .py modules",
+ dest="doctestmodules")
+ group.addoption("--doctest-report",
+ type=str.lower, default="udiff",
+ help="choose another output format for diffs on doctest failure",
+ choices=DOCTEST_REPORT_CHOICES,
+ dest="doctestreport")
+ group.addoption("--doctest-glob",
+ action="append", default=[], metavar="pat",
+ help="doctests file matching pattern, default: test*.txt",
+ dest="doctestglob")
+ group.addoption("--doctest-ignore-import-errors",
+ action="store_true", default=False,
+ help="ignore doctest ImportErrors",
+ dest="doctest_ignore_import_errors")
+
+
+def pytest_collect_file(path, parent):
+ config = parent.config
+ if path.ext == ".py":
+ if config.option.doctestmodules and not _is_setup_py(config, path, parent):
+ return DoctestModule(path, parent)
+ elif _is_doctest(config, path, parent):
+ return DoctestTextfile(path, parent)
+
+
+def _is_setup_py(config, path, parent):
+ if path.basename != "setup.py":
+ return False
+ contents = path.read()
+ return 'setuptools' in contents or 'distutils' in contents
+
+
+def _is_doctest(config, path, parent):
+ if path.ext in ('.txt', '.rst') and parent.session.isinitpath(path):
+ return True
+ globs = config.getoption("doctestglob") or ['test*.txt']
+ for glob in globs:
+ if path.check(fnmatch=glob):
+ return True
+ return False
+
+
+class ReprFailDoctest(TerminalRepr):
+
+ def __init__(self, reprlocation, lines):
+ self.reprlocation = reprlocation
+ self.lines = lines
+
+ def toterminal(self, tw):
+ for line in self.lines:
+ tw.line(line)
+ self.reprlocation.toterminal(tw)
+
+
+class DoctestItem(pytest.Item):
+ def __init__(self, name, parent, runner=None, dtest=None):
+ super(DoctestItem, self).__init__(name, parent)
+ self.runner = runner
+ self.dtest = dtest
+ self.obj = None
+ self.fixture_request = None
+
+ def setup(self):
+ if self.dtest is not None:
+ self.fixture_request = _setup_fixtures(self)
+ globs = dict(getfixture=self.fixture_request.getfixturevalue)
+ for name, value in self.fixture_request.getfixturevalue('doctest_namespace').items():
+ globs[name] = value
+ self.dtest.globs.update(globs)
+
+ def runtest(self):
+ _check_all_skipped(self.dtest)
+ self.runner.run(self.dtest)
+
+ def repr_failure(self, excinfo):
+ import doctest
+ if excinfo.errisinstance((doctest.DocTestFailure,
+ doctest.UnexpectedException)):
+ doctestfailure = excinfo.value
+ example = doctestfailure.example
+ test = doctestfailure.test
+ filename = test.filename
+ if test.lineno is None:
+ lineno = None
+ else:
+ lineno = test.lineno + example.lineno + 1
+ message = excinfo.type.__name__
+ reprlocation = ReprFileLocation(filename, lineno, message)
+ checker = _get_checker()
+ report_choice = _get_report_choice(self.config.getoption("doctestreport"))
+ if lineno is not None:
+ lines = doctestfailure.test.docstring.splitlines(False)
+ # add line numbers to the left of the error message
+ lines = ["%03d %s" % (i + test.lineno + 1, x)
+ for (i, x) in enumerate(lines)]
+ # trim docstring error lines to 10
+ lines = lines[max(example.lineno - 9, 0):example.lineno + 1]
+ else:
+ lines = ['EXAMPLE LOCATION UNKNOWN, not showing all tests of that example']
+ indent = '>>>'
+ for line in example.source.splitlines():
+ lines.append('??? %s %s' % (indent, line))
+ indent = '...'
+ if excinfo.errisinstance(doctest.DocTestFailure):
+ lines += checker.output_difference(example,
+ doctestfailure.got, report_choice).split("\n")
+ else:
+ inner_excinfo = ExceptionInfo(excinfo.value.exc_info)
+ lines += ["UNEXPECTED EXCEPTION: %s" %
+ repr(inner_excinfo.value)]
+ lines += traceback.format_exception(*excinfo.value.exc_info)
+ return ReprFailDoctest(reprlocation, lines)
+ else:
+ return super(DoctestItem, self).repr_failure(excinfo)
+
+ def reportinfo(self):
+ return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name
+
+
+def _get_flag_lookup():
+ import doctest
+ return dict(DONT_ACCEPT_TRUE_FOR_1=doctest.DONT_ACCEPT_TRUE_FOR_1,
+ DONT_ACCEPT_BLANKLINE=doctest.DONT_ACCEPT_BLANKLINE,
+ NORMALIZE_WHITESPACE=doctest.NORMALIZE_WHITESPACE,
+ ELLIPSIS=doctest.ELLIPSIS,
+ IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL,
+ COMPARISON_FLAGS=doctest.COMPARISON_FLAGS,
+ ALLOW_UNICODE=_get_allow_unicode_flag(),
+ ALLOW_BYTES=_get_allow_bytes_flag(),
+ )
+
+
+def get_optionflags(parent):
+ optionflags_str = parent.config.getini("doctest_optionflags")
+ flag_lookup_table = _get_flag_lookup()
+ flag_acc = 0
+ for flag in optionflags_str:
+ flag_acc |= flag_lookup_table[flag]
+ return flag_acc
+
+
+class DoctestTextfile(pytest.Module):
+ obj = None
+
+ def collect(self):
+ import doctest
+
+ # inspired by doctest.testfile; ideally we would use it directly,
+ # but it doesn't support passing a custom checker
+ encoding = self.config.getini("doctest_encoding")
+ text = self.fspath.read_text(encoding)
+ filename = str(self.fspath)
+ name = self.fspath.basename
+ globs = {'__name__': '__main__'}
+
+ optionflags = get_optionflags(self)
+ runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
+ checker=_get_checker())
+ _fix_spoof_python2(runner, encoding)
+
+ parser = doctest.DocTestParser()
+ test = parser.get_doctest(text, globs, name, filename, 0)
+ if test.examples:
+ yield DoctestItem(test.name, self, runner, test)
+
+
+def _check_all_skipped(test):
+ """raises pytest.skip() if all examples in the given DocTest have the SKIP
+ option set.
+ """
+ import doctest
+ all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples)
+ if all_skipped:
+ pytest.skip('all tests skipped by +SKIP option')
+
+
+class DoctestModule(pytest.Module):
+ def collect(self):
+ import doctest
+ if self.fspath.basename == "conftest.py":
+ module = self.config.pluginmanager._importconftest(self.fspath)
+ else:
+ try:
+ module = self.fspath.pyimport()
+ except ImportError:
+ if self.config.getvalue('doctest_ignore_import_errors'):
+ pytest.skip('unable to import module %r' % self.fspath)
+ else:
+ raise
+ # uses internal doctest module parsing mechanism
+ finder = doctest.DocTestFinder()
+ optionflags = get_optionflags(self)
+ runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
+ checker=_get_checker())
+
+ for test in finder.find(module, module.__name__):
+ if test.examples: # skip empty doctests
+ yield DoctestItem(test.name, self, runner, test)
+
+
+def _setup_fixtures(doctest_item):
+ """
+ Used by DoctestTextfile and DoctestItem to setup fixture information.
+ """
+ def func():
+ pass
+
+ doctest_item.funcargs = {}
+ fm = doctest_item.session._fixturemanager
+ doctest_item._fixtureinfo = fm.getfixtureinfo(node=doctest_item, func=func,
+ cls=None, funcargs=False)
+ fixture_request = FixtureRequest(doctest_item)
+ fixture_request._fillfixtures()
+ return fixture_request
+
+
+def _get_checker():
+ """
+ Returns a doctest.OutputChecker subclass that takes in account the
+ ALLOW_UNICODE option to ignore u'' prefixes in strings and ALLOW_BYTES
+ to strip b'' prefixes.
+ Useful when the same doctest should run in Python 2 and Python 3.
+
+ An inner class is used to avoid importing "doctest" at the module
+ level.
+ """
+ if hasattr(_get_checker, 'LiteralsOutputChecker'):
+ return _get_checker.LiteralsOutputChecker()
+
+ import doctest
+ import re
+
+ class LiteralsOutputChecker(doctest.OutputChecker):
+ """
+ Copied from doctest_nose_plugin.py from the nltk project:
+ https://github.com/nltk/nltk
+
+ Further extended to also support byte literals.
+ """
+
+ _unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE)
+ _bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE)
+
+ def check_output(self, want, got, optionflags):
+ res = doctest.OutputChecker.check_output(self, want, got,
+ optionflags)
+ if res:
+ return True
+
+ allow_unicode = optionflags & _get_allow_unicode_flag()
+ allow_bytes = optionflags & _get_allow_bytes_flag()
+ if not allow_unicode and not allow_bytes:
+ return False
+
+ else: # pragma: no cover
+ def remove_prefixes(regex, txt):
+ return re.sub(regex, r'\1\2', txt)
+
+ if allow_unicode:
+ want = remove_prefixes(self._unicode_literal_re, want)
+ got = remove_prefixes(self._unicode_literal_re, got)
+ if allow_bytes:
+ want = remove_prefixes(self._bytes_literal_re, want)
+ got = remove_prefixes(self._bytes_literal_re, got)
+ res = doctest.OutputChecker.check_output(self, want, got,
+ optionflags)
+ return res
+
+ _get_checker.LiteralsOutputChecker = LiteralsOutputChecker
+ return _get_checker.LiteralsOutputChecker()
+
+
+def _get_allow_unicode_flag():
+ """
+ Registers and returns the ALLOW_UNICODE flag.
+ """
+ import doctest
+ return doctest.register_optionflag('ALLOW_UNICODE')
+
+
+def _get_allow_bytes_flag():
+ """
+ Registers and returns the ALLOW_BYTES flag.
+ """
+ import doctest
+ return doctest.register_optionflag('ALLOW_BYTES')
+
+
+def _get_report_choice(key):
+ """
+ This function returns the actual `doctest` module flag value, we want to do it as late as possible to avoid
+ importing `doctest` and all its dependencies when parsing options, as it adds overhead and breaks tests.
+ """
+ import doctest
+
+ return {
+ DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF,
+ DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF,
+ DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF,
+ DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE,
+ DOCTEST_REPORT_CHOICE_NONE: 0,
+ }[key]
+
+
+def _fix_spoof_python2(runner, encoding):
+ """
+ Installs a "SpoofOut" into the given DebugRunner so it properly deals with unicode output. This
+ should patch only doctests for text files because they don't have a way to declare their
+ encoding. Doctests in docstrings from Python modules don't have the same problem given that
+ Python already decoded the strings.
+
+ This fixes the problem related in issue #2434.
+ """
+ from _pytest.compat import _PY2
+ if not _PY2:
+ return
+
+ from doctest import _SpoofOut
+
+ class UnicodeSpoof(_SpoofOut):
+
+ def getvalue(self):
+ result = _SpoofOut.getvalue(self)
+ if encoding:
+ result = result.decode(encoding)
+ return result
+
+ runner._fakeout = UnicodeSpoof()
+
+
+@pytest.fixture(scope='session')
+def doctest_namespace():
+ """
+ Inject names into the doctest namespace.
+ """
+ return dict()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/fixtures.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/fixtures.py
new file mode 100644
index 00000000000..e09ffaddba7
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/fixtures.py
@@ -0,0 +1,1147 @@
+from __future__ import absolute_import, division, print_function
+
+import functools
+import inspect
+import sys
+import warnings
+from collections import OrderedDict
+
+import attr
+import py
+from py._code.code import FormattedExcinfo
+
+import _pytest
+from _pytest import nodes
+from _pytest._code.code import TerminalRepr
+from _pytest.compat import (
+ NOTSET, exc_clear, _format_args,
+ getfslineno, get_real_func,
+ is_generator, isclass, getimfunc,
+ getlocation, getfuncargnames,
+ safe_getattr,
+ FuncargnamesCompatAttr,
+)
+from _pytest.outcomes import fail, TEST_OUTCOME
+
+
+def pytest_sessionstart(session):
+ import _pytest.python
+
+ scopename2class.update({
+ 'class': _pytest.python.Class,
+ 'module': _pytest.python.Module,
+ 'function': _pytest.main.Item,
+ 'session': _pytest.main.Session,
+ })
+ session._fixturemanager = FixtureManager(session)
+
+
+scopename2class = {}
+
+
+scope2props = dict(session=())
+scope2props["module"] = ("fspath", "module")
+scope2props["class"] = scope2props["module"] + ("cls",)
+scope2props["instance"] = scope2props["class"] + ("instance", )
+scope2props["function"] = scope2props["instance"] + ("function", "keywords")
+
+
+def scopeproperty(name=None, doc=None):
+ def decoratescope(func):
+ scopename = name or func.__name__
+
+ def provide(self):
+ if func.__name__ in scope2props[self.scope]:
+ return func(self)
+ raise AttributeError("%s not available in %s-scoped context" % (
+ scopename, self.scope))
+
+ return property(provide, None, None, func.__doc__)
+ return decoratescope
+
+
+def get_scope_node(node, scope):
+ cls = scopename2class.get(scope)
+ if cls is None:
+ raise ValueError("unknown scope")
+ return node.getparent(cls)
+
+
+def add_funcarg_pseudo_fixture_def(collector, metafunc, fixturemanager):
+ # this function will transform all collected calls to a functions
+ # if they use direct funcargs (i.e. direct parametrization)
+ # because we want later test execution to be able to rely on
+ # an existing FixtureDef structure for all arguments.
+ # XXX we can probably avoid this algorithm if we modify CallSpec2
+ # to directly care for creating the fixturedefs within its methods.
+ if not metafunc._calls[0].funcargs:
+ return # this function call does not have direct parametrization
+ # collect funcargs of all callspecs into a list of values
+ arg2params = {}
+ arg2scope = {}
+ for callspec in metafunc._calls:
+ for argname, argvalue in callspec.funcargs.items():
+ assert argname not in callspec.params
+ callspec.params[argname] = argvalue
+ arg2params_list = arg2params.setdefault(argname, [])
+ callspec.indices[argname] = len(arg2params_list)
+ arg2params_list.append(argvalue)
+ if argname not in arg2scope:
+ scopenum = callspec._arg2scopenum.get(argname,
+ scopenum_function)
+ arg2scope[argname] = scopes[scopenum]
+ callspec.funcargs.clear()
+
+ # register artificial FixtureDef's so that later at test execution
+ # time we can rely on a proper FixtureDef to exist for fixture setup.
+ arg2fixturedefs = metafunc._arg2fixturedefs
+ for argname, valuelist in arg2params.items():
+ # if we have a scope that is higher than function we need
+ # to make sure we only ever create an according fixturedef on
+ # a per-scope basis. We thus store and cache the fixturedef on the
+ # node related to the scope.
+ scope = arg2scope[argname]
+ node = None
+ if scope != "function":
+ node = get_scope_node(collector, scope)
+ if node is None:
+ assert scope == "class" and isinstance(collector, _pytest.python.Module)
+ # use module-level collector for class-scope (for now)
+ node = collector
+ if node and argname in node._name2pseudofixturedef:
+ arg2fixturedefs[argname] = [node._name2pseudofixturedef[argname]]
+ else:
+ fixturedef = FixtureDef(fixturemanager, '', argname,
+ get_direct_param_fixture_func,
+ arg2scope[argname],
+ valuelist, False, False)
+ arg2fixturedefs[argname] = [fixturedef]
+ if node is not None:
+ node._name2pseudofixturedef[argname] = fixturedef
+
+
+def getfixturemarker(obj):
+ """ return fixturemarker or None if it doesn't exist or raised
+ exceptions."""
+ try:
+ return getattr(obj, "_pytestfixturefunction", None)
+ except TEST_OUTCOME:
+ # some objects raise errors like request (from flask import request)
+ # we don't expect them to be fixture functions
+ return None
+
+
+def get_parametrized_fixture_keys(item, scopenum):
+ """ return list of keys for all parametrized arguments which match
+ the specified scope. """
+ assert scopenum < scopenum_function # function
+ try:
+ cs = item.callspec
+ except AttributeError:
+ pass
+ else:
+ # cs.indices.items() is random order of argnames. Need to
+ # sort this so that different calls to
+ # get_parametrized_fixture_keys will be deterministic.
+ for argname, param_index in sorted(cs.indices.items()):
+ if cs._arg2scopenum[argname] != scopenum:
+ continue
+ if scopenum == 0: # session
+ key = (argname, param_index)
+ elif scopenum == 1: # module
+ key = (argname, param_index, item.fspath)
+ elif scopenum == 2: # class
+ key = (argname, param_index, item.fspath, item.cls)
+ yield key
+
+
+# algorithm for sorting on a per-parametrized resource setup basis
+# it is called for scopenum==0 (session) first and performs sorting
+# down to the lower scopes such as to minimize number of "high scope"
+# setups and teardowns
+
+def reorder_items(items):
+ argkeys_cache = {}
+ for scopenum in range(0, scopenum_function):
+ argkeys_cache[scopenum] = d = {}
+ for item in items:
+ keys = OrderedDict.fromkeys(get_parametrized_fixture_keys(item, scopenum))
+ if keys:
+ d[item] = keys
+ return reorder_items_atscope(items, set(), argkeys_cache, 0)
+
+
+def reorder_items_atscope(items, ignore, argkeys_cache, scopenum):
+ if scopenum >= scopenum_function or len(items) < 3:
+ return items
+ items_done = []
+ while 1:
+ items_before, items_same, items_other, newignore = \
+ slice_items(items, ignore, argkeys_cache[scopenum])
+ items_before = reorder_items_atscope(
+ items_before, ignore, argkeys_cache, scopenum + 1)
+ if items_same is None:
+ # nothing to reorder in this scope
+ assert items_other is None
+ return items_done + items_before
+ items_done.extend(items_before)
+ items = items_same + items_other
+ ignore = newignore
+
+
+def slice_items(items, ignore, scoped_argkeys_cache):
+ # we pick the first item which uses a fixture instance in the
+ # requested scope and which we haven't seen yet. We slice the input
+ # items list into a list of items_nomatch, items_same and
+ # items_other
+ if scoped_argkeys_cache: # do we need to do work at all?
+ it = iter(items)
+ # first find a slicing key
+ for i, item in enumerate(it):
+ argkeys = scoped_argkeys_cache.get(item)
+ if argkeys is not None:
+ newargkeys = OrderedDict.fromkeys(k for k in argkeys if k not in ignore)
+ if newargkeys: # found a slicing key
+ slicing_argkey, _ = newargkeys.popitem()
+ items_before = items[:i]
+ items_same = [item]
+ items_other = []
+ # now slice the remainder of the list
+ for item in it:
+ argkeys = scoped_argkeys_cache.get(item)
+ if argkeys and slicing_argkey in argkeys and \
+ slicing_argkey not in ignore:
+ items_same.append(item)
+ else:
+ items_other.append(item)
+ newignore = ignore.copy()
+ newignore.add(slicing_argkey)
+ return (items_before, items_same, items_other, newignore)
+ return items, None, None, None
+
+
+def fillfixtures(function):
+ """ fill missing funcargs for a test function. """
+ try:
+ request = function._request
+ except AttributeError:
+ # XXX this special code path is only expected to execute
+ # with the oejskit plugin. It uses classes with funcargs
+ # and we thus have to work a bit to allow this.
+ fm = function.session._fixturemanager
+ fi = fm.getfixtureinfo(function.parent, function.obj, None)
+ function._fixtureinfo = fi
+ request = function._request = FixtureRequest(function)
+ request._fillfixtures()
+ # prune out funcargs for jstests
+ newfuncargs = {}
+ for name in fi.argnames:
+ newfuncargs[name] = function.funcargs[name]
+ function.funcargs = newfuncargs
+ else:
+ request._fillfixtures()
+
+
+def get_direct_param_fixture_func(request):
+ return request.param
+
+
+class FuncFixtureInfo:
+ def __init__(self, argnames, names_closure, name2fixturedefs):
+ self.argnames = argnames
+ self.names_closure = names_closure
+ self.name2fixturedefs = name2fixturedefs
+
+
+class FixtureRequest(FuncargnamesCompatAttr):
+ """ A request for a fixture from a test or fixture function.
+
+ A request object gives access to the requesting test context
+ and has an optional ``param`` attribute in case
+ the fixture is parametrized indirectly.
+ """
+
+ def __init__(self, pyfuncitem):
+ self._pyfuncitem = pyfuncitem
+ #: fixture for which this request is being performed
+ self.fixturename = None
+ #: Scope string, one of "function", "class", "module", "session"
+ self.scope = "function"
+ self._fixture_values = {} # argname -> fixture value
+ self._fixture_defs = {} # argname -> FixtureDef
+ fixtureinfo = pyfuncitem._fixtureinfo
+ self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
+ self._arg2index = {}
+ self._fixturemanager = pyfuncitem.session._fixturemanager
+
+ @property
+ def fixturenames(self):
+ # backward incompatible note: now a readonly property
+ return list(self._pyfuncitem._fixtureinfo.names_closure)
+
+ @property
+ def node(self):
+ """ underlying collection node (depends on current request scope)"""
+ return self._getscopeitem(self.scope)
+
+ def _getnextfixturedef(self, argname):
+ fixturedefs = self._arg2fixturedefs.get(argname, None)
+ if fixturedefs is None:
+ # we arrive here because of a a dynamic call to
+ # getfixturevalue(argname) usage which was naturally
+ # not known at parsing/collection time
+ parentid = self._pyfuncitem.parent.nodeid
+ fixturedefs = self._fixturemanager.getfixturedefs(argname, parentid)
+ self._arg2fixturedefs[argname] = fixturedefs
+ # fixturedefs list is immutable so we maintain a decreasing index
+ index = self._arg2index.get(argname, 0) - 1
+ if fixturedefs is None or (-index > len(fixturedefs)):
+ raise FixtureLookupError(argname, self)
+ self._arg2index[argname] = index
+ return fixturedefs[index]
+
+ @property
+ def config(self):
+ """ the pytest config object associated with this request. """
+ return self._pyfuncitem.config
+
+ @scopeproperty()
+ def function(self):
+ """ test function object if the request has a per-function scope. """
+ return self._pyfuncitem.obj
+
+ @scopeproperty("class")
+ def cls(self):
+ """ class (can be None) where the test function was collected. """
+ clscol = self._pyfuncitem.getparent(_pytest.python.Class)
+ if clscol:
+ return clscol.obj
+
+ @property
+ def instance(self):
+ """ instance (can be None) on which test function was collected. """
+ # unittest support hack, see _pytest.unittest.TestCaseFunction
+ try:
+ return self._pyfuncitem._testcase
+ except AttributeError:
+ function = getattr(self, "function", None)
+ if function is not None:
+ return py.builtin._getimself(function)
+
+ @scopeproperty()
+ def module(self):
+ """ python module object where the test function was collected. """
+ return self._pyfuncitem.getparent(_pytest.python.Module).obj
+
+ @scopeproperty()
+ def fspath(self):
+ """ the file system path of the test module which collected this test. """
+ return self._pyfuncitem.fspath
+
+ @property
+ def keywords(self):
+ """ keywords/markers dictionary for the underlying node. """
+ return self.node.keywords
+
+ @property
+ def session(self):
+ """ pytest session object. """
+ return self._pyfuncitem.session
+
+ def addfinalizer(self, finalizer):
+ """ add finalizer/teardown function to be called after the
+ last test within the requesting test context finished
+ execution. """
+ # XXX usually this method is shadowed by fixturedef specific ones
+ self._addfinalizer(finalizer, scope=self.scope)
+
+ def _addfinalizer(self, finalizer, scope):
+ colitem = self._getscopeitem(scope)
+ self._pyfuncitem.session._setupstate.addfinalizer(
+ finalizer=finalizer, colitem=colitem)
+
+ def applymarker(self, marker):
+ """ Apply a marker to a single test function invocation.
+ This method is useful if you don't want to have a keyword/marker
+ on all function invocations.
+
+ :arg marker: a :py:class:`_pytest.mark.MarkDecorator` object
+ created by a call to ``pytest.mark.NAME(...)``.
+ """
+ try:
+ self.node.keywords[marker.markname] = marker
+ except AttributeError:
+ raise ValueError(marker)
+
+ def raiseerror(self, msg):
+ """ raise a FixtureLookupError with the given message. """
+ raise self._fixturemanager.FixtureLookupError(None, self, msg)
+
+ def _fillfixtures(self):
+ item = self._pyfuncitem
+ fixturenames = getattr(item, "fixturenames", self.fixturenames)
+ for argname in fixturenames:
+ if argname not in item.funcargs:
+ item.funcargs[argname] = self.getfixturevalue(argname)
+
+ def cached_setup(self, setup, teardown=None, scope="module", extrakey=None):
+ """ (deprecated) Return a testing resource managed by ``setup`` &
+ ``teardown`` calls. ``scope`` and ``extrakey`` determine when the
+ ``teardown`` function will be called so that subsequent calls to
+ ``setup`` would recreate the resource. With pytest-2.3 you often
+ do not need ``cached_setup()`` as you can directly declare a scope
+ on a fixture function and register a finalizer through
+ ``request.addfinalizer()``.
+
+ :arg teardown: function receiving a previously setup resource.
+ :arg setup: a no-argument function creating a resource.
+ :arg scope: a string value out of ``function``, ``class``, ``module``
+ or ``session`` indicating the caching lifecycle of the resource.
+ :arg extrakey: added to internal caching key of (funcargname, scope).
+ """
+ if not hasattr(self.config, '_setupcache'):
+ self.config._setupcache = {} # XXX weakref?
+ cachekey = (self.fixturename, self._getscopeitem(scope), extrakey)
+ cache = self.config._setupcache
+ try:
+ val = cache[cachekey]
+ except KeyError:
+ self._check_scope(self.fixturename, self.scope, scope)
+ val = setup()
+ cache[cachekey] = val
+ if teardown is not None:
+ def finalizer():
+ del cache[cachekey]
+ teardown(val)
+ self._addfinalizer(finalizer, scope=scope)
+ return val
+
+ def getfixturevalue(self, argname):
+ """ Dynamically run a named fixture function.
+
+ Declaring fixtures via function argument is recommended where possible.
+ But if you can only decide whether to use another fixture at test
+ setup time, you may use this function to retrieve it inside a fixture
+ or test function body.
+ """
+ return self._get_active_fixturedef(argname).cached_result[0]
+
+ def getfuncargvalue(self, argname):
+ """ Deprecated, use getfixturevalue. """
+ from _pytest import deprecated
+ warnings.warn(
+ deprecated.GETFUNCARGVALUE,
+ DeprecationWarning,
+ stacklevel=2)
+ return self.getfixturevalue(argname)
+
+ def _get_active_fixturedef(self, argname):
+ try:
+ return self._fixture_defs[argname]
+ except KeyError:
+ try:
+ fixturedef = self._getnextfixturedef(argname)
+ except FixtureLookupError:
+ if argname == "request":
+ class PseudoFixtureDef:
+ cached_result = (self, [0], None)
+ scope = "function"
+ return PseudoFixtureDef
+ raise
+ # remove indent to prevent the python3 exception
+ # from leaking into the call
+ result = self._getfixturevalue(fixturedef)
+ self._fixture_values[argname] = result
+ self._fixture_defs[argname] = fixturedef
+ return fixturedef
+
+ def _get_fixturestack(self):
+ current = self
+ values = []
+ while 1:
+ fixturedef = getattr(current, "_fixturedef", None)
+ if fixturedef is None:
+ values.reverse()
+ return values
+ values.append(fixturedef)
+ current = current._parent_request
+
+ def _getfixturevalue(self, fixturedef):
+ # prepare a subrequest object before calling fixture function
+ # (latter managed by fixturedef)
+ argname = fixturedef.argname
+ funcitem = self._pyfuncitem
+ scope = fixturedef.scope
+ try:
+ param = funcitem.callspec.getparam(argname)
+ except (AttributeError, ValueError):
+ param = NOTSET
+ param_index = 0
+ if fixturedef.params is not None:
+ frame = inspect.stack()[3]
+ frameinfo = inspect.getframeinfo(frame[0])
+ source_path = frameinfo.filename
+ source_lineno = frameinfo.lineno
+ source_path = py.path.local(source_path)
+ if source_path.relto(funcitem.config.rootdir):
+ source_path = source_path.relto(funcitem.config.rootdir)
+ msg = (
+ "The requested fixture has no parameter defined for the "
+ "current test.\n\nRequested fixture '{0}' defined in:\n{1}"
+ "\n\nRequested here:\n{2}:{3}".format(
+ fixturedef.argname,
+ getlocation(fixturedef.func, funcitem.config.rootdir),
+ source_path,
+ source_lineno,
+ )
+ )
+ fail(msg)
+ else:
+ # indices might not be set if old-style metafunc.addcall() was used
+ param_index = funcitem.callspec.indices.get(argname, 0)
+ # if a parametrize invocation set a scope it will override
+ # the static scope defined with the fixture function
+ paramscopenum = funcitem.callspec._arg2scopenum.get(argname)
+ if paramscopenum is not None:
+ scope = scopes[paramscopenum]
+
+ subrequest = SubRequest(self, scope, param, param_index, fixturedef)
+
+ # check if a higher-level scoped fixture accesses a lower level one
+ subrequest._check_scope(argname, self.scope, scope)
+
+ # clear sys.exc_info before invoking the fixture (python bug?)
+ # if its not explicitly cleared it will leak into the call
+ exc_clear()
+ try:
+ # call the fixture function
+ val = fixturedef.execute(request=subrequest)
+ finally:
+ # if fixture function failed it might have registered finalizers
+ self.session._setupstate.addfinalizer(functools.partial(fixturedef.finish, request=subrequest),
+ subrequest.node)
+ return val
+
+ def _check_scope(self, argname, invoking_scope, requested_scope):
+ if argname == "request":
+ return
+ if scopemismatch(invoking_scope, requested_scope):
+ # try to report something helpful
+ lines = self._factorytraceback()
+ fail("ScopeMismatch: You tried to access the %r scoped "
+ "fixture %r with a %r scoped request object, "
+ "involved factories\n%s" % (
+ (requested_scope, argname, invoking_scope, "\n".join(lines))),
+ pytrace=False)
+
+ def _factorytraceback(self):
+ lines = []
+ for fixturedef in self._get_fixturestack():
+ factory = fixturedef.func
+ fs, lineno = getfslineno(factory)
+ p = self._pyfuncitem.session.fspath.bestrelpath(fs)
+ args = _format_args(factory)
+ lines.append("%s:%d: def %s%s" % (
+ p, lineno, factory.__name__, args))
+ return lines
+
+ def _getscopeitem(self, scope):
+ if scope == "function":
+ # this might also be a non-function Item despite its attribute name
+ return self._pyfuncitem
+ node = get_scope_node(self._pyfuncitem, scope)
+ if node is None and scope == "class":
+ # fallback to function item itself
+ node = self._pyfuncitem
+ assert node, 'Could not obtain a node for scope "{}" for function {!r}'.format(scope, self._pyfuncitem)
+ return node
+
+ def __repr__(self):
+ return "<FixtureRequest for %r>" % (self.node)
+
+
+class SubRequest(FixtureRequest):
+ """ a sub request for handling getting a fixture from a
+ test function/fixture. """
+
+ def __init__(self, request, scope, param, param_index, fixturedef):
+ self._parent_request = request
+ self.fixturename = fixturedef.argname
+ if param is not NOTSET:
+ self.param = param
+ self.param_index = param_index
+ self.scope = scope
+ self._fixturedef = fixturedef
+ self._pyfuncitem = request._pyfuncitem
+ self._fixture_values = request._fixture_values
+ self._fixture_defs = request._fixture_defs
+ self._arg2fixturedefs = request._arg2fixturedefs
+ self._arg2index = request._arg2index
+ self._fixturemanager = request._fixturemanager
+
+ def __repr__(self):
+ return "<SubRequest %r for %r>" % (self.fixturename, self._pyfuncitem)
+
+ def addfinalizer(self, finalizer):
+ self._fixturedef.addfinalizer(finalizer)
+
+
+class ScopeMismatchError(Exception):
+ """ A fixture function tries to use a different fixture function which
+ which has a lower scope (e.g. a Session one calls a function one)
+ """
+
+
+scopes = "session module class function".split()
+scopenum_function = scopes.index("function")
+
+
+def scopemismatch(currentscope, newscope):
+ return scopes.index(newscope) > scopes.index(currentscope)
+
+
+def scope2index(scope, descr, where=None):
+ """Look up the index of ``scope`` and raise a descriptive value error
+ if not defined.
+ """
+ try:
+ return scopes.index(scope)
+ except ValueError:
+ raise ValueError(
+ "{0} {1}has an unsupported scope value '{2}'".format(
+ descr, 'from {0} '.format(where) if where else '',
+ scope)
+ )
+
+
+class FixtureLookupError(LookupError):
+ """ could not return a requested Fixture (missing or invalid). """
+
+ def __init__(self, argname, request, msg=None):
+ self.argname = argname
+ self.request = request
+ self.fixturestack = request._get_fixturestack()
+ self.msg = msg
+
+ def formatrepr(self):
+ tblines = []
+ addline = tblines.append
+ stack = [self.request._pyfuncitem.obj]
+ stack.extend(map(lambda x: x.func, self.fixturestack))
+ msg = self.msg
+ if msg is not None:
+ # the last fixture raise an error, let's present
+ # it at the requesting side
+ stack = stack[:-1]
+ for function in stack:
+ fspath, lineno = getfslineno(function)
+ try:
+ lines, _ = inspect.getsourcelines(get_real_func(function))
+ except (IOError, IndexError, TypeError):
+ error_msg = "file %s, line %s: source code not available"
+ addline(error_msg % (fspath, lineno + 1))
+ else:
+ addline("file %s, line %s" % (fspath, lineno + 1))
+ for i, line in enumerate(lines):
+ line = line.rstrip()
+ addline(" " + line)
+ if line.lstrip().startswith('def'):
+ break
+
+ if msg is None:
+ fm = self.request._fixturemanager
+ available = []
+ parentid = self.request._pyfuncitem.parent.nodeid
+ for name, fixturedefs in fm._arg2fixturedefs.items():
+ faclist = list(fm._matchfactories(fixturedefs, parentid))
+ if faclist and name not in available:
+ available.append(name)
+ msg = "fixture %r not found" % (self.argname,)
+ msg += "\n available fixtures: %s" % (", ".join(sorted(available)),)
+ msg += "\n use 'pytest --fixtures [testpath]' for help on them."
+
+ return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname)
+
+
+class FixtureLookupErrorRepr(TerminalRepr):
+ def __init__(self, filename, firstlineno, tblines, errorstring, argname):
+ self.tblines = tblines
+ self.errorstring = errorstring
+ self.filename = filename
+ self.firstlineno = firstlineno
+ self.argname = argname
+
+ def toterminal(self, tw):
+ # tw.line("FixtureLookupError: %s" %(self.argname), red=True)
+ for tbline in self.tblines:
+ tw.line(tbline.rstrip())
+ lines = self.errorstring.split("\n")
+ if lines:
+ tw.line('{0} {1}'.format(FormattedExcinfo.fail_marker,
+ lines[0].strip()), red=True)
+ for line in lines[1:]:
+ tw.line('{0} {1}'.format(FormattedExcinfo.flow_marker,
+ line.strip()), red=True)
+ tw.line()
+ tw.line("%s:%d" % (self.filename, self.firstlineno + 1))
+
+
+def fail_fixturefunc(fixturefunc, msg):
+ fs, lineno = getfslineno(fixturefunc)
+ location = "%s:%s" % (fs, lineno + 1)
+ source = _pytest._code.Source(fixturefunc)
+ fail(msg + ":\n\n" + str(source.indent()) + "\n" + location,
+ pytrace=False)
+
+
+def call_fixture_func(fixturefunc, request, kwargs):
+ yieldctx = is_generator(fixturefunc)
+ if yieldctx:
+ it = fixturefunc(**kwargs)
+ res = next(it)
+
+ def teardown():
+ try:
+ next(it)
+ except StopIteration:
+ pass
+ else:
+ fail_fixturefunc(fixturefunc,
+ "yield_fixture function has more than one 'yield'")
+
+ request.addfinalizer(teardown)
+ else:
+ res = fixturefunc(**kwargs)
+ return res
+
+
+class FixtureDef:
+ """ A container for a factory definition. """
+
+ def __init__(self, fixturemanager, baseid, argname, func, scope, params,
+ unittest=False, ids=None):
+ self._fixturemanager = fixturemanager
+ self.baseid = baseid or ''
+ self.has_location = baseid is not None
+ self.func = func
+ self.argname = argname
+ self.scope = scope
+ self.scopenum = scope2index(
+ scope or "function",
+ descr='fixture {0}'.format(func.__name__),
+ where=baseid
+ )
+ self.params = params
+ self.argnames = getfuncargnames(func, is_method=unittest)
+ self.unittest = unittest
+ self.ids = ids
+ self._finalizers = []
+
+ def addfinalizer(self, finalizer):
+ self._finalizers.append(finalizer)
+
+ def finish(self, request):
+ exceptions = []
+ try:
+ while self._finalizers:
+ try:
+ func = self._finalizers.pop()
+ func()
+ except: # noqa
+ exceptions.append(sys.exc_info())
+ if exceptions:
+ e = exceptions[0]
+ del exceptions # ensure we don't keep all frames alive because of the traceback
+ py.builtin._reraise(*e)
+
+ finally:
+ hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
+ hook.pytest_fixture_post_finalizer(fixturedef=self, request=request)
+ # even if finalization fails, we invalidate
+ # the cached fixture value and remove
+ # all finalizers because they may be bound methods which will
+ # keep instances alive
+ if hasattr(self, "cached_result"):
+ del self.cached_result
+ self._finalizers = []
+
+ def execute(self, request):
+ # get required arguments and register our own finish()
+ # with their finalization
+ for argname in self.argnames:
+ fixturedef = request._get_active_fixturedef(argname)
+ if argname != "request":
+ fixturedef.addfinalizer(functools.partial(self.finish, request=request))
+
+ my_cache_key = request.param_index
+ cached_result = getattr(self, "cached_result", None)
+ if cached_result is not None:
+ result, cache_key, err = cached_result
+ if my_cache_key == cache_key:
+ if err is not None:
+ py.builtin._reraise(*err)
+ else:
+ return result
+ # we have a previous but differently parametrized fixture instance
+ # so we need to tear it down before creating a new one
+ self.finish(request)
+ assert not hasattr(self, "cached_result")
+
+ hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
+ return hook.pytest_fixture_setup(fixturedef=self, request=request)
+
+ def __repr__(self):
+ return ("<FixtureDef name=%r scope=%r baseid=%r >" %
+ (self.argname, self.scope, self.baseid))
+
+
+def pytest_fixture_setup(fixturedef, request):
+ """ Execution of fixture setup. """
+ kwargs = {}
+ for argname in fixturedef.argnames:
+ fixdef = request._get_active_fixturedef(argname)
+ result, arg_cache_key, exc = fixdef.cached_result
+ request._check_scope(argname, request.scope, fixdef.scope)
+ kwargs[argname] = result
+
+ fixturefunc = fixturedef.func
+ if fixturedef.unittest:
+ if request.instance is not None:
+ # bind the unbound method to the TestCase instance
+ fixturefunc = fixturedef.func.__get__(request.instance)
+ else:
+ # the fixture function needs to be bound to the actual
+ # request.instance so that code working with "fixturedef" behaves
+ # as expected.
+ if request.instance is not None:
+ fixturefunc = getimfunc(fixturedef.func)
+ if fixturefunc != fixturedef.func:
+ fixturefunc = fixturefunc.__get__(request.instance)
+ my_cache_key = request.param_index
+ try:
+ result = call_fixture_func(fixturefunc, request, kwargs)
+ except TEST_OUTCOME:
+ fixturedef.cached_result = (None, my_cache_key, sys.exc_info())
+ raise
+ fixturedef.cached_result = (result, my_cache_key, None)
+ return result
+
+
+def _ensure_immutable_ids(ids):
+ if ids is None:
+ return
+ if callable(ids):
+ return ids
+ return tuple(ids)
+
+
+@attr.s(frozen=True)
+class FixtureFunctionMarker(object):
+ scope = attr.ib()
+ params = attr.ib(convert=attr.converters.optional(tuple))
+ autouse = attr.ib(default=False)
+ ids = attr.ib(default=None, convert=_ensure_immutable_ids)
+ name = attr.ib(default=None)
+
+ def __call__(self, function):
+ if isclass(function):
+ raise ValueError(
+ "class fixtures not supported (may be in the future)")
+ function._pytestfixturefunction = self
+ return function
+
+
+def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
+ """ (return a) decorator to mark a fixture factory function.
+
+ This decorator can be used (with or without parameters) to define a
+ fixture function. The name of the fixture function can later be
+ referenced to cause its invocation ahead of running tests: test
+ modules or classes can use the pytest.mark.usefixtures(fixturename)
+ marker. Test functions can directly use fixture names as input
+ arguments in which case the fixture instance returned from the fixture
+ function will be injected.
+
+ :arg scope: the scope for which this fixture is shared, one of
+ "function" (default), "class", "module" or "session".
+
+ :arg params: an optional list of parameters which will cause multiple
+ invocations of the fixture function and all of the tests
+ using it.
+
+ :arg autouse: if True, the fixture func is activated for all tests that
+ can see it. If False (the default) then an explicit
+ reference is needed to activate the fixture.
+
+ :arg ids: list of string ids each corresponding to the params
+ so that they are part of the test id. If no ids are provided
+ they will be generated automatically from the params.
+
+ :arg name: the name of the fixture. This defaults to the name of the
+ decorated function. If a fixture is used in the same module in
+ which it is defined, the function name of the fixture will be
+ shadowed by the function arg that requests the fixture; one way
+ to resolve this is to name the decorated function
+ ``fixture_<fixturename>`` and then use
+ ``@pytest.fixture(name='<fixturename>')``.
+
+ Fixtures can optionally provide their values to test functions using a ``yield`` statement,
+ instead of ``return``. In this case, the code block after the ``yield`` statement is executed
+ as teardown code regardless of the test outcome. A fixture function must yield exactly once.
+ """
+ if callable(scope) and params is None and autouse is False:
+ # direct decoration
+ return FixtureFunctionMarker(
+ "function", params, autouse, name=name)(scope)
+ if params is not None and not isinstance(params, (list, tuple)):
+ params = list(params)
+ return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name)
+
+
+def yield_fixture(scope="function", params=None, autouse=False, ids=None, name=None):
+ """ (return a) decorator to mark a yield-fixture factory function.
+
+ .. deprecated:: 3.0
+ Use :py:func:`pytest.fixture` directly instead.
+ """
+ if callable(scope) and params is None and not autouse:
+ # direct decoration
+ return FixtureFunctionMarker(
+ "function", params, autouse, ids=ids, name=name)(scope)
+ else:
+ return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name)
+
+
+defaultfuncargprefixmarker = fixture()
+
+
+@fixture(scope="session")
+def pytestconfig(request):
+ """ the pytest config object with access to command line opts."""
+ return request.config
+
+
+class FixtureManager:
+ """
+ pytest fixtures definitions and information is stored and managed
+ from this class.
+
+ During collection fm.parsefactories() is called multiple times to parse
+ fixture function definitions into FixtureDef objects and internal
+ data structures.
+
+ During collection of test functions, metafunc-mechanics instantiate
+ a FuncFixtureInfo object which is cached per node/func-name.
+ This FuncFixtureInfo object is later retrieved by Function nodes
+ which themselves offer a fixturenames attribute.
+
+ The FuncFixtureInfo object holds information about fixtures and FixtureDefs
+ relevant for a particular function. An initial list of fixtures is
+ assembled like this:
+
+ - ini-defined usefixtures
+ - autouse-marked fixtures along the collection chain up from the function
+ - usefixtures markers at module/class/function level
+ - test function funcargs
+
+ Subsequently the funcfixtureinfo.fixturenames attribute is computed
+ as the closure of the fixtures needed to setup the initial fixtures,
+ i. e. fixtures needed by fixture functions themselves are appended
+ to the fixturenames list.
+
+ Upon the test-setup phases all fixturenames are instantiated, retrieved
+ by a lookup of their FuncFixtureInfo.
+ """
+
+ _argprefix = "pytest_funcarg__"
+ FixtureLookupError = FixtureLookupError
+ FixtureLookupErrorRepr = FixtureLookupErrorRepr
+
+ def __init__(self, session):
+ self.session = session
+ self.config = session.config
+ self._arg2fixturedefs = {}
+ self._holderobjseen = set()
+ self._arg2finish = {}
+ self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))]
+ session.config.pluginmanager.register(self, "funcmanage")
+
+ def getfixtureinfo(self, node, func, cls, funcargs=True):
+ if funcargs and not hasattr(node, "nofuncargs"):
+ argnames = getfuncargnames(func, cls=cls)
+ else:
+ argnames = ()
+ usefixtures = getattr(func, "usefixtures", None)
+ initialnames = argnames
+ if usefixtures is not None:
+ initialnames = usefixtures.args + initialnames
+ fm = node.session._fixturemanager
+ names_closure, arg2fixturedefs = fm.getfixtureclosure(initialnames,
+ node)
+ return FuncFixtureInfo(argnames, names_closure, arg2fixturedefs)
+
+ def pytest_plugin_registered(self, plugin):
+ nodeid = None
+ try:
+ p = py.path.local(plugin.__file__)
+ except AttributeError:
+ pass
+ else:
+ # construct the base nodeid which is later used to check
+ # what fixtures are visible for particular tests (as denoted
+ # by their test id)
+ if p.basename.startswith("conftest.py"):
+ nodeid = p.dirpath().relto(self.config.rootdir)
+ if p.sep != nodes.SEP:
+ nodeid = nodeid.replace(p.sep, nodes.SEP)
+ self.parsefactories(plugin, nodeid)
+
+ def _getautousenames(self, nodeid):
+ """ return a tuple of fixture names to be used. """
+ autousenames = []
+ for baseid, basenames in self._nodeid_and_autousenames:
+ if nodeid.startswith(baseid):
+ if baseid:
+ i = len(baseid)
+ nextchar = nodeid[i:i + 1]
+ if nextchar and nextchar not in ":/":
+ continue
+ autousenames.extend(basenames)
+ # make sure autousenames are sorted by scope, scopenum 0 is session
+ autousenames.sort(
+ key=lambda x: self._arg2fixturedefs[x][-1].scopenum)
+ return autousenames
+
+ def getfixtureclosure(self, fixturenames, parentnode):
+ # collect the closure of all fixtures , starting with the given
+ # fixturenames as the initial set. As we have to visit all
+ # factory definitions anyway, we also return a arg2fixturedefs
+ # mapping so that the caller can reuse it and does not have
+ # to re-discover fixturedefs again for each fixturename
+ # (discovering matching fixtures for a given name/node is expensive)
+
+ parentid = parentnode.nodeid
+ fixturenames_closure = self._getautousenames(parentid)
+
+ def merge(otherlist):
+ for arg in otherlist:
+ if arg not in fixturenames_closure:
+ fixturenames_closure.append(arg)
+
+ merge(fixturenames)
+ arg2fixturedefs = {}
+ lastlen = -1
+ while lastlen != len(fixturenames_closure):
+ lastlen = len(fixturenames_closure)
+ for argname in fixturenames_closure:
+ if argname in arg2fixturedefs:
+ continue
+ fixturedefs = self.getfixturedefs(argname, parentid)
+ if fixturedefs:
+ arg2fixturedefs[argname] = fixturedefs
+ merge(fixturedefs[-1].argnames)
+ return fixturenames_closure, arg2fixturedefs
+
+ def pytest_generate_tests(self, metafunc):
+ for argname in metafunc.fixturenames:
+ faclist = metafunc._arg2fixturedefs.get(argname)
+ if faclist:
+ fixturedef = faclist[-1]
+ if fixturedef.params is not None:
+ parametrize_func = getattr(metafunc.function, 'parametrize', None)
+ func_params = getattr(parametrize_func, 'args', [[None]])
+ func_kwargs = getattr(parametrize_func, 'kwargs', {})
+ # skip directly parametrized arguments
+ if "argnames" in func_kwargs:
+ argnames = parametrize_func.kwargs["argnames"]
+ else:
+ argnames = func_params[0]
+ if not isinstance(argnames, (tuple, list)):
+ argnames = [x.strip() for x in argnames.split(",") if x.strip()]
+ if argname not in func_params and argname not in argnames:
+ metafunc.parametrize(argname, fixturedef.params,
+ indirect=True, scope=fixturedef.scope,
+ ids=fixturedef.ids)
+ else:
+ continue # will raise FixtureLookupError at setup time
+
+ def pytest_collection_modifyitems(self, items):
+ # separate parametrized setups
+ items[:] = reorder_items(items)
+
+ def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
+ if nodeid is not NOTSET:
+ holderobj = node_or_obj
+ else:
+ holderobj = node_or_obj.obj
+ nodeid = node_or_obj.nodeid
+ if holderobj in self._holderobjseen:
+ return
+ self._holderobjseen.add(holderobj)
+ autousenames = []
+ for name in dir(holderobj):
+ # The attribute can be an arbitrary descriptor, so the attribute
+ # access below can raise. safe_getatt() ignores such exceptions.
+ obj = safe_getattr(holderobj, name, None)
+ # fixture functions have a pytest_funcarg__ prefix (pre-2.3 style)
+ # or are "@pytest.fixture" marked
+ marker = getfixturemarker(obj)
+ if marker is None:
+ if not name.startswith(self._argprefix):
+ continue
+ if not callable(obj):
+ continue
+ marker = defaultfuncargprefixmarker
+ from _pytest import deprecated
+ self.config.warn('C1', deprecated.FUNCARG_PREFIX.format(name=name), nodeid=nodeid)
+ name = name[len(self._argprefix):]
+ elif not isinstance(marker, FixtureFunctionMarker):
+ # magic globals with __getattr__ might have got us a wrong
+ # fixture attribute
+ continue
+ else:
+ if marker.name:
+ name = marker.name
+ msg = 'fixtures cannot have "pytest_funcarg__" prefix ' \
+ 'and be decorated with @pytest.fixture:\n%s' % name
+ assert not name.startswith(self._argprefix), msg
+
+ fixture_def = FixtureDef(self, nodeid, name, obj,
+ marker.scope, marker.params,
+ unittest=unittest, ids=marker.ids)
+
+ faclist = self._arg2fixturedefs.setdefault(name, [])
+ if fixture_def.has_location:
+ faclist.append(fixture_def)
+ else:
+ # fixturedefs with no location are at the front
+ # so this inserts the current fixturedef after the
+ # existing fixturedefs from external plugins but
+ # before the fixturedefs provided in conftests.
+ i = len([f for f in faclist if not f.has_location])
+ faclist.insert(i, fixture_def)
+ if marker.autouse:
+ autousenames.append(name)
+
+ if autousenames:
+ self._nodeid_and_autousenames.append((nodeid or '', autousenames))
+
+ def getfixturedefs(self, argname, nodeid):
+ """
+ Gets a list of fixtures which are applicable to the given node id.
+
+ :param str argname: name of the fixture to search for
+ :param str nodeid: full node id of the requesting test.
+ :return: list[FixtureDef]
+ """
+ try:
+ fixturedefs = self._arg2fixturedefs[argname]
+ except KeyError:
+ return None
+ else:
+ return tuple(self._matchfactories(fixturedefs, nodeid))
+
+ def _matchfactories(self, fixturedefs, nodeid):
+ for fixturedef in fixturedefs:
+ if nodes.ischildnode(fixturedef.baseid, nodeid):
+ yield fixturedef
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/freeze_support.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/freeze_support.py
new file mode 100644
index 00000000000..97147a88250
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/freeze_support.py
@@ -0,0 +1,43 @@
+"""
+Provides a function to report all internal modules for using freezing tools
+pytest
+"""
+from __future__ import absolute_import, division, print_function
+
+
+def freeze_includes():
+ """
+ Returns a list of module names used by py.test that should be
+ included by cx_freeze.
+ """
+ import py
+ import _pytest
+ result = list(_iter_all_modules(py))
+ result += list(_iter_all_modules(_pytest))
+ return result
+
+
+def _iter_all_modules(package, prefix=''):
+ """
+ Iterates over the names of all modules that can be found in the given
+ package, recursively.
+ Example:
+ _iter_all_modules(_pytest) ->
+ ['_pytest.assertion.newinterpret',
+ '_pytest.capture',
+ '_pytest.core',
+ ...
+ ]
+ """
+ import os
+ import pkgutil
+ if type(package) is not str:
+ path, prefix = package.__path__[0], package.__name__ + '.'
+ else:
+ path = package
+ for _, name, is_package in pkgutil.iter_modules([path]):
+ if is_package:
+ for m in _iter_all_modules(os.path.join(path, name), prefix=name + '.'):
+ yield prefix + m
+ else:
+ yield prefix + name
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/helpconfig.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/helpconfig.py
new file mode 100644
index 00000000000..e744637f866
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/helpconfig.py
@@ -0,0 +1,184 @@
+""" version info, help messages, tracing configuration. """
+from __future__ import absolute_import, division, print_function
+
+import py
+import pytest
+from _pytest.config import PrintHelp
+import os
+import sys
+from argparse import Action
+
+
+class HelpAction(Action):
+ """This is an argparse Action that will raise an exception in
+ order to skip the rest of the argument parsing when --help is passed.
+ This prevents argparse from quitting due to missing required arguments
+ when any are defined, for example by ``pytest_addoption``.
+ This is similar to the way that the builtin argparse --help option is
+ implemented by raising SystemExit.
+ """
+
+ def __init__(self,
+ option_strings,
+ dest=None,
+ default=False,
+ help=None):
+ super(HelpAction, self).__init__(
+ option_strings=option_strings,
+ dest=dest,
+ const=True,
+ default=default,
+ nargs=0,
+ help=help)
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ setattr(namespace, self.dest, self.const)
+
+ # We should only skip the rest of the parsing after preparse is done
+ if getattr(parser._parser, 'after_preparse', False):
+ raise PrintHelp
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup('debugconfig')
+ group.addoption('--version', action="store_true",
+ help="display pytest lib version and import information.")
+ group._addoption("-h", "--help", action=HelpAction, dest="help",
+ help="show help message and configuration info")
+ group._addoption('-p', action="append", dest="plugins", default=[],
+ metavar="name",
+ help="early-load given plugin (multi-allowed). "
+ "To avoid loading of plugins, use the `no:` prefix, e.g. "
+ "`no:doctest`.")
+ group.addoption('--traceconfig', '--trace-config',
+ action="store_true", default=False,
+ help="trace considerations of conftest.py files."),
+ group.addoption('--debug',
+ action="store_true", dest="debug", default=False,
+ help="store internal tracing debug information in 'pytestdebug.log'.")
+ group._addoption(
+ '-o', '--override-ini', nargs='*', dest="override_ini",
+ action="append",
+ help="override config option with option=value style, e.g. `-o xfail_strict=True`.")
+
+
+@pytest.hookimpl(hookwrapper=True)
+def pytest_cmdline_parse():
+ outcome = yield
+ config = outcome.get_result()
+ if config.option.debug:
+ path = os.path.abspath("pytestdebug.log")
+ debugfile = open(path, 'w')
+ debugfile.write("versions pytest-%s, py-%s, "
+ "python-%s\ncwd=%s\nargs=%s\n\n" % (
+ pytest.__version__, py.__version__,
+ ".".join(map(str, sys.version_info)),
+ os.getcwd(), config._origargs))
+ config.trace.root.setwriter(debugfile.write)
+ undo_tracing = config.pluginmanager.enable_tracing()
+ sys.stderr.write("writing pytestdebug information to %s\n" % path)
+
+ def unset_tracing():
+ debugfile.close()
+ sys.stderr.write("wrote pytestdebug information to %s\n" %
+ debugfile.name)
+ config.trace.root.setwriter(None)
+ undo_tracing()
+
+ config.add_cleanup(unset_tracing)
+
+
+def pytest_cmdline_main(config):
+ if config.option.version:
+ p = py.path.local(pytest.__file__)
+ sys.stderr.write("This is pytest version %s, imported from %s\n" %
+ (pytest.__version__, p))
+ plugininfo = getpluginversioninfo(config)
+ if plugininfo:
+ for line in plugininfo:
+ sys.stderr.write(line + "\n")
+ return 0
+ elif config.option.help:
+ config._do_configure()
+ showhelp(config)
+ config._ensure_unconfigure()
+ return 0
+
+
+def showhelp(config):
+ reporter = config.pluginmanager.get_plugin('terminalreporter')
+ tw = reporter._tw
+ tw.write(config._parser.optparser.format_help())
+ tw.line()
+ tw.line()
+ tw.line("[pytest] ini-options in the first "
+ "pytest.ini|tox.ini|setup.cfg file found:")
+ tw.line()
+
+ for name in config._parser._ininames:
+ help, type, default = config._parser._inidict[name]
+ if type is None:
+ type = "string"
+ spec = "%s (%s)" % (name, type)
+ line = " %-24s %s" % (spec, help)
+ tw.line(line[:tw.fullwidth])
+
+ tw.line()
+ tw.line("environment variables:")
+ vars = [
+ ("PYTEST_ADDOPTS", "extra command line options"),
+ ("PYTEST_PLUGINS", "comma-separated plugins to load during startup"),
+ ("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals")
+ ]
+ for name, help in vars:
+ tw.line(" %-24s %s" % (name, help))
+ tw.line()
+ tw.line()
+
+ tw.line("to see available markers type: pytest --markers")
+ tw.line("to see available fixtures type: pytest --fixtures")
+ tw.line("(shown according to specified file_or_dir or current dir "
+ "if not specified)")
+
+ for warningreport in reporter.stats.get('warnings', []):
+ tw.line("warning : " + warningreport.message, red=True)
+ return
+
+
+conftest_options = [
+ ('pytest_plugins', 'list of plugin names to load'),
+]
+
+
+def getpluginversioninfo(config):
+ lines = []
+ plugininfo = config.pluginmanager.list_plugin_distinfo()
+ if plugininfo:
+ lines.append("setuptools registered plugins:")
+ for plugin, dist in plugininfo:
+ loc = getattr(plugin, '__file__', repr(plugin))
+ content = "%s-%s at %s" % (dist.project_name, dist.version, loc)
+ lines.append(" " + content)
+ return lines
+
+
+def pytest_report_header(config):
+ lines = []
+ if config.option.debug or config.option.traceconfig:
+ lines.append("using: pytest-%s pylib-%s" %
+ (pytest.__version__, py.__version__))
+
+ verinfo = getpluginversioninfo(config)
+ if verinfo:
+ lines.extend(verinfo)
+
+ if config.option.traceconfig:
+ lines.append("active plugins:")
+ items = config.pluginmanager.list_name_plugin()
+ for name, plugin in items:
+ if hasattr(plugin, '__file__'):
+ r = plugin.__file__
+ else:
+ r = repr(plugin)
+ lines.append(" %-20s: %s" % (name, r))
+ return lines
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/hookspec.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/hookspec.py
new file mode 100644
index 00000000000..440bf99d375
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/hookspec.py
@@ -0,0 +1,423 @@
+""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
+
+from pluggy import HookspecMarker
+
+hookspec = HookspecMarker("pytest")
+
+# -------------------------------------------------------------------------
+# Initialization hooks called for every plugin
+# -------------------------------------------------------------------------
+
+
+@hookspec(historic=True)
+def pytest_addhooks(pluginmanager):
+ """called at plugin registration time to allow adding new hooks via a call to
+ pluginmanager.add_hookspecs(module_or_class, prefix)."""
+
+
+@hookspec(historic=True)
+def pytest_namespace():
+ """
+ DEPRECATED: this hook causes direct monkeypatching on pytest, its use is strongly discouraged
+ return dict of name->object to be made globally available in
+ the pytest namespace. This hook is called at plugin registration
+ time.
+ """
+
+
+@hookspec(historic=True)
+def pytest_plugin_registered(plugin, manager):
+ """ a new pytest plugin got registered. """
+
+
+@hookspec(historic=True)
+def pytest_addoption(parser):
+ """register argparse-style options and ini-style config values,
+ called once at the beginning of a test run.
+
+ .. note::
+
+ This function should be implemented only in plugins or ``conftest.py``
+ files situated at the tests root directory due to how pytest
+ :ref:`discovers plugins during startup <pluginorder>`.
+
+ :arg parser: To add command line options, call
+ :py:func:`parser.addoption(...) <_pytest.config.Parser.addoption>`.
+ To add ini-file values call :py:func:`parser.addini(...)
+ <_pytest.config.Parser.addini>`.
+
+ Options can later be accessed through the
+ :py:class:`config <_pytest.config.Config>` object, respectively:
+
+ - :py:func:`config.getoption(name) <_pytest.config.Config.getoption>` to
+ retrieve the value of a command line option.
+
+ - :py:func:`config.getini(name) <_pytest.config.Config.getini>` to retrieve
+ a value read from an ini-style file.
+
+ The config object is passed around on many internal objects via the ``.config``
+ attribute or can be retrieved as the ``pytestconfig`` fixture or accessed
+ via (deprecated) ``pytest.config``.
+ """
+
+
+@hookspec(historic=True)
+def pytest_configure(config):
+ """
+ Allows plugins and conftest files to perform initial configuration.
+
+ This hook is called for every plugin and initial conftest file
+ after command line options have been parsed.
+
+ After that, the hook is called for other conftest files as they are
+ imported.
+
+ :arg config: pytest config object
+ :type config: _pytest.config.Config
+ """
+
+# -------------------------------------------------------------------------
+# Bootstrapping hooks called for plugins registered early enough:
+# internal and 3rd party plugins as well as directly
+# discoverable conftest.py local plugins.
+# -------------------------------------------------------------------------
+
+
+@hookspec(firstresult=True)
+def pytest_cmdline_parse(pluginmanager, args):
+ """return initialized config object, parsing the specified args.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+def pytest_cmdline_preparse(config, args):
+ """(deprecated) modify command line arguments before option parsing. """
+
+
+@hookspec(firstresult=True)
+def pytest_cmdline_main(config):
+ """ called for performing the main command line action. The default
+ implementation will invoke the configure hooks and runtest_mainloop.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+def pytest_load_initial_conftests(early_config, parser, args):
+ """ implements the loading of initial conftest files ahead
+ of command line option parsing. """
+
+
+# -------------------------------------------------------------------------
+# collection hooks
+# -------------------------------------------------------------------------
+
+@hookspec(firstresult=True)
+def pytest_collection(session):
+ """ perform the collection protocol for the given session.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+def pytest_collection_modifyitems(session, config, items):
+ """ called after collection has been performed, may filter or re-order
+ the items in-place."""
+
+
+def pytest_collection_finish(session):
+ """ called after collection has been performed and modified. """
+
+
+@hookspec(firstresult=True)
+def pytest_ignore_collect(path, config):
+ """ return True to prevent considering this path for collection.
+ This hook is consulted for all files and directories prior to calling
+ more specific hooks.
+
+ Stops at first non-None result, see :ref:`firstresult`
+ """
+
+
+@hookspec(firstresult=True)
+def pytest_collect_directory(path, parent):
+ """ called before traversing a directory for collection files.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+def pytest_collect_file(path, parent):
+ """ return collection Node or None for the given path. Any new node
+ needs to have the specified ``parent`` as a parent."""
+
+# logging hooks for collection
+
+
+def pytest_collectstart(collector):
+ """ collector starts collecting. """
+
+
+def pytest_itemcollected(item):
+ """ we just collected a test item. """
+
+
+def pytest_collectreport(report):
+ """ collector finished collecting. """
+
+
+def pytest_deselected(items):
+ """ called for test items deselected by keyword. """
+
+
+@hookspec(firstresult=True)
+def pytest_make_collect_report(collector):
+ """ perform ``collector.collect()`` and return a CollectReport.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+# -------------------------------------------------------------------------
+# Python test function related hooks
+# -------------------------------------------------------------------------
+
+
+@hookspec(firstresult=True)
+def pytest_pycollect_makemodule(path, parent):
+ """ return a Module collector or None for the given path.
+ This hook will be called for each matching test module path.
+ The pytest_collect_file hook needs to be used if you want to
+ create test modules for files that do not match as a test module.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+@hookspec(firstresult=True)
+def pytest_pycollect_makeitem(collector, name, obj):
+ """ return custom item/collector for a python object in a module, or None.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+@hookspec(firstresult=True)
+def pytest_pyfunc_call(pyfuncitem):
+ """ call underlying test function.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+def pytest_generate_tests(metafunc):
+ """ generate (multiple) parametrized calls to a test function."""
+
+
+@hookspec(firstresult=True)
+def pytest_make_parametrize_id(config, val, argname):
+ """Return a user-friendly string representation of the given ``val`` that will be used
+ by @pytest.mark.parametrize calls. Return None if the hook doesn't know about ``val``.
+ The parameter name is available as ``argname``, if required.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+# -------------------------------------------------------------------------
+# generic runtest related hooks
+# -------------------------------------------------------------------------
+
+
+@hookspec(firstresult=True)
+def pytest_runtestloop(session):
+ """ called for performing the main runtest loop
+ (after collection finished).
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+def pytest_itemstart(item, node):
+ """ (deprecated, use pytest_runtest_logstart). """
+
+
+@hookspec(firstresult=True)
+def pytest_runtest_protocol(item, nextitem):
+ """ implements the runtest_setup/call/teardown protocol for
+ the given test item, including capturing exceptions and calling
+ reporting hooks.
+
+ :arg item: test item for which the runtest protocol is performed.
+
+ :arg nextitem: the scheduled-to-be-next test item (or None if this
+ is the end my friend). This argument is passed on to
+ :py:func:`pytest_runtest_teardown`.
+
+ :return boolean: True if no further hook implementations should be invoked.
+
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+def pytest_runtest_logstart(nodeid, location):
+ """ signal the start of running a single test item. """
+
+
+def pytest_runtest_setup(item):
+ """ called before ``pytest_runtest_call(item)``. """
+
+
+def pytest_runtest_call(item):
+ """ called to execute the test ``item``. """
+
+
+def pytest_runtest_teardown(item, nextitem):
+ """ called after ``pytest_runtest_call``.
+
+ :arg nextitem: the scheduled-to-be-next test item (None if no further
+ test item is scheduled). This argument can be used to
+ perform exact teardowns, i.e. calling just enough finalizers
+ so that nextitem only needs to call setup-functions.
+ """
+
+
+@hookspec(firstresult=True)
+def pytest_runtest_makereport(item, call):
+ """ return a :py:class:`_pytest.runner.TestReport` object
+ for the given :py:class:`pytest.Item <_pytest.main.Item>` and
+ :py:class:`_pytest.runner.CallInfo`.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+def pytest_runtest_logreport(report):
+ """ process a test setup/call/teardown report relating to
+ the respective phase of executing a test. """
+
+# -------------------------------------------------------------------------
+# Fixture related hooks
+# -------------------------------------------------------------------------
+
+
+@hookspec(firstresult=True)
+def pytest_fixture_setup(fixturedef, request):
+ """ performs fixture setup execution.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+def pytest_fixture_post_finalizer(fixturedef, request):
+ """ called after fixture teardown, but before the cache is cleared so
+ the fixture result cache ``fixturedef.cached_result`` can
+ still be accessed."""
+
+# -------------------------------------------------------------------------
+# test session related hooks
+# -------------------------------------------------------------------------
+
+
+def pytest_sessionstart(session):
+ """ before session.main() is called. """
+
+
+def pytest_sessionfinish(session, exitstatus):
+ """ whole test run finishes. """
+
+
+def pytest_unconfigure(config):
+ """ called before test process is exited. """
+
+
+# -------------------------------------------------------------------------
+# hooks for customizing the assert methods
+# -------------------------------------------------------------------------
+
+def pytest_assertrepr_compare(config, op, left, right):
+ """return explanation for comparisons in failing assert expressions.
+
+ Return None for no custom explanation, otherwise return a list
+ of strings. The strings will be joined by newlines but any newlines
+ *in* a string will be escaped. Note that all but the first line will
+ be indented slightly, the intention is for the first line to be a summary.
+ """
+
+# -------------------------------------------------------------------------
+# hooks for influencing reporting (invoked from _pytest_terminal)
+# -------------------------------------------------------------------------
+
+
+def pytest_report_header(config, startdir):
+ """ return a string or list of strings to be displayed as header info for terminal reporting.
+
+ :param config: the pytest config object.
+ :param startdir: py.path object with the starting dir
+
+ .. note::
+
+ This function should be implemented only in plugins or ``conftest.py``
+ files situated at the tests root directory due to how pytest
+ :ref:`discovers plugins during startup <pluginorder>`.
+ """
+
+
+def pytest_report_collectionfinish(config, startdir, items):
+ """
+ .. versionadded:: 3.2
+
+ return a string or list of strings to be displayed after collection has finished successfully.
+
+ This strings will be displayed after the standard "collected X items" message.
+
+ :param config: the pytest config object.
+ :param startdir: py.path object with the starting dir
+ :param items: list of pytest items that are going to be executed; this list should not be modified.
+ """
+
+
+@hookspec(firstresult=True)
+def pytest_report_teststatus(report):
+ """ return result-category, shortletter and verbose word for reporting.
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+
+def pytest_terminal_summary(terminalreporter, exitstatus):
+ """ add additional section in terminal summary reporting. """
+
+
+@hookspec(historic=True)
+def pytest_logwarning(message, code, nodeid, fslocation):
+ """ process a warning specified by a message, a code string,
+ a nodeid and fslocation (both of which may be None
+ if the warning is not tied to a partilar node/location)."""
+
+# -------------------------------------------------------------------------
+# doctest hooks
+# -------------------------------------------------------------------------
+
+
+@hookspec(firstresult=True)
+def pytest_doctest_prepare_content(content):
+ """ return processed content for a given doctest
+
+ Stops at first non-None result, see :ref:`firstresult` """
+
+# -------------------------------------------------------------------------
+# error handling and internal debugging hooks
+# -------------------------------------------------------------------------
+
+
+def pytest_internalerror(excrepr, excinfo):
+ """ called for internal errors. """
+
+
+def pytest_keyboard_interrupt(excinfo):
+ """ called for keyboard interrupt. """
+
+
+def pytest_exception_interact(node, call, report):
+ """called when an exception was raised which can potentially be
+ interactively handled.
+
+ This hook is only called if an exception was raised
+ that is not an internal exception like ``skip.Exception``.
+ """
+
+
+def pytest_enter_pdb(config):
+ """ called upon pdb.set_trace(), can be used by plugins to take special
+ action just before the python debugger enters in interactive mode.
+
+ :arg config: pytest config object
+ :type config: _pytest.config.Config
+ """
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/junitxml.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/junitxml.py
new file mode 100644
index 00000000000..7fb40dc3548
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/junitxml.py
@@ -0,0 +1,453 @@
+"""
+ report test results in JUnit-XML format,
+ for use with Jenkins and build integration servers.
+
+
+Based on initial code from Ross Lawley.
+
+Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/
+src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
+"""
+from __future__ import absolute_import, division, print_function
+
+import functools
+import py
+import os
+import re
+import sys
+import time
+import pytest
+from _pytest import nodes
+from _pytest.config import filename_arg
+
+# Python 2.X and 3.X compatibility
+if sys.version_info[0] < 3:
+ from codecs import open
+else:
+ unichr = chr
+ unicode = str
+ long = int
+
+
+class Junit(py.xml.Namespace):
+ pass
+
+
+# We need to get the subset of the invalid unicode ranges according to
+# XML 1.0 which are valid in this python build. Hence we calculate
+# this dynamically instead of hardcoding it. The spec range of valid
+# chars is: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
+# | [#x10000-#x10FFFF]
+_legal_chars = (0x09, 0x0A, 0x0d)
+_legal_ranges = (
+ (0x20, 0x7E), (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF),
+)
+_legal_xml_re = [
+ unicode("%s-%s") % (unichr(low), unichr(high))
+ for (low, high) in _legal_ranges if low < sys.maxunicode
+]
+_legal_xml_re = [unichr(x) for x in _legal_chars] + _legal_xml_re
+illegal_xml_re = re.compile(unicode('[^%s]') % unicode('').join(_legal_xml_re))
+del _legal_chars
+del _legal_ranges
+del _legal_xml_re
+
+_py_ext_re = re.compile(r"\.py$")
+
+
+def bin_xml_escape(arg):
+ def repl(matchobj):
+ i = ord(matchobj.group())
+ if i <= 0xFF:
+ return unicode('#x%02X') % i
+ else:
+ return unicode('#x%04X') % i
+
+ return py.xml.raw(illegal_xml_re.sub(repl, py.xml.escape(arg)))
+
+
+class _NodeReporter(object):
+ def __init__(self, nodeid, xml):
+
+ self.id = nodeid
+ self.xml = xml
+ self.add_stats = self.xml.add_stats
+ self.duration = 0
+ self.properties = []
+ self.nodes = []
+ self.testcase = None
+ self.attrs = {}
+
+ def append(self, node):
+ self.xml.add_stats(type(node).__name__)
+ self.nodes.append(node)
+
+ def add_property(self, name, value):
+ self.properties.append((str(name), bin_xml_escape(value)))
+
+ def make_properties_node(self):
+ """Return a Junit node containing custom properties, if any.
+ """
+ if self.properties:
+ return Junit.properties([
+ Junit.property(name=name, value=value)
+ for name, value in self.properties
+ ])
+ return ''
+
+ def record_testreport(self, testreport):
+ assert not self.testcase
+ names = mangle_test_address(testreport.nodeid)
+ classnames = names[:-1]
+ if self.xml.prefix:
+ classnames.insert(0, self.xml.prefix)
+ attrs = {
+ "classname": ".".join(classnames),
+ "name": bin_xml_escape(names[-1]),
+ "file": testreport.location[0],
+ }
+ if testreport.location[1] is not None:
+ attrs["line"] = testreport.location[1]
+ if hasattr(testreport, "url"):
+ attrs["url"] = testreport.url
+ self.attrs = attrs
+
+ def to_xml(self):
+ testcase = Junit.testcase(time=self.duration, **self.attrs)
+ testcase.append(self.make_properties_node())
+ for node in self.nodes:
+ testcase.append(node)
+ return testcase
+
+ def _add_simple(self, kind, message, data=None):
+ data = bin_xml_escape(data)
+ node = kind(data, message=message)
+ self.append(node)
+
+ def write_captured_output(self, report):
+ for capname in ('out', 'err'):
+ content = getattr(report, 'capstd' + capname)
+ if content:
+ tag = getattr(Junit, 'system-' + capname)
+ self.append(tag(bin_xml_escape(content)))
+
+ def append_pass(self, report):
+ self.add_stats('passed')
+
+ def append_failure(self, report):
+ # msg = str(report.longrepr.reprtraceback.extraline)
+ if hasattr(report, "wasxfail"):
+ self._add_simple(
+ Junit.skipped,
+ "xfail-marked test passes unexpectedly")
+ else:
+ if hasattr(report.longrepr, "reprcrash"):
+ message = report.longrepr.reprcrash.message
+ elif isinstance(report.longrepr, (unicode, str)):
+ message = report.longrepr
+ else:
+ message = str(report.longrepr)
+ message = bin_xml_escape(message)
+ fail = Junit.failure(message=message)
+ fail.append(bin_xml_escape(report.longrepr))
+ self.append(fail)
+
+ def append_collect_error(self, report):
+ # msg = str(report.longrepr.reprtraceback.extraline)
+ self.append(Junit.error(bin_xml_escape(report.longrepr),
+ message="collection failure"))
+
+ def append_collect_skipped(self, report):
+ self._add_simple(
+ Junit.skipped, "collection skipped", report.longrepr)
+
+ def append_error(self, report):
+ if getattr(report, 'when', None) == 'teardown':
+ msg = "test teardown failure"
+ else:
+ msg = "test setup failure"
+ self._add_simple(
+ Junit.error, msg, report.longrepr)
+
+ def append_skipped(self, report):
+ if hasattr(report, "wasxfail"):
+ self._add_simple(
+ Junit.skipped, "expected test failure", report.wasxfail
+ )
+ else:
+ filename, lineno, skipreason = report.longrepr
+ if skipreason.startswith("Skipped: "):
+ skipreason = bin_xml_escape(skipreason[9:])
+ self.append(
+ Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason),
+ type="pytest.skip",
+ message=skipreason))
+ self.write_captured_output(report)
+
+ def finalize(self):
+ data = self.to_xml().unicode(indent=0)
+ self.__dict__.clear()
+ self.to_xml = lambda: py.xml.raw(data)
+
+
+@pytest.fixture
+def record_xml_property(request):
+ """Add extra xml properties to the tag for the calling test.
+ The fixture is callable with ``(name, value)``, with value being automatically
+ xml-encoded.
+ """
+ request.node.warn(
+ code='C3',
+ message='record_xml_property is an experimental feature',
+ )
+ xml = getattr(request.config, "_xml", None)
+ if xml is not None:
+ node_reporter = xml.node_reporter(request.node.nodeid)
+ return node_reporter.add_property
+ else:
+ def add_property_noop(name, value):
+ pass
+
+ return add_property_noop
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("terminal reporting")
+ group.addoption(
+ '--junitxml', '--junit-xml',
+ action="store",
+ dest="xmlpath",
+ metavar="path",
+ type=functools.partial(filename_arg, optname="--junitxml"),
+ default=None,
+ help="create junit-xml style report file at given path.")
+ group.addoption(
+ '--junitprefix', '--junit-prefix',
+ action="store",
+ metavar="str",
+ default=None,
+ help="prepend prefix to classnames in junit-xml output")
+ parser.addini("junit_suite_name", "Test suite name for JUnit report", default="pytest")
+
+
+def pytest_configure(config):
+ xmlpath = config.option.xmlpath
+ # prevent opening xmllog on slave nodes (xdist)
+ if xmlpath and not hasattr(config, 'slaveinput'):
+ config._xml = LogXML(xmlpath, config.option.junitprefix, config.getini("junit_suite_name"))
+ config.pluginmanager.register(config._xml)
+
+
+def pytest_unconfigure(config):
+ xml = getattr(config, '_xml', None)
+ if xml:
+ del config._xml
+ config.pluginmanager.unregister(xml)
+
+
+def mangle_test_address(address):
+ path, possible_open_bracket, params = address.partition('[')
+ names = path.split("::")
+ try:
+ names.remove('()')
+ except ValueError:
+ pass
+ # convert file path to dotted path
+ names[0] = names[0].replace(nodes.SEP, '.')
+ names[0] = _py_ext_re.sub("", names[0])
+ # put any params back
+ names[-1] += possible_open_bracket + params
+ return names
+
+
+class LogXML(object):
+ def __init__(self, logfile, prefix, suite_name="pytest"):
+ logfile = os.path.expanduser(os.path.expandvars(logfile))
+ self.logfile = os.path.normpath(os.path.abspath(logfile))
+ self.prefix = prefix
+ self.suite_name = suite_name
+ self.stats = dict.fromkeys([
+ 'error',
+ 'passed',
+ 'failure',
+ 'skipped',
+ ], 0)
+ self.node_reporters = {} # nodeid -> _NodeReporter
+ self.node_reporters_ordered = []
+ self.global_properties = []
+ # List of reports that failed on call but teardown is pending.
+ self.open_reports = []
+ self.cnt_double_fail_tests = 0
+
+ def finalize(self, report):
+ nodeid = getattr(report, 'nodeid', report)
+ # local hack to handle xdist report order
+ slavenode = getattr(report, 'node', None)
+ reporter = self.node_reporters.pop((nodeid, slavenode))
+ if reporter is not None:
+ reporter.finalize()
+
+ def node_reporter(self, report):
+ nodeid = getattr(report, 'nodeid', report)
+ # local hack to handle xdist report order
+ slavenode = getattr(report, 'node', None)
+
+ key = nodeid, slavenode
+
+ if key in self.node_reporters:
+ # TODO: breasks for --dist=each
+ return self.node_reporters[key]
+
+ reporter = _NodeReporter(nodeid, self)
+
+ self.node_reporters[key] = reporter
+ self.node_reporters_ordered.append(reporter)
+
+ return reporter
+
+ def add_stats(self, key):
+ if key in self.stats:
+ self.stats[key] += 1
+
+ def _opentestcase(self, report):
+ reporter = self.node_reporter(report)
+ reporter.record_testreport(report)
+ return reporter
+
+ def pytest_runtest_logreport(self, report):
+ """handle a setup/call/teardown report, generating the appropriate
+ xml tags as necessary.
+
+ note: due to plugins like xdist, this hook may be called in interlaced
+ order with reports from other nodes. for example:
+
+ usual call order:
+ -> setup node1
+ -> call node1
+ -> teardown node1
+ -> setup node2
+ -> call node2
+ -> teardown node2
+
+ possible call order in xdist:
+ -> setup node1
+ -> call node1
+ -> setup node2
+ -> call node2
+ -> teardown node2
+ -> teardown node1
+ """
+ close_report = None
+ if report.passed:
+ if report.when == "call": # ignore setup/teardown
+ reporter = self._opentestcase(report)
+ reporter.append_pass(report)
+ elif report.failed:
+ if report.when == "teardown":
+ # The following vars are needed when xdist plugin is used
+ report_wid = getattr(report, "worker_id", None)
+ report_ii = getattr(report, "item_index", None)
+ close_report = next(
+ (rep for rep in self.open_reports
+ if (rep.nodeid == report.nodeid and
+ getattr(rep, "item_index", None) == report_ii and
+ getattr(rep, "worker_id", None) == report_wid
+ )
+ ), None)
+ if close_report:
+ # We need to open new testcase in case we have failure in
+ # call and error in teardown in order to follow junit
+ # schema
+ self.finalize(close_report)
+ self.cnt_double_fail_tests += 1
+ reporter = self._opentestcase(report)
+ if report.when == "call":
+ reporter.append_failure(report)
+ self.open_reports.append(report)
+ else:
+ reporter.append_error(report)
+ elif report.skipped:
+ reporter = self._opentestcase(report)
+ reporter.append_skipped(report)
+ self.update_testcase_duration(report)
+ if report.when == "teardown":
+ reporter = self._opentestcase(report)
+ reporter.write_captured_output(report)
+ self.finalize(report)
+ report_wid = getattr(report, "worker_id", None)
+ report_ii = getattr(report, "item_index", None)
+ close_report = next(
+ (rep for rep in self.open_reports
+ if (rep.nodeid == report.nodeid and
+ getattr(rep, "item_index", None) == report_ii and
+ getattr(rep, "worker_id", None) == report_wid
+ )
+ ), None)
+ if close_report:
+ self.open_reports.remove(close_report)
+
+ def update_testcase_duration(self, report):
+ """accumulates total duration for nodeid from given report and updates
+ the Junit.testcase with the new total if already created.
+ """
+ reporter = self.node_reporter(report)
+ reporter.duration += getattr(report, 'duration', 0.0)
+
+ def pytest_collectreport(self, report):
+ if not report.passed:
+ reporter = self._opentestcase(report)
+ if report.failed:
+ reporter.append_collect_error(report)
+ else:
+ reporter.append_collect_skipped(report)
+
+ def pytest_internalerror(self, excrepr):
+ reporter = self.node_reporter('internal')
+ reporter.attrs.update(classname="pytest", name='internal')
+ reporter._add_simple(Junit.error, 'internal error', excrepr)
+
+ def pytest_sessionstart(self):
+ self.suite_start_time = time.time()
+
+ def pytest_sessionfinish(self):
+ dirname = os.path.dirname(os.path.abspath(self.logfile))
+ if not os.path.isdir(dirname):
+ os.makedirs(dirname)
+ logfile = open(self.logfile, 'w', encoding='utf-8')
+ suite_stop_time = time.time()
+ suite_time_delta = suite_stop_time - self.suite_start_time
+
+ numtests = (self.stats['passed'] + self.stats['failure'] +
+ self.stats['skipped'] + self.stats['error'] -
+ self.cnt_double_fail_tests)
+ logfile.write('<?xml version="1.0" encoding="utf-8"?>')
+
+ logfile.write(Junit.testsuite(
+ self._get_global_properties_node(),
+ [x.to_xml() for x in self.node_reporters_ordered],
+ name=self.suite_name,
+ errors=self.stats['error'],
+ failures=self.stats['failure'],
+ skips=self.stats['skipped'],
+ tests=numtests,
+ time="%.3f" % suite_time_delta, ).unicode(indent=0))
+ logfile.close()
+
+ def pytest_terminal_summary(self, terminalreporter):
+ terminalreporter.write_sep("-",
+ "generated xml file: %s" % (self.logfile))
+
+ def add_global_property(self, name, value):
+ self.global_properties.append((str(name), bin_xml_escape(value)))
+
+ def _get_global_properties_node(self):
+ """Return a Junit node containing custom properties, if any.
+ """
+ if self.global_properties:
+ return Junit.properties(
+ [
+ Junit.property(name=name, value=value)
+ for name, value in self.global_properties
+ ]
+ )
+ return ''
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/logging.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/logging.py
new file mode 100644
index 00000000000..ed4db25ad44
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/logging.py
@@ -0,0 +1,337 @@
+from __future__ import absolute_import, division, print_function
+
+import logging
+from contextlib import closing, contextmanager
+import sys
+import six
+
+import pytest
+import py
+
+
+DEFAULT_LOG_FORMAT = '%(filename)-25s %(lineno)4d %(levelname)-8s %(message)s'
+DEFAULT_LOG_DATE_FORMAT = '%H:%M:%S'
+
+
+def get_option_ini(config, *names):
+ for name in names:
+ ret = config.getoption(name) # 'default' arg won't work as expected
+ if ret is None:
+ ret = config.getini(name)
+ if ret:
+ return ret
+
+
+def pytest_addoption(parser):
+ """Add options to control log capturing."""
+ group = parser.getgroup('logging')
+
+ def add_option_ini(option, dest, default=None, type=None, **kwargs):
+ parser.addini(dest, default=default, type=type,
+ help='default value for ' + option)
+ group.addoption(option, dest=dest, **kwargs)
+
+ add_option_ini(
+ '--no-print-logs',
+ dest='log_print', action='store_const', const=False, default=True,
+ type='bool',
+ help='disable printing caught logs on failed tests.')
+ add_option_ini(
+ '--log-level',
+ dest='log_level', default=None,
+ help='logging level used by the logging module')
+ add_option_ini(
+ '--log-format',
+ dest='log_format', default=DEFAULT_LOG_FORMAT,
+ help='log format as used by the logging module.')
+ add_option_ini(
+ '--log-date-format',
+ dest='log_date_format', default=DEFAULT_LOG_DATE_FORMAT,
+ help='log date format as used by the logging module.')
+ add_option_ini(
+ '--log-cli-level',
+ dest='log_cli_level', default=None,
+ help='cli logging level.')
+ add_option_ini(
+ '--log-cli-format',
+ dest='log_cli_format', default=None,
+ help='log format as used by the logging module.')
+ add_option_ini(
+ '--log-cli-date-format',
+ dest='log_cli_date_format', default=None,
+ help='log date format as used by the logging module.')
+ add_option_ini(
+ '--log-file',
+ dest='log_file', default=None,
+ help='path to a file when logging will be written to.')
+ add_option_ini(
+ '--log-file-level',
+ dest='log_file_level', default=None,
+ help='log file logging level.')
+ add_option_ini(
+ '--log-file-format',
+ dest='log_file_format', default=DEFAULT_LOG_FORMAT,
+ help='log format as used by the logging module.')
+ add_option_ini(
+ '--log-file-date-format',
+ dest='log_file_date_format', default=DEFAULT_LOG_DATE_FORMAT,
+ help='log date format as used by the logging module.')
+
+
+@contextmanager
+def logging_using_handler(handler, logger=None):
+ """Context manager that safely registers a given handler."""
+ logger = logger or logging.getLogger(logger)
+
+ if handler in logger.handlers: # reentrancy
+ # Adding the same handler twice would confuse logging system.
+ # Just don't do that.
+ yield
+ else:
+ logger.addHandler(handler)
+ try:
+ yield
+ finally:
+ logger.removeHandler(handler)
+
+
+@contextmanager
+def catching_logs(handler, formatter=None,
+ level=logging.NOTSET, logger=None):
+ """Context manager that prepares the whole logging machinery properly."""
+ logger = logger or logging.getLogger(logger)
+
+ if formatter is not None:
+ handler.setFormatter(formatter)
+ handler.setLevel(level)
+
+ with logging_using_handler(handler, logger):
+ orig_level = logger.level
+ logger.setLevel(min(orig_level, level))
+ try:
+ yield handler
+ finally:
+ logger.setLevel(orig_level)
+
+
+class LogCaptureHandler(logging.StreamHandler):
+ """A logging handler that stores log records and the log text."""
+
+ def __init__(self):
+ """Creates a new log handler."""
+ logging.StreamHandler.__init__(self, py.io.TextIO())
+ self.records = []
+
+ def emit(self, record):
+ """Keep the log records in a list in addition to the log text."""
+ self.records.append(record)
+ logging.StreamHandler.emit(self, record)
+
+
+class LogCaptureFixture(object):
+ """Provides access and control of log capturing."""
+
+ def __init__(self, item):
+ """Creates a new funcarg."""
+ self._item = item
+
+ @property
+ def handler(self):
+ return self._item.catch_log_handler
+
+ @property
+ def text(self):
+ """Returns the log text."""
+ return self.handler.stream.getvalue()
+
+ @property
+ def records(self):
+ """Returns the list of log records."""
+ return self.handler.records
+
+ @property
+ def record_tuples(self):
+ """Returns a list of a striped down version of log records intended
+ for use in assertion comparison.
+
+ The format of the tuple is:
+
+ (logger_name, log_level, message)
+ """
+ return [(r.name, r.levelno, r.getMessage()) for r in self.records]
+
+ def clear(self):
+ """Reset the list of log records."""
+ self.handler.records = []
+
+ def set_level(self, level, logger=None):
+ """Sets the level for capturing of logs.
+
+ By default, the level is set on the handler used to capture
+ logs. Specify a logger name to instead set the level of any
+ logger.
+ """
+ if logger is None:
+ logger = self.handler
+ else:
+ logger = logging.getLogger(logger)
+ logger.setLevel(level)
+
+ @contextmanager
+ def at_level(self, level, logger=None):
+ """Context manager that sets the level for capturing of logs.
+
+ By default, the level is set on the handler used to capture
+ logs. Specify a logger name to instead set the level of any
+ logger.
+ """
+ if logger is None:
+ logger = self.handler
+ else:
+ logger = logging.getLogger(logger)
+
+ orig_level = logger.level
+ logger.setLevel(level)
+ try:
+ yield
+ finally:
+ logger.setLevel(orig_level)
+
+
+@pytest.fixture
+def caplog(request):
+ """Access and control log capturing.
+
+ Captured logs are available through the following methods::
+
+ * caplog.text() -> string containing formatted log output
+ * caplog.records() -> list of logging.LogRecord instances
+ * caplog.record_tuples() -> list of (logger_name, level, message) tuples
+ """
+ return LogCaptureFixture(request.node)
+
+
+def get_actual_log_level(config, *setting_names):
+ """Return the actual logging level."""
+
+ for setting_name in setting_names:
+ log_level = config.getoption(setting_name)
+ if log_level is None:
+ log_level = config.getini(setting_name)
+ if log_level:
+ break
+ else:
+ return
+
+ if isinstance(log_level, six.string_types):
+ log_level = log_level.upper()
+ try:
+ return int(getattr(logging, log_level, log_level))
+ except ValueError:
+ # Python logging does not recognise this as a logging level
+ raise pytest.UsageError(
+ "'{0}' is not recognized as a logging level name for "
+ "'{1}'. Please consider passing the "
+ "logging level num instead.".format(
+ log_level,
+ setting_name))
+
+
+def pytest_configure(config):
+ config.pluginmanager.register(LoggingPlugin(config),
+ 'logging-plugin')
+
+
+class LoggingPlugin(object):
+ """Attaches to the logging module and captures log messages for each test.
+ """
+
+ def __init__(self, config):
+ """Creates a new plugin to capture log messages.
+
+ The formatter can be safely shared across all handlers so
+ create a single one for the entire test session here.
+ """
+ self.log_cli_level = get_actual_log_level(
+ config, 'log_cli_level', 'log_level') or logging.WARNING
+
+ self.print_logs = get_option_ini(config, 'log_print')
+ self.formatter = logging.Formatter(
+ get_option_ini(config, 'log_format'),
+ get_option_ini(config, 'log_date_format'))
+
+ log_cli_handler = logging.StreamHandler(sys.stderr)
+ log_cli_format = get_option_ini(
+ config, 'log_cli_format', 'log_format')
+ log_cli_date_format = get_option_ini(
+ config, 'log_cli_date_format', 'log_date_format')
+ log_cli_formatter = logging.Formatter(
+ log_cli_format,
+ datefmt=log_cli_date_format)
+ self.log_cli_handler = log_cli_handler # needed for a single unittest
+ self.live_logs = catching_logs(log_cli_handler,
+ formatter=log_cli_formatter,
+ level=self.log_cli_level)
+
+ log_file = get_option_ini(config, 'log_file')
+ if log_file:
+ self.log_file_level = get_actual_log_level(
+ config, 'log_file_level') or logging.WARNING
+
+ log_file_format = get_option_ini(
+ config, 'log_file_format', 'log_format')
+ log_file_date_format = get_option_ini(
+ config, 'log_file_date_format', 'log_date_format')
+ self.log_file_handler = logging.FileHandler(
+ log_file,
+ # Each pytest runtests session will write to a clean logfile
+ mode='w')
+ log_file_formatter = logging.Formatter(
+ log_file_format,
+ datefmt=log_file_date_format)
+ self.log_file_handler.setFormatter(log_file_formatter)
+ else:
+ self.log_file_handler = None
+
+ @contextmanager
+ def _runtest_for(self, item, when):
+ """Implements the internals of pytest_runtest_xxx() hook."""
+ with catching_logs(LogCaptureHandler(),
+ formatter=self.formatter) as log_handler:
+ item.catch_log_handler = log_handler
+ try:
+ yield # run test
+ finally:
+ del item.catch_log_handler
+
+ if self.print_logs:
+ # Add a captured log section to the report.
+ log = log_handler.stream.getvalue().strip()
+ item.add_report_section(when, 'log', log)
+
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_runtest_setup(self, item):
+ with self._runtest_for(item, 'setup'):
+ yield
+
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_runtest_call(self, item):
+ with self._runtest_for(item, 'call'):
+ yield
+
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_runtest_teardown(self, item):
+ with self._runtest_for(item, 'teardown'):
+ yield
+
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_runtestloop(self, session):
+ """Runs all collected test items."""
+ with self.live_logs:
+ if self.log_file_handler is not None:
+ with closing(self.log_file_handler):
+ with catching_logs(self.log_file_handler,
+ level=self.log_file_level):
+ yield # run all the tests
+ else:
+ yield # run all the tests
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/main.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/main.py
new file mode 100644
index 00000000000..25554098dac
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/main.py
@@ -0,0 +1,821 @@
+""" core implementation of testing process: init, session, runtest loop. """
+from __future__ import absolute_import, division, print_function
+
+import functools
+import os
+import six
+import sys
+
+import _pytest
+from _pytest import nodes
+import _pytest._code
+import py
+try:
+ from collections import MutableMapping as MappingMixin
+except ImportError:
+ from UserDict import DictMixin as MappingMixin
+
+from _pytest.config import directory_arg, UsageError, hookimpl
+from _pytest.outcomes import exit
+from _pytest.runner import collect_one_node
+
+tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
+
+# exitcodes for the command line
+EXIT_OK = 0
+EXIT_TESTSFAILED = 1
+EXIT_INTERRUPTED = 2
+EXIT_INTERNALERROR = 3
+EXIT_USAGEERROR = 4
+EXIT_NOTESTSCOLLECTED = 5
+
+
+def pytest_addoption(parser):
+ parser.addini("norecursedirs", "directory patterns to avoid for recursion",
+ type="args", default=['.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg', 'venv'])
+ parser.addini("testpaths", "directories to search for tests when no files or directories are given in the "
+ "command line.",
+ type="args", default=[])
+ # parser.addini("dirpatterns",
+ # "patterns specifying possible locations of test files",
+ # type="linelist", default=["**/test_*.txt",
+ # "**/test_*.py", "**/*_test.py"]
+ # )
+ group = parser.getgroup("general", "running and selection options")
+ group._addoption('-x', '--exitfirst', action="store_const",
+ dest="maxfail", const=1,
+ help="exit instantly on first error or failed test."),
+ group._addoption('--maxfail', metavar="num",
+ action="store", type=int, dest="maxfail", default=0,
+ help="exit after first num failures or errors.")
+ group._addoption('--strict', action="store_true",
+ help="marks not registered in configuration file raise errors.")
+ group._addoption("-c", metavar="file", type=str, dest="inifilename",
+ help="load configuration from `file` instead of trying to locate one of the implicit "
+ "configuration files.")
+ group._addoption("--continue-on-collection-errors", action="store_true",
+ default=False, dest="continue_on_collection_errors",
+ help="Force test execution even if collection errors occur.")
+
+ group = parser.getgroup("collect", "collection")
+ group.addoption('--collectonly', '--collect-only', action="store_true",
+ help="only collect tests, don't execute them."),
+ group.addoption('--pyargs', action="store_true",
+ help="try to interpret all arguments as python packages.")
+ group.addoption("--ignore", action="append", metavar="path",
+ help="ignore path during collection (multi-allowed).")
+ # when changing this to --conf-cut-dir, config.py Conftest.setinitial
+ # needs upgrading as well
+ group.addoption('--confcutdir', dest="confcutdir", default=None,
+ metavar="dir", type=functools.partial(directory_arg, optname="--confcutdir"),
+ help="only load conftest.py's relative to specified dir.")
+ group.addoption('--noconftest', action="store_true",
+ dest="noconftest", default=False,
+ help="Don't load any conftest.py files.")
+ group.addoption('--keepduplicates', '--keep-duplicates', action="store_true",
+ dest="keepduplicates", default=False,
+ help="Keep duplicate tests.")
+ group.addoption('--collect-in-virtualenv', action='store_true',
+ dest='collect_in_virtualenv', default=False,
+ help="Don't ignore tests in a local virtualenv directory")
+
+ group = parser.getgroup("debugconfig",
+ "test session debugging and configuration")
+ group.addoption('--basetemp', dest="basetemp", default=None, metavar="dir",
+ help="base temporary directory for this test run.")
+
+
+def pytest_configure(config):
+ __import__('pytest').config = config # compatibiltiy
+
+
+def wrap_session(config, doit):
+ """Skeleton command line program"""
+ session = Session(config)
+ session.exitstatus = EXIT_OK
+ initstate = 0
+ try:
+ try:
+ config._do_configure()
+ initstate = 1
+ config.hook.pytest_sessionstart(session=session)
+ initstate = 2
+ session.exitstatus = doit(config, session) or 0
+ except UsageError:
+ raise
+ except Failed:
+ session.exitstatus = EXIT_TESTSFAILED
+ except KeyboardInterrupt:
+ excinfo = _pytest._code.ExceptionInfo()
+ if initstate < 2 and isinstance(excinfo.value, exit.Exception):
+ sys.stderr.write('{0}: {1}\n'.format(
+ excinfo.typename, excinfo.value.msg))
+ config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
+ session.exitstatus = EXIT_INTERRUPTED
+ except: # noqa
+ excinfo = _pytest._code.ExceptionInfo()
+ config.notify_exception(excinfo, config.option)
+ session.exitstatus = EXIT_INTERNALERROR
+ if excinfo.errisinstance(SystemExit):
+ sys.stderr.write("mainloop: caught Spurious SystemExit!\n")
+
+ finally:
+ excinfo = None # Explicitly break reference cycle.
+ session.startdir.chdir()
+ if initstate >= 2:
+ config.hook.pytest_sessionfinish(
+ session=session,
+ exitstatus=session.exitstatus)
+ config._ensure_unconfigure()
+ return session.exitstatus
+
+
+def pytest_cmdline_main(config):
+ return wrap_session(config, _main)
+
+
+def _main(config, session):
+ """ default command line protocol for initialization, session,
+ running tests and reporting. """
+ config.hook.pytest_collection(session=session)
+ config.hook.pytest_runtestloop(session=session)
+
+ if session.testsfailed:
+ return EXIT_TESTSFAILED
+ elif session.testscollected == 0:
+ return EXIT_NOTESTSCOLLECTED
+
+
+def pytest_collection(session):
+ return session.perform_collect()
+
+
+def pytest_runtestloop(session):
+ if (session.testsfailed and
+ not session.config.option.continue_on_collection_errors):
+ raise session.Interrupted(
+ "%d errors during collection" % session.testsfailed)
+
+ if session.config.option.collectonly:
+ return True
+
+ for i, item in enumerate(session.items):
+ nextitem = session.items[i + 1] if i + 1 < len(session.items) else None
+ item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
+ if session.shouldfail:
+ raise session.Failed(session.shouldfail)
+ if session.shouldstop:
+ raise session.Interrupted(session.shouldstop)
+ return True
+
+
+def _in_venv(path):
+ """Attempts to detect if ``path`` is the root of a Virtual Environment by
+ checking for the existence of the appropriate activate script"""
+ bindir = path.join('Scripts' if sys.platform.startswith('win') else 'bin')
+ if not bindir.exists():
+ return False
+ activates = ('activate', 'activate.csh', 'activate.fish',
+ 'Activate', 'Activate.bat', 'Activate.ps1')
+ return any([fname.basename in activates for fname in bindir.listdir()])
+
+
+def pytest_ignore_collect(path, config):
+ ignore_paths = config._getconftest_pathlist("collect_ignore", path=path.dirpath())
+ ignore_paths = ignore_paths or []
+ excludeopt = config.getoption("ignore")
+ if excludeopt:
+ ignore_paths.extend([py.path.local(x) for x in excludeopt])
+
+ if py.path.local(path) in ignore_paths:
+ return True
+
+ allow_in_venv = config.getoption("collect_in_virtualenv")
+ if _in_venv(path) and not allow_in_venv:
+ return True
+
+ # Skip duplicate paths.
+ keepduplicates = config.getoption("keepduplicates")
+ duplicate_paths = config.pluginmanager._duplicatepaths
+ if not keepduplicates:
+ if path in duplicate_paths:
+ return True
+ else:
+ duplicate_paths.add(path)
+
+ return False
+
+
+class FSHookProxy:
+ def __init__(self, fspath, pm, remove_mods):
+ self.fspath = fspath
+ self.pm = pm
+ self.remove_mods = remove_mods
+
+ def __getattr__(self, name):
+ x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods)
+ self.__dict__[name] = x
+ return x
+
+
+class _CompatProperty(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __get__(self, obj, owner):
+ if obj is None:
+ return self
+
+ # TODO: reenable in the features branch
+ # warnings.warn(
+ # "usage of {owner!r}.{name} is deprecated, please use pytest.{name} instead".format(
+ # name=self.name, owner=type(owner).__name__),
+ # PendingDeprecationWarning, stacklevel=2)
+ return getattr(__import__('pytest'), self.name)
+
+
+class NodeKeywords(MappingMixin):
+ def __init__(self, node):
+ self.node = node
+ self.parent = node.parent
+ self._markers = {node.name: True}
+
+ def __getitem__(self, key):
+ try:
+ return self._markers[key]
+ except KeyError:
+ if self.parent is None:
+ raise
+ return self.parent.keywords[key]
+
+ def __setitem__(self, key, value):
+ self._markers[key] = value
+
+ def __delitem__(self, key):
+ raise ValueError("cannot delete key in keywords dict")
+
+ def __iter__(self):
+ seen = set(self._markers)
+ if self.parent is not None:
+ seen.update(self.parent.keywords)
+ return iter(seen)
+
+ def __len__(self):
+ return len(self.__iter__())
+
+ def keys(self):
+ return list(self)
+
+ def __repr__(self):
+ return "<NodeKeywords for node %s>" % (self.node, )
+
+
+class Node(object):
+ """ base class for Collector and Item the test collection tree.
+ Collector subclasses have children, Items are terminal nodes."""
+
+ def __init__(self, name, parent=None, config=None, session=None):
+ #: a unique name within the scope of the parent node
+ self.name = name
+
+ #: the parent collector node.
+ self.parent = parent
+
+ #: the pytest config object
+ self.config = config or parent.config
+
+ #: the session this node is part of
+ self.session = session or parent.session
+
+ #: filesystem path where this node was collected from (can be None)
+ self.fspath = getattr(parent, 'fspath', None)
+
+ #: keywords/markers collected from all scopes
+ self.keywords = NodeKeywords(self)
+
+ #: allow adding of extra keywords to use for matching
+ self.extra_keyword_matches = set()
+
+ # used for storing artificial fixturedefs for direct parametrization
+ self._name2pseudofixturedef = {}
+
+ @property
+ def ihook(self):
+ """ fspath sensitive hook proxy used to call pytest hooks"""
+ return self.session.gethookproxy(self.fspath)
+
+ Module = _CompatProperty("Module")
+ Class = _CompatProperty("Class")
+ Instance = _CompatProperty("Instance")
+ Function = _CompatProperty("Function")
+ File = _CompatProperty("File")
+ Item = _CompatProperty("Item")
+
+ def _getcustomclass(self, name):
+ maybe_compatprop = getattr(type(self), name)
+ if isinstance(maybe_compatprop, _CompatProperty):
+ return getattr(__import__('pytest'), name)
+ else:
+ cls = getattr(self, name)
+ # TODO: reenable in the features branch
+ # warnings.warn("use of node.%s is deprecated, "
+ # "use pytest_pycollect_makeitem(...) to create custom "
+ # "collection nodes" % name, category=DeprecationWarning)
+ return cls
+
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__,
+ getattr(self, 'name', None))
+
+ def warn(self, code, message):
+ """ generate a warning with the given code and message for this
+ item. """
+ assert isinstance(code, str)
+ fslocation = getattr(self, "location", None)
+ if fslocation is None:
+ fslocation = getattr(self, "fspath", None)
+ self.ihook.pytest_logwarning.call_historic(kwargs=dict(
+ code=code, message=message,
+ nodeid=self.nodeid, fslocation=fslocation))
+
+ # methods for ordering nodes
+ @property
+ def nodeid(self):
+ """ a ::-separated string denoting its collection tree address. """
+ try:
+ return self._nodeid
+ except AttributeError:
+ self._nodeid = x = self._makeid()
+ return x
+
+ def _makeid(self):
+ return self.parent.nodeid + "::" + self.name
+
+ def __hash__(self):
+ return hash(self.nodeid)
+
+ def setup(self):
+ pass
+
+ def teardown(self):
+ pass
+
+ def listchain(self):
+ """ return list of all parent collectors up to self,
+ starting from root of collection tree. """
+ chain = []
+ item = self
+ while item is not None:
+ chain.append(item)
+ item = item.parent
+ chain.reverse()
+ return chain
+
+ def add_marker(self, marker):
+ """ dynamically add a marker object to the node.
+
+ ``marker`` can be a string or pytest.mark.* instance.
+ """
+ from _pytest.mark import MarkDecorator, MARK_GEN
+ if isinstance(marker, six.string_types):
+ marker = getattr(MARK_GEN, marker)
+ elif not isinstance(marker, MarkDecorator):
+ raise ValueError("is not a string or pytest.mark.* Marker")
+ self.keywords[marker.name] = marker
+
+ def get_marker(self, name):
+ """ get a marker object from this node or None if
+ the node doesn't have a marker with that name. """
+ val = self.keywords.get(name, None)
+ if val is not None:
+ from _pytest.mark import MarkInfo, MarkDecorator
+ if isinstance(val, (MarkDecorator, MarkInfo)):
+ return val
+
+ def listextrakeywords(self):
+ """ Return a set of all extra keywords in self and any parents."""
+ extra_keywords = set()
+ item = self
+ for item in self.listchain():
+ extra_keywords.update(item.extra_keyword_matches)
+ return extra_keywords
+
+ def listnames(self):
+ return [x.name for x in self.listchain()]
+
+ def addfinalizer(self, fin):
+ """ register a function to be called when this node is finalized.
+
+ This method can only be called when this node is active
+ in a setup chain, for example during self.setup().
+ """
+ self.session._setupstate.addfinalizer(fin, self)
+
+ def getparent(self, cls):
+ """ get the next parent node (including ourself)
+ which is an instance of the given class"""
+ current = self
+ while current and not isinstance(current, cls):
+ current = current.parent
+ return current
+
+ def _prunetraceback(self, excinfo):
+ pass
+
+ def _repr_failure_py(self, excinfo, style=None):
+ fm = self.session._fixturemanager
+ if excinfo.errisinstance(fm.FixtureLookupError):
+ return excinfo.value.formatrepr()
+ tbfilter = True
+ if self.config.option.fulltrace:
+ style = "long"
+ else:
+ tb = _pytest._code.Traceback([excinfo.traceback[-1]])
+ self._prunetraceback(excinfo)
+ if len(excinfo.traceback) == 0:
+ excinfo.traceback = tb
+ tbfilter = False # prunetraceback already does it
+ if style == "auto":
+ style = "long"
+ # XXX should excinfo.getrepr record all data and toterminal() process it?
+ if style is None:
+ if self.config.option.tbstyle == "short":
+ style = "short"
+ else:
+ style = "long"
+
+ try:
+ os.getcwd()
+ abspath = False
+ except OSError:
+ abspath = True
+
+ return excinfo.getrepr(funcargs=True, abspath=abspath,
+ showlocals=self.config.option.showlocals,
+ style=style, tbfilter=tbfilter)
+
+ repr_failure = _repr_failure_py
+
+
+class Collector(Node):
+ """ Collector instances create children through collect()
+ and thus iteratively build a tree.
+ """
+
+ class CollectError(Exception):
+ """ an error during collection, contains a custom message. """
+
+ def collect(self):
+ """ returns a list of children (items and collectors)
+ for this collection node.
+ """
+ raise NotImplementedError("abstract")
+
+ def repr_failure(self, excinfo):
+ """ represent a collection failure. """
+ if excinfo.errisinstance(self.CollectError):
+ exc = excinfo.value
+ return str(exc.args[0])
+ return self._repr_failure_py(excinfo, style="short")
+
+ def _prunetraceback(self, excinfo):
+ if hasattr(self, 'fspath'):
+ traceback = excinfo.traceback
+ ntraceback = traceback.cut(path=self.fspath)
+ if ntraceback == traceback:
+ ntraceback = ntraceback.cut(excludepath=tracebackcutdir)
+ excinfo.traceback = ntraceback.filter()
+
+
+class FSCollector(Collector):
+ def __init__(self, fspath, parent=None, config=None, session=None):
+ fspath = py.path.local(fspath) # xxx only for test_resultlog.py?
+ name = fspath.basename
+ if parent is not None:
+ rel = fspath.relto(parent.fspath)
+ if rel:
+ name = rel
+ name = name.replace(os.sep, nodes.SEP)
+ super(FSCollector, self).__init__(name, parent, config, session)
+ self.fspath = fspath
+
+ def _check_initialpaths_for_relpath(self):
+ for initialpath in self.session._initialpaths:
+ if self.fspath.common(initialpath) == initialpath:
+ return self.fspath.relto(initialpath.dirname)
+
+ def _makeid(self):
+ relpath = self.fspath.relto(self.config.rootdir)
+
+ if not relpath:
+ relpath = self._check_initialpaths_for_relpath()
+ if os.sep != nodes.SEP:
+ relpath = relpath.replace(os.sep, nodes.SEP)
+ return relpath
+
+
+class File(FSCollector):
+ """ base class for collecting tests from a file. """
+
+
+class Item(Node):
+ """ a basic test invocation item. Note that for a single function
+ there might be multiple test invocation items.
+ """
+ nextitem = None
+
+ def __init__(self, name, parent=None, config=None, session=None):
+ super(Item, self).__init__(name, parent, config, session)
+ self._report_sections = []
+
+ def add_report_section(self, when, key, content):
+ """
+ Adds a new report section, similar to what's done internally to add stdout and
+ stderr captured output::
+
+ item.add_report_section("call", "stdout", "report section contents")
+
+ :param str when:
+ One of the possible capture states, ``"setup"``, ``"call"``, ``"teardown"``.
+ :param str key:
+ Name of the section, can be customized at will. Pytest uses ``"stdout"`` and
+ ``"stderr"`` internally.
+
+ :param str content:
+ The full contents as a string.
+ """
+ if content:
+ self._report_sections.append((when, key, content))
+
+ def reportinfo(self):
+ return self.fspath, None, ""
+
+ @property
+ def location(self):
+ try:
+ return self._location
+ except AttributeError:
+ location = self.reportinfo()
+ # bestrelpath is a quite slow function
+ cache = self.config.__dict__.setdefault("_bestrelpathcache", {})
+ try:
+ fspath = cache[location[0]]
+ except KeyError:
+ fspath = self.session.fspath.bestrelpath(location[0])
+ cache[location[0]] = fspath
+ location = (fspath, location[1], str(location[2]))
+ self._location = location
+ return location
+
+
+class NoMatch(Exception):
+ """ raised if matching cannot locate a matching names. """
+
+
+class Interrupted(KeyboardInterrupt):
+ """ signals an interrupted test run. """
+ __module__ = 'builtins' # for py3
+
+
+class Failed(Exception):
+ """ signals an stop as failed test run. """
+
+
+class Session(FSCollector):
+ Interrupted = Interrupted
+ Failed = Failed
+
+ def __init__(self, config):
+ FSCollector.__init__(self, config.rootdir, parent=None,
+ config=config, session=self)
+ self.testsfailed = 0
+ self.testscollected = 0
+ self.shouldstop = False
+ self.shouldfail = False
+ self.trace = config.trace.root.get("collection")
+ self._norecursepatterns = config.getini("norecursedirs")
+ self.startdir = py.path.local()
+ self.config.pluginmanager.register(self, name="session")
+
+ def _makeid(self):
+ return ""
+
+ @hookimpl(tryfirst=True)
+ def pytest_collectstart(self):
+ if self.shouldfail:
+ raise self.Failed(self.shouldfail)
+ if self.shouldstop:
+ raise self.Interrupted(self.shouldstop)
+
+ @hookimpl(tryfirst=True)
+ def pytest_runtest_logreport(self, report):
+ if report.failed and not hasattr(report, 'wasxfail'):
+ self.testsfailed += 1
+ maxfail = self.config.getvalue("maxfail")
+ if maxfail and self.testsfailed >= maxfail:
+ self.shouldfail = "stopping after %d failures" % (
+ self.testsfailed)
+ pytest_collectreport = pytest_runtest_logreport
+
+ def isinitpath(self, path):
+ return path in self._initialpaths
+
+ def gethookproxy(self, fspath):
+ # check if we have the common case of running
+ # hooks with all conftest.py filesall conftest.py
+ pm = self.config.pluginmanager
+ my_conftestmodules = pm._getconftestmodules(fspath)
+ remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
+ if remove_mods:
+ # one or more conftests are not in use at this fspath
+ proxy = FSHookProxy(fspath, pm, remove_mods)
+ else:
+ # all plugis are active for this fspath
+ proxy = self.config.hook
+ return proxy
+
+ def perform_collect(self, args=None, genitems=True):
+ hook = self.config.hook
+ try:
+ items = self._perform_collect(args, genitems)
+ self.config.pluginmanager.check_pending()
+ hook.pytest_collection_modifyitems(session=self,
+ config=self.config, items=items)
+ finally:
+ hook.pytest_collection_finish(session=self)
+ self.testscollected = len(items)
+ return items
+
+ def _perform_collect(self, args, genitems):
+ if args is None:
+ args = self.config.args
+ self.trace("perform_collect", self, args)
+ self.trace.root.indent += 1
+ self._notfound = []
+ self._initialpaths = set()
+ self._initialparts = []
+ self.items = items = []
+ for arg in args:
+ parts = self._parsearg(arg)
+ self._initialparts.append(parts)
+ self._initialpaths.add(parts[0])
+ rep = collect_one_node(self)
+ self.ihook.pytest_collectreport(report=rep)
+ self.trace.root.indent -= 1
+ if self._notfound:
+ errors = []
+ for arg, exc in self._notfound:
+ line = "(no name %r in any of %r)" % (arg, exc.args[0])
+ errors.append("not found: %s\n%s" % (arg, line))
+ # XXX: test this
+ raise UsageError(*errors)
+ if not genitems:
+ return rep.result
+ else:
+ if rep.passed:
+ for node in rep.result:
+ self.items.extend(self.genitems(node))
+ return items
+
+ def collect(self):
+ for parts in self._initialparts:
+ arg = "::".join(map(str, parts))
+ self.trace("processing argument", arg)
+ self.trace.root.indent += 1
+ try:
+ for x in self._collect(arg):
+ yield x
+ except NoMatch:
+ # we are inside a make_report hook so
+ # we cannot directly pass through the exception
+ self._notfound.append((arg, sys.exc_info()[1]))
+
+ self.trace.root.indent -= 1
+
+ def _collect(self, arg):
+ names = self._parsearg(arg)
+ path = names.pop(0)
+ if path.check(dir=1):
+ assert not names, "invalid arg %r" % (arg,)
+ for path in path.visit(fil=lambda x: x.check(file=1),
+ rec=self._recurse, bf=True, sort=True):
+ for x in self._collectfile(path):
+ yield x
+ else:
+ assert path.check(file=1)
+ for x in self.matchnodes(self._collectfile(path), names):
+ yield x
+
+ def _collectfile(self, path):
+ ihook = self.gethookproxy(path)
+ if not self.isinitpath(path):
+ if ihook.pytest_ignore_collect(path=path, config=self.config):
+ return ()
+ return ihook.pytest_collect_file(path=path, parent=self)
+
+ def _recurse(self, path):
+ ihook = self.gethookproxy(path.dirpath())
+ if ihook.pytest_ignore_collect(path=path, config=self.config):
+ return
+ for pat in self._norecursepatterns:
+ if path.check(fnmatch=pat):
+ return False
+ ihook = self.gethookproxy(path)
+ ihook.pytest_collect_directory(path=path, parent=self)
+ return True
+
+ def _tryconvertpyarg(self, x):
+ """Convert a dotted module name to path.
+
+ """
+ import pkgutil
+ try:
+ loader = pkgutil.find_loader(x)
+ except ImportError:
+ return x
+ if loader is None:
+ return x
+ # This method is sometimes invoked when AssertionRewritingHook, which
+ # does not define a get_filename method, is already in place:
+ try:
+ path = loader.get_filename(x)
+ except AttributeError:
+ # Retrieve path from AssertionRewritingHook:
+ path = loader.modules[x][0].co_filename
+ if loader.is_package(x):
+ path = os.path.dirname(path)
+ return path
+
+ def _parsearg(self, arg):
+ """ return (fspath, names) tuple after checking the file exists. """
+ parts = str(arg).split("::")
+ if self.config.option.pyargs:
+ parts[0] = self._tryconvertpyarg(parts[0])
+ relpath = parts[0].replace("/", os.sep)
+ path = self.config.invocation_dir.join(relpath, abs=True)
+ if not path.check():
+ if self.config.option.pyargs:
+ raise UsageError(
+ "file or package not found: " + arg +
+ " (missing __init__.py?)")
+ else:
+ raise UsageError("file not found: " + arg)
+ parts[0] = path
+ return parts
+
+ def matchnodes(self, matching, names):
+ self.trace("matchnodes", matching, names)
+ self.trace.root.indent += 1
+ nodes = self._matchnodes(matching, names)
+ num = len(nodes)
+ self.trace("matchnodes finished -> ", num, "nodes")
+ self.trace.root.indent -= 1
+ if num == 0:
+ raise NoMatch(matching, names[:1])
+ return nodes
+
+ def _matchnodes(self, matching, names):
+ if not matching or not names:
+ return matching
+ name = names[0]
+ assert name
+ nextnames = names[1:]
+ resultnodes = []
+ for node in matching:
+ if isinstance(node, Item):
+ if not names:
+ resultnodes.append(node)
+ continue
+ assert isinstance(node, Collector)
+ rep = collect_one_node(node)
+ if rep.passed:
+ has_matched = False
+ for x in rep.result:
+ # TODO: remove parametrized workaround once collection structure contains parametrization
+ if x.name == name or x.name.split("[")[0] == name:
+ resultnodes.extend(self.matchnodes([x], nextnames))
+ has_matched = True
+ # XXX accept IDs that don't have "()" for class instances
+ if not has_matched and len(rep.result) == 1 and x.name == "()":
+ nextnames.insert(0, name)
+ resultnodes.extend(self.matchnodes([x], nextnames))
+ else:
+ # report collection failures here to avoid failing to run some test
+ # specified in the command line because the module could not be
+ # imported (#134)
+ node.ihook.pytest_collectreport(report=rep)
+ return resultnodes
+
+ def genitems(self, node):
+ self.trace("genitems", node)
+ if isinstance(node, Item):
+ node.ihook.pytest_itemcollected(item=node)
+ yield node
+ else:
+ assert isinstance(node, Collector)
+ rep = collect_one_node(node)
+ if rep.passed:
+ for subnode in rep.result:
+ for x in self.genitems(subnode):
+ yield x
+ node.ihook.pytest_collectreport(report=rep)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/mark.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/mark.py
new file mode 100644
index 00000000000..3f1f01b1a2e
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/mark.py
@@ -0,0 +1,496 @@
+""" generic mechanism for marking and selecting python functions. """
+from __future__ import absolute_import, division, print_function
+
+import inspect
+import warnings
+import attr
+from collections import namedtuple
+from operator import attrgetter
+from six.moves import map
+from .deprecated import MARK_PARAMETERSET_UNPACKING
+from .compat import NOTSET, getfslineno
+
+
+def alias(name, warning=None):
+ getter = attrgetter(name)
+
+ def warned(self):
+ warnings.warn(warning, stacklevel=2)
+ return getter(self)
+
+ return property(getter if warning is None else warned, doc='alias for ' + name)
+
+
+class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')):
+ @classmethod
+ def param(cls, *values, **kw):
+ marks = kw.pop('marks', ())
+ if isinstance(marks, MarkDecorator):
+ marks = marks,
+ else:
+ assert isinstance(marks, (tuple, list, set))
+
+ def param_extract_id(id=None):
+ return id
+
+ id = param_extract_id(**kw)
+ return cls(values, marks, id)
+
+ @classmethod
+ def extract_from(cls, parameterset, legacy_force_tuple=False):
+ """
+ :param parameterset:
+ a legacy style parameterset that may or may not be a tuple,
+ and may or may not be wrapped into a mess of mark objects
+
+ :param legacy_force_tuple:
+ enforce tuple wrapping so single argument tuple values
+ don't get decomposed and break tests
+
+ """
+
+ if isinstance(parameterset, cls):
+ return parameterset
+ if not isinstance(parameterset, MarkDecorator) and legacy_force_tuple:
+ return cls.param(parameterset)
+
+ newmarks = []
+ argval = parameterset
+ while isinstance(argval, MarkDecorator):
+ newmarks.append(MarkDecorator(Mark(
+ argval.markname, argval.args[:-1], argval.kwargs)))
+ argval = argval.args[-1]
+ assert not isinstance(argval, ParameterSet)
+ if legacy_force_tuple:
+ argval = argval,
+
+ if newmarks:
+ warnings.warn(MARK_PARAMETERSET_UNPACKING)
+
+ return cls(argval, marks=newmarks, id=None)
+
+ @classmethod
+ def _for_parameterize(cls, argnames, argvalues, function):
+ if not isinstance(argnames, (tuple, list)):
+ argnames = [x.strip() for x in argnames.split(",") if x.strip()]
+ force_tuple = len(argnames) == 1
+ else:
+ force_tuple = False
+ parameters = [
+ ParameterSet.extract_from(x, legacy_force_tuple=force_tuple)
+ for x in argvalues]
+ del argvalues
+
+ if not parameters:
+ fs, lineno = getfslineno(function)
+ reason = "got empty parameter set %r, function %s at %s:%d" % (
+ argnames, function.__name__, fs, lineno)
+ mark = MARK_GEN.skip(reason=reason)
+ parameters.append(ParameterSet(
+ values=(NOTSET,) * len(argnames),
+ marks=[mark],
+ id=None,
+ ))
+ return argnames, parameters
+
+
+class MarkerError(Exception):
+
+ """Error in use of a pytest marker/attribute."""
+
+
+def param(*values, **kw):
+ return ParameterSet.param(*values, **kw)
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("general")
+ group._addoption(
+ '-k',
+ action="store", dest="keyword", default='', metavar="EXPRESSION",
+ help="only run tests which match the given substring expression. "
+ "An expression is a python evaluatable expression "
+ "where all names are substring-matched against test names "
+ "and their parent classes. Example: -k 'test_method or test_"
+ "other' matches all test functions and classes whose name "
+ "contains 'test_method' or 'test_other', while -k 'not test_method' "
+ "matches those that don't contain 'test_method' in their names. "
+ "Additionally keywords are matched to classes and functions "
+ "containing extra names in their 'extra_keyword_matches' set, "
+ "as well as functions which have names assigned directly to them."
+ )
+
+ group._addoption(
+ "-m",
+ action="store", dest="markexpr", default="", metavar="MARKEXPR",
+ help="only run tests matching given mark expression. "
+ "example: -m 'mark1 and not mark2'."
+ )
+
+ group.addoption(
+ "--markers", action="store_true",
+ help="show markers (builtin, plugin and per-project ones)."
+ )
+
+ parser.addini("markers", "markers for test functions", 'linelist')
+
+
+def pytest_cmdline_main(config):
+ import _pytest.config
+ if config.option.markers:
+ config._do_configure()
+ tw = _pytest.config.create_terminal_writer(config)
+ for line in config.getini("markers"):
+ parts = line.split(":", 1)
+ name = parts[0]
+ rest = parts[1] if len(parts) == 2 else ''
+ tw.write("@pytest.mark.%s:" % name, bold=True)
+ tw.line(rest)
+ tw.line()
+ config._ensure_unconfigure()
+ return 0
+
+
+pytest_cmdline_main.tryfirst = True
+
+
+def pytest_collection_modifyitems(items, config):
+ keywordexpr = config.option.keyword.lstrip()
+ matchexpr = config.option.markexpr
+ if not keywordexpr and not matchexpr:
+ return
+ # pytest used to allow "-" for negating
+ # but today we just allow "-" at the beginning, use "not" instead
+ # we probably remove "-" altogether soon
+ if keywordexpr.startswith("-"):
+ keywordexpr = "not " + keywordexpr[1:]
+ selectuntil = False
+ if keywordexpr[-1:] == ":":
+ selectuntil = True
+ keywordexpr = keywordexpr[:-1]
+
+ remaining = []
+ deselected = []
+ for colitem in items:
+ if keywordexpr and not matchkeyword(colitem, keywordexpr):
+ deselected.append(colitem)
+ else:
+ if selectuntil:
+ keywordexpr = None
+ if matchexpr:
+ if not matchmark(colitem, matchexpr):
+ deselected.append(colitem)
+ continue
+ remaining.append(colitem)
+
+ if deselected:
+ config.hook.pytest_deselected(items=deselected)
+ items[:] = remaining
+
+
+@attr.s
+class MarkMapping(object):
+ """Provides a local mapping for markers where item access
+ resolves to True if the marker is present. """
+
+ own_mark_names = attr.ib()
+
+ @classmethod
+ def from_keywords(cls, keywords):
+ mark_names = set()
+ for key, value in keywords.items():
+ if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator):
+ mark_names.add(key)
+ return cls(mark_names)
+
+ def __getitem__(self, name):
+ return name in self.own_mark_names
+
+
+class KeywordMapping(object):
+ """Provides a local mapping for keywords.
+ Given a list of names, map any substring of one of these names to True.
+ """
+
+ def __init__(self, names):
+ self._names = names
+
+ def __getitem__(self, subname):
+ for name in self._names:
+ if subname in name:
+ return True
+ return False
+
+
+def matchmark(colitem, markexpr):
+ """Tries to match on any marker names, attached to the given colitem."""
+ return eval(markexpr, {}, MarkMapping.from_keywords(colitem.keywords))
+
+
+def matchkeyword(colitem, keywordexpr):
+ """Tries to match given keyword expression to given collector item.
+
+ Will match on the name of colitem, including the names of its parents.
+ Only matches names of items which are either a :class:`Class` or a
+ :class:`Function`.
+ Additionally, matches on names in the 'extra_keyword_matches' set of
+ any item, as well as names directly assigned to test functions.
+ """
+ mapped_names = set()
+
+ # Add the names of the current item and any parent items
+ import pytest
+ for item in colitem.listchain():
+ if not isinstance(item, pytest.Instance):
+ mapped_names.add(item.name)
+
+ # Add the names added as extra keywords to current or parent items
+ for name in colitem.listextrakeywords():
+ mapped_names.add(name)
+
+ # Add the names attached to the current function through direct assignment
+ if hasattr(colitem, 'function'):
+ for name in colitem.function.__dict__:
+ mapped_names.add(name)
+
+ mapping = KeywordMapping(mapped_names)
+ if " " not in keywordexpr:
+ # special case to allow for simple "-k pass" and "-k 1.3"
+ return mapping[keywordexpr]
+ elif keywordexpr.startswith("not ") and " " not in keywordexpr[4:]:
+ return not mapping[keywordexpr[4:]]
+ return eval(keywordexpr, {}, mapping)
+
+
+def pytest_configure(config):
+ config._old_mark_config = MARK_GEN._config
+ if config.option.strict:
+ MARK_GEN._config = config
+
+
+def pytest_unconfigure(config):
+ MARK_GEN._config = getattr(config, '_old_mark_config', None)
+
+
+class MarkGenerator:
+ """ Factory for :class:`MarkDecorator` objects - exposed as
+ a ``pytest.mark`` singleton instance. Example::
+
+ import pytest
+ @pytest.mark.slowtest
+ def test_function():
+ pass
+
+ will set a 'slowtest' :class:`MarkInfo` object
+ on the ``test_function`` object. """
+ _config = None
+
+ def __getattr__(self, name):
+ if name[0] == "_":
+ raise AttributeError("Marker name must NOT start with underscore")
+ if self._config is not None:
+ self._check(name)
+ return MarkDecorator(Mark(name, (), {}))
+
+ def _check(self, name):
+ try:
+ if name in self._markers:
+ return
+ except AttributeError:
+ pass
+ self._markers = values = set()
+ for line in self._config.getini("markers"):
+ marker = line.split(":", 1)[0]
+ marker = marker.rstrip()
+ x = marker.split("(", 1)[0]
+ values.add(x)
+ if name not in self._markers:
+ raise AttributeError("%r not a registered marker" % (name,))
+
+
+def istestfunc(func):
+ return hasattr(func, "__call__") and \
+ getattr(func, "__name__", "<lambda>") != "<lambda>"
+
+
+@attr.s(frozen=True)
+class Mark(object):
+ name = attr.ib()
+ args = attr.ib()
+ kwargs = attr.ib()
+
+ def combined_with(self, other):
+ assert self.name == other.name
+ return Mark(
+ self.name, self.args + other.args,
+ dict(self.kwargs, **other.kwargs))
+
+
+@attr.s
+class MarkDecorator(object):
+ """ A decorator for test functions and test classes. When applied
+ it will create :class:`MarkInfo` objects which may be
+ :ref:`retrieved by hooks as item keywords <excontrolskip>`.
+ MarkDecorator instances are often created like this::
+
+ mark1 = pytest.mark.NAME # simple MarkDecorator
+ mark2 = pytest.mark.NAME(name1=value) # parametrized MarkDecorator
+
+ and can then be applied as decorators to test functions::
+
+ @mark2
+ def test_function():
+ pass
+
+ When a MarkDecorator instance is called it does the following:
+ 1. If called with a single class as its only positional argument and no
+ additional keyword arguments, it attaches itself to the class so it
+ gets applied automatically to all test cases found in that class.
+ 2. If called with a single function as its only positional argument and
+ no additional keyword arguments, it attaches a MarkInfo object to the
+ function, containing all the arguments already stored internally in
+ the MarkDecorator.
+ 3. When called in any other case, it performs a 'fake construction' call,
+ i.e. it returns a new MarkDecorator instance with the original
+ MarkDecorator's content updated with the arguments passed to this
+ call.
+
+ Note: The rules above prevent MarkDecorator objects from storing only a
+ single function or class reference as their positional argument with no
+ additional keyword or positional arguments.
+
+ """
+
+ mark = attr.ib(validator=attr.validators.instance_of(Mark))
+
+ name = alias('mark.name')
+ args = alias('mark.args')
+ kwargs = alias('mark.kwargs')
+
+ @property
+ def markname(self):
+ return self.name # for backward-compat (2.4.1 had this attr)
+
+ def __eq__(self, other):
+ return self.mark == other.mark if isinstance(other, MarkDecorator) else False
+
+ def __repr__(self):
+ return "<MarkDecorator %r>" % (self.mark,)
+
+ def with_args(self, *args, **kwargs):
+ """ return a MarkDecorator with extra arguments added
+
+ unlike call this can be used even if the sole argument is a callable/class
+
+ :return: MarkDecorator
+ """
+
+ mark = Mark(self.name, args, kwargs)
+ return self.__class__(self.mark.combined_with(mark))
+
+ def __call__(self, *args, **kwargs):
+ """ if passed a single callable argument: decorate it with mark info.
+ otherwise add *args/**kwargs in-place to mark information. """
+ if args and not kwargs:
+ func = args[0]
+ is_class = inspect.isclass(func)
+ if len(args) == 1 and (istestfunc(func) or is_class):
+ if is_class:
+ store_mark(func, self.mark)
+ else:
+ store_legacy_markinfo(func, self.mark)
+ store_mark(func, self.mark)
+ return func
+ return self.with_args(*args, **kwargs)
+
+
+def get_unpacked_marks(obj):
+ """
+ obtain the unpacked marks that are stored on a object
+ """
+ mark_list = getattr(obj, 'pytestmark', [])
+
+ if not isinstance(mark_list, list):
+ mark_list = [mark_list]
+ return [
+ getattr(mark, 'mark', mark) # unpack MarkDecorator
+ for mark in mark_list
+ ]
+
+
+def store_mark(obj, mark):
+ """store a Mark on a object
+ this is used to implement the Mark declarations/decorators correctly
+ """
+ assert isinstance(mark, Mark), mark
+ # always reassign name to avoid updating pytestmark
+ # in a reference that was only borrowed
+ obj.pytestmark = get_unpacked_marks(obj) + [mark]
+
+
+def store_legacy_markinfo(func, mark):
+ """create the legacy MarkInfo objects and put them onto the function
+ """
+ if not isinstance(mark, Mark):
+ raise TypeError("got {mark!r} instead of a Mark".format(mark=mark))
+ holder = getattr(func, mark.name, None)
+ if holder is None:
+ holder = MarkInfo(mark)
+ setattr(func, mark.name, holder)
+ else:
+ holder.add_mark(mark)
+
+
+class MarkInfo(object):
+ """ Marking object created by :class:`MarkDecorator` instances. """
+
+ def __init__(self, mark):
+ assert isinstance(mark, Mark), repr(mark)
+ self.combined = mark
+ self._marks = [mark]
+
+ name = alias('combined.name')
+ args = alias('combined.args')
+ kwargs = alias('combined.kwargs')
+
+ def __repr__(self):
+ return "<MarkInfo {0!r}>".format(self.combined)
+
+ def add_mark(self, mark):
+ """ add a MarkInfo with the given args and kwargs. """
+ self._marks.append(mark)
+ self.combined = self.combined.combined_with(mark)
+
+ def __iter__(self):
+ """ yield MarkInfo objects each relating to a marking-call. """
+ return map(MarkInfo, self._marks)
+
+
+MARK_GEN = MarkGenerator()
+
+
+def _marked(func, mark):
+ """ Returns True if :func: is already marked with :mark:, False otherwise.
+ This can happen if marker is applied to class and the test file is
+ invoked more than once.
+ """
+ try:
+ func_mark = getattr(func, mark.name)
+ except AttributeError:
+ return False
+ return mark.args == func_mark.args and mark.kwargs == func_mark.kwargs
+
+
+def transfer_markers(funcobj, cls, mod):
+ """
+ this function transfers class level markers and module level markers
+ into function level markinfo objects
+
+ this is the main reason why marks are so broken
+ the resolution will involve phasing out function level MarkInfo objects
+
+ """
+ for obj in (cls, mod):
+ for mark in get_unpacked_marks(obj):
+ if not _marked(funcobj, mark):
+ store_legacy_markinfo(funcobj, mark)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/monkeypatch.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/monkeypatch.py
new file mode 100644
index 00000000000..40ae560f070
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/monkeypatch.py
@@ -0,0 +1,258 @@
+""" monkeypatching and mocking functionality. """
+from __future__ import absolute_import, division, print_function
+
+import os
+import sys
+import re
+import six
+from _pytest.fixtures import fixture
+
+RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
+
+
+@fixture
+def monkeypatch():
+ """The returned ``monkeypatch`` fixture provides these
+ helper methods to modify objects, dictionaries or os.environ::
+
+ monkeypatch.setattr(obj, name, value, raising=True)
+ monkeypatch.delattr(obj, name, raising=True)
+ monkeypatch.setitem(mapping, name, value)
+ monkeypatch.delitem(obj, name, raising=True)
+ monkeypatch.setenv(name, value, prepend=False)
+ monkeypatch.delenv(name, value, raising=True)
+ monkeypatch.syspath_prepend(path)
+ monkeypatch.chdir(path)
+
+ All modifications will be undone after the requesting
+ test function or fixture has finished. The ``raising``
+ parameter determines if a KeyError or AttributeError
+ will be raised if the set/deletion operation has no target.
+ """
+ mpatch = MonkeyPatch()
+ yield mpatch
+ mpatch.undo()
+
+
+def resolve(name):
+ # simplified from zope.dottedname
+ parts = name.split('.')
+
+ used = parts.pop(0)
+ found = __import__(used)
+ for part in parts:
+ used += '.' + part
+ try:
+ found = getattr(found, part)
+ except AttributeError:
+ pass
+ else:
+ continue
+ # we use explicit un-nesting of the handling block in order
+ # to avoid nested exceptions on python 3
+ try:
+ __import__(used)
+ except ImportError as ex:
+ # str is used for py2 vs py3
+ expected = str(ex).split()[-1]
+ if expected == used:
+ raise
+ else:
+ raise ImportError(
+ 'import error in %s: %s' % (used, ex)
+ )
+ found = annotated_getattr(found, part, used)
+ return found
+
+
+def annotated_getattr(obj, name, ann):
+ try:
+ obj = getattr(obj, name)
+ except AttributeError:
+ raise AttributeError(
+ '%r object at %s has no attribute %r' % (
+ type(obj).__name__, ann, name
+ )
+ )
+ return obj
+
+
+def derive_importpath(import_path, raising):
+ if not isinstance(import_path, six.string_types) or "." not in import_path:
+ raise TypeError("must be absolute import path string, not %r" %
+ (import_path,))
+ module, attr = import_path.rsplit('.', 1)
+ target = resolve(module)
+ if raising:
+ annotated_getattr(target, attr, ann=module)
+ return attr, target
+
+
+class Notset:
+ def __repr__(self):
+ return "<notset>"
+
+
+notset = Notset()
+
+
+class MonkeyPatch:
+ """ Object returned by the ``monkeypatch`` fixture keeping a record of setattr/item/env/syspath changes.
+ """
+
+ def __init__(self):
+ self._setattr = []
+ self._setitem = []
+ self._cwd = None
+ self._savesyspath = None
+
+ def setattr(self, target, name, value=notset, raising=True):
+ """ Set attribute value on target, memorizing the old value.
+ By default raise AttributeError if the attribute did not exist.
+
+ For convenience you can specify a string as ``target`` which
+ will be interpreted as a dotted import path, with the last part
+ being the attribute name. Example:
+ ``monkeypatch.setattr("os.getcwd", lambda x: "/")``
+ would set the ``getcwd`` function of the ``os`` module.
+
+ The ``raising`` value determines if the setattr should fail
+ if the attribute is not already present (defaults to True
+ which means it will raise).
+ """
+ __tracebackhide__ = True
+ import inspect
+
+ if value is notset:
+ if not isinstance(target, six.string_types):
+ raise TypeError("use setattr(target, name, value) or "
+ "setattr(target, value) with target being a dotted "
+ "import string")
+ value = name
+ name, target = derive_importpath(target, raising)
+
+ oldval = getattr(target, name, notset)
+ if raising and oldval is notset:
+ raise AttributeError("%r has no attribute %r" % (target, name))
+
+ # avoid class descriptors like staticmethod/classmethod
+ if inspect.isclass(target):
+ oldval = target.__dict__.get(name, notset)
+ self._setattr.append((target, name, oldval))
+ setattr(target, name, value)
+
+ def delattr(self, target, name=notset, raising=True):
+ """ Delete attribute ``name`` from ``target``, by default raise
+ AttributeError it the attribute did not previously exist.
+
+ If no ``name`` is specified and ``target`` is a string
+ it will be interpreted as a dotted import path with the
+ last part being the attribute name.
+
+ If ``raising`` is set to False, no exception will be raised if the
+ attribute is missing.
+ """
+ __tracebackhide__ = True
+ if name is notset:
+ if not isinstance(target, six.string_types):
+ raise TypeError("use delattr(target, name) or "
+ "delattr(target) with target being a dotted "
+ "import string")
+ name, target = derive_importpath(target, raising)
+
+ if not hasattr(target, name):
+ if raising:
+ raise AttributeError(name)
+ else:
+ self._setattr.append((target, name, getattr(target, name, notset)))
+ delattr(target, name)
+
+ def setitem(self, dic, name, value):
+ """ Set dictionary entry ``name`` to value. """
+ self._setitem.append((dic, name, dic.get(name, notset)))
+ dic[name] = value
+
+ def delitem(self, dic, name, raising=True):
+ """ Delete ``name`` from dict. Raise KeyError if it doesn't exist.
+
+ If ``raising`` is set to False, no exception will be raised if the
+ key is missing.
+ """
+ if name not in dic:
+ if raising:
+ raise KeyError(name)
+ else:
+ self._setitem.append((dic, name, dic.get(name, notset)))
+ del dic[name]
+
+ def setenv(self, name, value, prepend=None):
+ """ Set environment variable ``name`` to ``value``. If ``prepend``
+ is a character, read the current environment variable value
+ and prepend the ``value`` adjoined with the ``prepend`` character."""
+ value = str(value)
+ if prepend and name in os.environ:
+ value = value + prepend + os.environ[name]
+ self.setitem(os.environ, name, value)
+
+ def delenv(self, name, raising=True):
+ """ Delete ``name`` from the environment. Raise KeyError it does not
+ exist.
+
+ If ``raising`` is set to False, no exception will be raised if the
+ environment variable is missing.
+ """
+ self.delitem(os.environ, name, raising=raising)
+
+ def syspath_prepend(self, path):
+ """ Prepend ``path`` to ``sys.path`` list of import locations. """
+ if self._savesyspath is None:
+ self._savesyspath = sys.path[:]
+ sys.path.insert(0, str(path))
+
+ def chdir(self, path):
+ """ Change the current working directory to the specified path.
+ Path can be a string or a py.path.local object.
+ """
+ if self._cwd is None:
+ self._cwd = os.getcwd()
+ if hasattr(path, "chdir"):
+ path.chdir()
+ else:
+ os.chdir(path)
+
+ def undo(self):
+ """ Undo previous changes. This call consumes the
+ undo stack. Calling it a second time has no effect unless
+ you do more monkeypatching after the undo call.
+
+ There is generally no need to call `undo()`, since it is
+ called automatically during tear-down.
+
+ Note that the same `monkeypatch` fixture is used across a
+ single test function invocation. If `monkeypatch` is used both by
+ the test function itself and one of the test fixtures,
+ calling `undo()` will undo all of the changes made in
+ both functions.
+ """
+ for obj, name, value in reversed(self._setattr):
+ if value is not notset:
+ setattr(obj, name, value)
+ else:
+ delattr(obj, name)
+ self._setattr[:] = []
+ for dictionary, name, value in reversed(self._setitem):
+ if value is notset:
+ try:
+ del dictionary[name]
+ except KeyError:
+ pass # was already deleted, so we have the desired state
+ else:
+ dictionary[name] = value
+ self._setitem[:] = []
+ if self._savesyspath is not None:
+ sys.path[:] = self._savesyspath
+ self._savesyspath = None
+
+ if self._cwd is not None:
+ os.chdir(self._cwd)
+ self._cwd = None
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/nodes.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/nodes.py
new file mode 100644
index 00000000000..ad3af2ce67c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/nodes.py
@@ -0,0 +1,37 @@
+SEP = "/"
+
+
+def _splitnode(nodeid):
+ """Split a nodeid into constituent 'parts'.
+
+ Node IDs are strings, and can be things like:
+ ''
+ 'testing/code'
+ 'testing/code/test_excinfo.py'
+ 'testing/code/test_excinfo.py::TestFormattedExcinfo::()'
+
+ Return values are lists e.g.
+ []
+ ['testing', 'code']
+ ['testing', 'code', 'test_excinfo.py']
+ ['testing', 'code', 'test_excinfo.py', 'TestFormattedExcinfo', '()']
+ """
+ if nodeid == '':
+ # If there is no root node at all, return an empty list so the caller's logic can remain sane
+ return []
+ parts = nodeid.split(SEP)
+ # Replace single last element 'test_foo.py::Bar::()' with multiple elements 'test_foo.py', 'Bar', '()'
+ parts[-1:] = parts[-1].split("::")
+ return parts
+
+
+def ischildnode(baseid, nodeid):
+ """Return True if the nodeid is a child node of the baseid.
+
+ E.g. 'foo/bar::Baz::()' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp'
+ """
+ base_parts = _splitnode(baseid)
+ node_parts = _splitnode(nodeid)
+ if len(node_parts) < len(base_parts):
+ return False
+ return node_parts[:len(base_parts)] == base_parts
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/nose.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/nose.py
new file mode 100644
index 00000000000..c81542eadf3
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/nose.py
@@ -0,0 +1,72 @@
+""" run test suites written for nose. """
+from __future__ import absolute_import, division, print_function
+
+import sys
+
+from _pytest import unittest, runner, python
+from _pytest.config import hookimpl
+
+
+def get_skip_exceptions():
+ skip_classes = set()
+ for module_name in ('unittest', 'unittest2', 'nose'):
+ mod = sys.modules.get(module_name)
+ if hasattr(mod, 'SkipTest'):
+ skip_classes.add(mod.SkipTest)
+ return tuple(skip_classes)
+
+
+def pytest_runtest_makereport(item, call):
+ if call.excinfo and call.excinfo.errisinstance(get_skip_exceptions()):
+ # let's substitute the excinfo with a pytest.skip one
+ call2 = call.__class__(
+ lambda: runner.skip(str(call.excinfo.value)), call.when)
+ call.excinfo = call2.excinfo
+
+
+@hookimpl(trylast=True)
+def pytest_runtest_setup(item):
+ if is_potential_nosetest(item):
+ if isinstance(item.parent, python.Generator):
+ gen = item.parent
+ if not hasattr(gen, '_nosegensetup'):
+ call_optional(gen.obj, 'setup')
+ if isinstance(gen.parent, python.Instance):
+ call_optional(gen.parent.obj, 'setup')
+ gen._nosegensetup = True
+ if not call_optional(item.obj, 'setup'):
+ # call module level setup if there is no object level one
+ call_optional(item.parent.obj, 'setup')
+ # XXX this implies we only call teardown when setup worked
+ item.session._setupstate.addfinalizer((lambda: teardown_nose(item)), item)
+
+
+def teardown_nose(item):
+ if is_potential_nosetest(item):
+ if not call_optional(item.obj, 'teardown'):
+ call_optional(item.parent.obj, 'teardown')
+ # if hasattr(item.parent, '_nosegensetup'):
+ # #call_optional(item._nosegensetup, 'teardown')
+ # del item.parent._nosegensetup
+
+
+def pytest_make_collect_report(collector):
+ if isinstance(collector, python.Generator):
+ call_optional(collector.obj, 'setup')
+
+
+def is_potential_nosetest(item):
+ # extra check needed since we do not do nose style setup/teardown
+ # on direct unittest style classes
+ return isinstance(item, python.Function) and \
+ not isinstance(item, unittest.TestCaseFunction)
+
+
+def call_optional(obj, name):
+ method = getattr(obj, name, None)
+ isfixture = hasattr(method, "_pytestfixturefunction")
+ if method is not None and not isfixture and callable(method):
+ # If there's any problems allow the exception to raise rather than
+ # silently ignoring them
+ method()
+ return True
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/outcomes.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/outcomes.py
new file mode 100644
index 00000000000..7f0c18fa6c1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/outcomes.py
@@ -0,0 +1,147 @@
+"""
+exception classes and constants handling test outcomes
+as well as functions creating them
+"""
+from __future__ import absolute_import, division, print_function
+import py
+import sys
+
+
+class OutcomeException(BaseException):
+ """ OutcomeException and its subclass instances indicate and
+ contain info about test and collection outcomes.
+ """
+ def __init__(self, msg=None, pytrace=True):
+ BaseException.__init__(self, msg)
+ self.msg = msg
+ self.pytrace = pytrace
+
+ def __repr__(self):
+ if self.msg:
+ val = self.msg
+ if isinstance(val, bytes):
+ val = py._builtin._totext(val, errors='replace')
+ return val
+ return "<%s instance>" % (self.__class__.__name__,)
+ __str__ = __repr__
+
+
+TEST_OUTCOME = (OutcomeException, Exception)
+
+
+class Skipped(OutcomeException):
+ # XXX hackish: on 3k we fake to live in the builtins
+ # in order to have Skipped exception printing shorter/nicer
+ __module__ = 'builtins'
+
+ def __init__(self, msg=None, pytrace=True, allow_module_level=False):
+ OutcomeException.__init__(self, msg=msg, pytrace=pytrace)
+ self.allow_module_level = allow_module_level
+
+
+class Failed(OutcomeException):
+ """ raised from an explicit call to pytest.fail() """
+ __module__ = 'builtins'
+
+
+class Exit(KeyboardInterrupt):
+ """ raised for immediate program exits (no tracebacks/summaries)"""
+ def __init__(self, msg="unknown reason"):
+ self.msg = msg
+ KeyboardInterrupt.__init__(self, msg)
+
+# exposed helper methods
+
+
+def exit(msg):
+ """ exit testing process as if KeyboardInterrupt was triggered. """
+ __tracebackhide__ = True
+ raise Exit(msg)
+
+
+exit.Exception = Exit
+
+
+def skip(msg="", **kwargs):
+ """ skip an executing test with the given message. Note: it's usually
+ better to use the pytest.mark.skipif marker to declare a test to be
+ skipped under certain conditions like mismatching platforms or
+ dependencies. See the pytest_skipping plugin for details.
+
+ :kwarg bool allow_module_level: allows this function to be called at
+ module level, skipping the rest of the module. Default to False.
+ """
+ __tracebackhide__ = True
+ allow_module_level = kwargs.pop('allow_module_level', False)
+ if kwargs:
+ keys = [k for k in kwargs.keys()]
+ raise TypeError('unexpected keyword arguments: {0}'.format(keys))
+ raise Skipped(msg=msg, allow_module_level=allow_module_level)
+
+
+skip.Exception = Skipped
+
+
+def fail(msg="", pytrace=True):
+ """ explicitly fail an currently-executing test with the given Message.
+
+ :arg pytrace: if false the msg represents the full failure information
+ and no python traceback will be reported.
+ """
+ __tracebackhide__ = True
+ raise Failed(msg=msg, pytrace=pytrace)
+
+
+fail.Exception = Failed
+
+
+class XFailed(fail.Exception):
+ """ raised from an explicit call to pytest.xfail() """
+
+
+def xfail(reason=""):
+ """ xfail an executing test or setup functions with the given reason."""
+ __tracebackhide__ = True
+ raise XFailed(reason)
+
+
+xfail.Exception = XFailed
+
+
+def importorskip(modname, minversion=None):
+ """ return imported module if it has at least "minversion" as its
+ __version__ attribute. If no minversion is specified the a skip
+ is only triggered if the module can not be imported.
+ """
+ import warnings
+ __tracebackhide__ = True
+ compile(modname, '', 'eval') # to catch syntaxerrors
+ should_skip = False
+
+ with warnings.catch_warnings():
+ # make sure to ignore ImportWarnings that might happen because
+ # of existing directories with the same name we're trying to
+ # import but without a __init__.py file
+ warnings.simplefilter('ignore')
+ try:
+ __import__(modname)
+ except ImportError:
+ # Do not raise chained exception here(#1485)
+ should_skip = True
+ if should_skip:
+ raise Skipped("could not import %r" % (modname,), allow_module_level=True)
+ mod = sys.modules[modname]
+ if minversion is None:
+ return mod
+ verattr = getattr(mod, '__version__', None)
+ if minversion is not None:
+ try:
+ from pkg_resources import parse_version as pv
+ except ImportError:
+ raise Skipped("we have a required version for %r but can not import "
+ "pkg_resources to parse version strings." % (modname,),
+ allow_module_level=True)
+ if verattr is None or pv(verattr) < pv(minversion):
+ raise Skipped("module %r has __version__ %r, required is: %r" % (
+ modname, verattr, minversion), allow_module_level=True)
+ return mod
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/pastebin.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/pastebin.py
new file mode 100644
index 00000000000..b588b021b12
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/pastebin.py
@@ -0,0 +1,100 @@
+""" submit failure or test session information to a pastebin service. """
+from __future__ import absolute_import, division, print_function
+
+import pytest
+import six
+import sys
+import tempfile
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("terminal reporting")
+ group._addoption('--pastebin', metavar="mode",
+ action='store', dest="pastebin", default=None,
+ choices=['failed', 'all'],
+ help="send failed|all info to bpaste.net pastebin service.")
+
+
+@pytest.hookimpl(trylast=True)
+def pytest_configure(config):
+ if config.option.pastebin == "all":
+ tr = config.pluginmanager.getplugin('terminalreporter')
+ # if no terminal reporter plugin is present, nothing we can do here;
+ # this can happen when this function executes in a slave node
+ # when using pytest-xdist, for example
+ if tr is not None:
+ # pastebin file will be utf-8 encoded binary file
+ config._pastebinfile = tempfile.TemporaryFile('w+b')
+ oldwrite = tr._tw.write
+
+ def tee_write(s, **kwargs):
+ oldwrite(s, **kwargs)
+ if isinstance(s, six.text_type):
+ s = s.encode('utf-8')
+ config._pastebinfile.write(s)
+
+ tr._tw.write = tee_write
+
+
+def pytest_unconfigure(config):
+ if hasattr(config, '_pastebinfile'):
+ # get terminal contents and delete file
+ config._pastebinfile.seek(0)
+ sessionlog = config._pastebinfile.read()
+ config._pastebinfile.close()
+ del config._pastebinfile
+ # undo our patching in the terminal reporter
+ tr = config.pluginmanager.getplugin('terminalreporter')
+ del tr._tw.__dict__['write']
+ # write summary
+ tr.write_sep("=", "Sending information to Paste Service")
+ pastebinurl = create_new_paste(sessionlog)
+ tr.write_line("pastebin session-log: %s\n" % pastebinurl)
+
+
+def create_new_paste(contents):
+ """
+ Creates a new paste using bpaste.net service.
+
+ :contents: paste contents as utf-8 encoded bytes
+ :returns: url to the pasted contents
+ """
+ import re
+ if sys.version_info < (3, 0):
+ from urllib import urlopen, urlencode
+ else:
+ from urllib.request import urlopen
+ from urllib.parse import urlencode
+
+ params = {
+ 'code': contents,
+ 'lexer': 'python3' if sys.version_info[0] == 3 else 'python',
+ 'expiry': '1week',
+ }
+ url = 'https://bpaste.net'
+ response = urlopen(url, data=urlencode(params).encode('ascii')).read()
+ m = re.search(r'href="/raw/(\w+)"', response.decode('utf-8'))
+ if m:
+ return '%s/show/%s' % (url, m.group(1))
+ else:
+ return 'bad response: ' + response
+
+
+def pytest_terminal_summary(terminalreporter):
+ import _pytest.config
+ if terminalreporter.config.option.pastebin != "failed":
+ return
+ tr = terminalreporter
+ if 'failed' in tr.stats:
+ terminalreporter.write_sep("=", "Sending information to Paste Service")
+ for rep in terminalreporter.stats.get('failed'):
+ try:
+ msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc
+ except AttributeError:
+ msg = tr._getfailureheadline(rep)
+ tw = _pytest.config.create_terminal_writer(terminalreporter.config, stringio=True)
+ rep.toterminal(tw)
+ s = tw.stringio.getvalue()
+ assert len(s)
+ pastebinurl = create_new_paste(s)
+ tr.write_line("%s --> %s" % (msg, pastebinurl))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/pytester.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/pytester.py
new file mode 100644
index 00000000000..f2dd5994f1b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/pytester.py
@@ -0,0 +1,1187 @@
+""" (disabled by default) support for testing pytest and pytest plugins. """
+from __future__ import absolute_import, division, print_function
+
+import codecs
+import gc
+import os
+import platform
+import re
+import subprocess
+import six
+import sys
+import time
+import traceback
+from fnmatch import fnmatch
+
+from weakref import WeakKeyDictionary
+
+from _pytest.capture import MultiCapture, SysCapture
+from _pytest._code import Source
+import py
+import pytest
+from _pytest.main import Session, EXIT_OK
+from _pytest.assertion.rewrite import AssertionRewritingHook
+
+
+PYTEST_FULLPATH = os.path.abspath(pytest.__file__.rstrip("oc")).replace("$py.class", ".py")
+
+
+def pytest_addoption(parser):
+ # group = parser.getgroup("pytester", "pytester (self-tests) options")
+ parser.addoption('--lsof',
+ action="store_true", dest="lsof", default=False,
+ help=("run FD checks if lsof is available"))
+
+ parser.addoption('--runpytest', default="inprocess", dest="runpytest",
+ choices=("inprocess", "subprocess", ),
+ help=("run pytest sub runs in tests using an 'inprocess' "
+ "or 'subprocess' (python -m main) method"))
+
+
+def pytest_configure(config):
+ if config.getvalue("lsof"):
+ checker = LsofFdLeakChecker()
+ if checker.matching_platform():
+ config.pluginmanager.register(checker)
+
+
+class LsofFdLeakChecker(object):
+ def get_open_files(self):
+ out = self._exec_lsof()
+ open_files = self._parse_lsof_output(out)
+ return open_files
+
+ def _exec_lsof(self):
+ pid = os.getpid()
+ return py.process.cmdexec("lsof -Ffn0 -p %d" % pid)
+
+ def _parse_lsof_output(self, out):
+ def isopen(line):
+ return line.startswith('f') and ("deleted" not in line and
+ 'mem' not in line and "txt" not in line and 'cwd' not in line)
+
+ open_files = []
+
+ for line in out.split("\n"):
+ if isopen(line):
+ fields = line.split('\0')
+ fd = fields[0][1:]
+ filename = fields[1][1:]
+ if filename.startswith('/'):
+ open_files.append((fd, filename))
+
+ return open_files
+
+ def matching_platform(self):
+ try:
+ py.process.cmdexec("lsof -v")
+ except (py.process.cmdexec.Error, UnicodeDecodeError):
+ # cmdexec may raise UnicodeDecodeError on Windows systems
+ # with locale other than english:
+ # https://bitbucket.org/pytest-dev/py/issues/66
+ return False
+ else:
+ return True
+
+ @pytest.hookimpl(hookwrapper=True, tryfirst=True)
+ def pytest_runtest_protocol(self, item):
+ lines1 = self.get_open_files()
+ yield
+ if hasattr(sys, "pypy_version_info"):
+ gc.collect()
+ lines2 = self.get_open_files()
+
+ new_fds = set([t[0] for t in lines2]) - set([t[0] for t in lines1])
+ leaked_files = [t for t in lines2 if t[0] in new_fds]
+ if leaked_files:
+ error = []
+ error.append("***** %s FD leakage detected" % len(leaked_files))
+ error.extend([str(f) for f in leaked_files])
+ error.append("*** Before:")
+ error.extend([str(f) for f in lines1])
+ error.append("*** After:")
+ error.extend([str(f) for f in lines2])
+ error.append(error[0])
+ error.append("*** function %s:%s: %s " % item.location)
+ error.append("See issue #2366")
+ item.warn('', "\n".join(error))
+
+
+# XXX copied from execnet's conftest.py - needs to be merged
+winpymap = {
+ 'python2.7': r'C:\Python27\python.exe',
+ 'python3.4': r'C:\Python34\python.exe',
+ 'python3.5': r'C:\Python35\python.exe',
+ 'python3.6': r'C:\Python36\python.exe',
+}
+
+
+def getexecutable(name, cache={}):
+ try:
+ return cache[name]
+ except KeyError:
+ executable = py.path.local.sysfind(name)
+ if executable:
+ import subprocess
+ popen = subprocess.Popen([str(executable), "--version"],
+ universal_newlines=True, stderr=subprocess.PIPE)
+ out, err = popen.communicate()
+ if name == "jython":
+ if not err or "2.5" not in err:
+ executable = None
+ if "2.5.2" in err:
+ executable = None # http://bugs.jython.org/issue1790
+ elif popen.returncode != 0:
+ # Handle pyenv's 127.
+ executable = None
+ cache[name] = executable
+ return executable
+
+
+@pytest.fixture(params=['python2.7', 'python3.4', 'pypy', 'pypy3'])
+def anypython(request):
+ name = request.param
+ executable = getexecutable(name)
+ if executable is None:
+ if sys.platform == "win32":
+ executable = winpymap.get(name, None)
+ if executable:
+ executable = py.path.local(executable)
+ if executable.check():
+ return executable
+ pytest.skip("no suitable %s found" % (name,))
+ return executable
+
+# used at least by pytest-xdist plugin
+
+
+@pytest.fixture
+def _pytest(request):
+ """ Return a helper which offers a gethookrecorder(hook)
+ method which returns a HookRecorder instance which helps
+ to make assertions about called hooks.
+ """
+ return PytestArg(request)
+
+
+class PytestArg:
+ def __init__(self, request):
+ self.request = request
+
+ def gethookrecorder(self, hook):
+ hookrecorder = HookRecorder(hook._pm)
+ self.request.addfinalizer(hookrecorder.finish_recording)
+ return hookrecorder
+
+
+def get_public_names(values):
+ """Only return names from iterator values without a leading underscore."""
+ return [x for x in values if x[0] != "_"]
+
+
+class ParsedCall:
+ def __init__(self, name, kwargs):
+ self.__dict__.update(kwargs)
+ self._name = name
+
+ def __repr__(self):
+ d = self.__dict__.copy()
+ del d['_name']
+ return "<ParsedCall %r(**%r)>" % (self._name, d)
+
+
+class HookRecorder:
+ """Record all hooks called in a plugin manager.
+
+ This wraps all the hook calls in the plugin manager, recording
+ each call before propagating the normal calls.
+
+ """
+
+ def __init__(self, pluginmanager):
+ self._pluginmanager = pluginmanager
+ self.calls = []
+
+ def before(hook_name, hook_impls, kwargs):
+ self.calls.append(ParsedCall(hook_name, kwargs))
+
+ def after(outcome, hook_name, hook_impls, kwargs):
+ pass
+
+ self._undo_wrapping = pluginmanager.add_hookcall_monitoring(before, after)
+
+ def finish_recording(self):
+ self._undo_wrapping()
+
+ def getcalls(self, names):
+ if isinstance(names, str):
+ names = names.split()
+ return [call for call in self.calls if call._name in names]
+
+ def assert_contains(self, entries):
+ __tracebackhide__ = True
+ i = 0
+ entries = list(entries)
+ backlocals = sys._getframe(1).f_locals
+ while entries:
+ name, check = entries.pop(0)
+ for ind, call in enumerate(self.calls[i:]):
+ if call._name == name:
+ print("NAMEMATCH", name, call)
+ if eval(check, backlocals, call.__dict__):
+ print("CHECKERMATCH", repr(check), "->", call)
+ else:
+ print("NOCHECKERMATCH", repr(check), "-", call)
+ continue
+ i += ind + 1
+ break
+ print("NONAMEMATCH", name, "with", call)
+ else:
+ pytest.fail("could not find %r check %r" % (name, check))
+
+ def popcall(self, name):
+ __tracebackhide__ = True
+ for i, call in enumerate(self.calls):
+ if call._name == name:
+ del self.calls[i]
+ return call
+ lines = ["could not find call %r, in:" % (name,)]
+ lines.extend([" %s" % str(x) for x in self.calls])
+ pytest.fail("\n".join(lines))
+
+ def getcall(self, name):
+ values = self.getcalls(name)
+ assert len(values) == 1, (name, values)
+ return values[0]
+
+ # functionality for test reports
+
+ def getreports(self,
+ names="pytest_runtest_logreport pytest_collectreport"):
+ return [x.report for x in self.getcalls(names)]
+
+ def matchreport(self, inamepart="",
+ names="pytest_runtest_logreport pytest_collectreport", when=None):
+ """ return a testreport whose dotted import path matches """
+ values = []
+ for rep in self.getreports(names=names):
+ try:
+ if not when and rep.when != "call" and rep.passed:
+ # setup/teardown passing reports - let's ignore those
+ continue
+ except AttributeError:
+ pass
+ if when and getattr(rep, 'when', None) != when:
+ continue
+ if not inamepart or inamepart in rep.nodeid.split("::"):
+ values.append(rep)
+ if not values:
+ raise ValueError("could not find test report matching %r: "
+ "no test reports at all!" % (inamepart,))
+ if len(values) > 1:
+ raise ValueError(
+ "found 2 or more testreports matching %r: %s" % (inamepart, values))
+ return values[0]
+
+ def getfailures(self,
+ names='pytest_runtest_logreport pytest_collectreport'):
+ return [rep for rep in self.getreports(names) if rep.failed]
+
+ def getfailedcollections(self):
+ return self.getfailures('pytest_collectreport')
+
+ def listoutcomes(self):
+ passed = []
+ skipped = []
+ failed = []
+ for rep in self.getreports(
+ "pytest_collectreport pytest_runtest_logreport"):
+ if rep.passed:
+ if getattr(rep, "when", None) == "call":
+ passed.append(rep)
+ elif rep.skipped:
+ skipped.append(rep)
+ elif rep.failed:
+ failed.append(rep)
+ return passed, skipped, failed
+
+ def countoutcomes(self):
+ return [len(x) for x in self.listoutcomes()]
+
+ def assertoutcome(self, passed=0, skipped=0, failed=0):
+ realpassed, realskipped, realfailed = self.listoutcomes()
+ assert passed == len(realpassed)
+ assert skipped == len(realskipped)
+ assert failed == len(realfailed)
+
+ def clear(self):
+ self.calls[:] = []
+
+
+@pytest.fixture
+def linecomp(request):
+ return LineComp()
+
+
+@pytest.fixture(name='LineMatcher')
+def LineMatcher_fixture(request):
+ return LineMatcher
+
+
+@pytest.fixture
+def testdir(request, tmpdir_factory):
+ return Testdir(request, tmpdir_factory)
+
+
+rex_outcome = re.compile(r"(\d+) ([\w-]+)")
+
+
+class RunResult:
+ """The result of running a command.
+
+ Attributes:
+
+ :ret: The return value.
+ :outlines: List of lines captured from stdout.
+ :errlines: List of lines captures from stderr.
+ :stdout: :py:class:`LineMatcher` of stdout, use ``stdout.str()`` to
+ reconstruct stdout or the commonly used
+ ``stdout.fnmatch_lines()`` method.
+ :stderrr: :py:class:`LineMatcher` of stderr.
+ :duration: Duration in seconds.
+
+ """
+
+ def __init__(self, ret, outlines, errlines, duration):
+ self.ret = ret
+ self.outlines = outlines
+ self.errlines = errlines
+ self.stdout = LineMatcher(outlines)
+ self.stderr = LineMatcher(errlines)
+ self.duration = duration
+
+ def parseoutcomes(self):
+ """ Return a dictionary of outcomestring->num from parsing
+ the terminal output that the test process produced."""
+ for line in reversed(self.outlines):
+ if 'seconds' in line:
+ outcomes = rex_outcome.findall(line)
+ if outcomes:
+ d = {}
+ for num, cat in outcomes:
+ d[cat] = int(num)
+ return d
+ raise ValueError("Pytest terminal report not found")
+
+ def assert_outcomes(self, passed=0, skipped=0, failed=0, error=0):
+ """ assert that the specified outcomes appear with the respective
+ numbers (0 means it didn't occur) in the text output from a test run."""
+ d = self.parseoutcomes()
+ obtained = {
+ 'passed': d.get('passed', 0),
+ 'skipped': d.get('skipped', 0),
+ 'failed': d.get('failed', 0),
+ 'error': d.get('error', 0),
+ }
+ assert obtained == dict(passed=passed, skipped=skipped, failed=failed, error=error)
+
+
+class Testdir:
+ """Temporary test directory with tools to test/run pytest itself.
+
+ This is based on the ``tmpdir`` fixture but provides a number of
+ methods which aid with testing pytest itself. Unless
+ :py:meth:`chdir` is used all methods will use :py:attr:`tmpdir` as
+ current working directory.
+
+ Attributes:
+
+ :tmpdir: The :py:class:`py.path.local` instance of the temporary
+ directory.
+
+ :plugins: A list of plugins to use with :py:meth:`parseconfig` and
+ :py:meth:`runpytest`. Initially this is an empty list but
+ plugins can be added to the list. The type of items to add to
+ the list depend on the method which uses them so refer to them
+ for details.
+
+ """
+
+ def __init__(self, request, tmpdir_factory):
+ self.request = request
+ self._mod_collections = WeakKeyDictionary()
+ name = request.function.__name__
+ self.tmpdir = tmpdir_factory.mktemp(name, numbered=True)
+ self.plugins = []
+ self._savesyspath = (list(sys.path), list(sys.meta_path))
+ self._savemodulekeys = set(sys.modules)
+ self.chdir() # always chdir
+ self.request.addfinalizer(self.finalize)
+ method = self.request.config.getoption("--runpytest")
+ if method == "inprocess":
+ self._runpytest_method = self.runpytest_inprocess
+ elif method == "subprocess":
+ self._runpytest_method = self.runpytest_subprocess
+
+ def __repr__(self):
+ return "<Testdir %r>" % (self.tmpdir,)
+
+ def finalize(self):
+ """Clean up global state artifacts.
+
+ Some methods modify the global interpreter state and this
+ tries to clean this up. It does not remove the temporary
+ directory however so it can be looked at after the test run
+ has finished.
+
+ """
+ sys.path[:], sys.meta_path[:] = self._savesyspath
+ if hasattr(self, '_olddir'):
+ self._olddir.chdir()
+ self.delete_loaded_modules()
+
+ def delete_loaded_modules(self):
+ """Delete modules that have been loaded during a test.
+
+ This allows the interpreter to catch module changes in case
+ the module is re-imported.
+ """
+ for name in set(sys.modules).difference(self._savemodulekeys):
+ # some zope modules used by twisted-related tests keeps internal
+ # state and can't be deleted; we had some trouble in the past
+ # with zope.interface for example
+ if not name.startswith("zope"):
+ del sys.modules[name]
+
+ def make_hook_recorder(self, pluginmanager):
+ """Create a new :py:class:`HookRecorder` for a PluginManager."""
+ assert not hasattr(pluginmanager, "reprec")
+ pluginmanager.reprec = reprec = HookRecorder(pluginmanager)
+ self.request.addfinalizer(reprec.finish_recording)
+ return reprec
+
+ def chdir(self):
+ """Cd into the temporary directory.
+
+ This is done automatically upon instantiation.
+
+ """
+ old = self.tmpdir.chdir()
+ if not hasattr(self, '_olddir'):
+ self._olddir = old
+
+ def _makefile(self, ext, args, kwargs, encoding='utf-8'):
+ items = list(kwargs.items())
+
+ def to_text(s):
+ return s.decode(encoding) if isinstance(s, bytes) else six.text_type(s)
+
+ if args:
+ source = u"\n".join(to_text(x) for x in args)
+ basename = self.request.function.__name__
+ items.insert(0, (basename, source))
+
+ ret = None
+ for basename, value in items:
+ p = self.tmpdir.join(basename).new(ext=ext)
+ p.dirpath().ensure_dir()
+ source = Source(value)
+ source = u"\n".join(to_text(line) for line in source.lines)
+ p.write(source.strip().encode(encoding), "wb")
+ if ret is None:
+ ret = p
+ return ret
+
+ def makefile(self, ext, *args, **kwargs):
+ """Create a new file in the testdir.
+
+ ext: The extension the file should use, including the dot.
+ E.g. ".py".
+
+ args: All args will be treated as strings and joined using
+ newlines. The result will be written as contents to the
+ file. The name of the file will be based on the test
+ function requesting this fixture.
+ E.g. "testdir.makefile('.txt', 'line1', 'line2')"
+
+ kwargs: Each keyword is the name of a file, while the value of
+ it will be written as contents of the file.
+ E.g. "testdir.makefile('.ini', pytest='[pytest]\naddopts=-rs\n')"
+
+ """
+ return self._makefile(ext, args, kwargs)
+
+ def makeconftest(self, source):
+ """Write a contest.py file with 'source' as contents."""
+ return self.makepyfile(conftest=source)
+
+ def makeini(self, source):
+ """Write a tox.ini file with 'source' as contents."""
+ return self.makefile('.ini', tox=source)
+
+ def getinicfg(self, source):
+ """Return the pytest section from the tox.ini config file."""
+ p = self.makeini(source)
+ return py.iniconfig.IniConfig(p)['pytest']
+
+ def makepyfile(self, *args, **kwargs):
+ """Shortcut for .makefile() with a .py extension."""
+ return self._makefile('.py', args, kwargs)
+
+ def maketxtfile(self, *args, **kwargs):
+ """Shortcut for .makefile() with a .txt extension."""
+ return self._makefile('.txt', args, kwargs)
+
+ def syspathinsert(self, path=None):
+ """Prepend a directory to sys.path, defaults to :py:attr:`tmpdir`.
+
+ This is undone automatically after the test.
+ """
+ if path is None:
+ path = self.tmpdir
+ sys.path.insert(0, str(path))
+ # a call to syspathinsert() usually means that the caller
+ # wants to import some dynamically created files.
+ # with python3 we thus invalidate import caches.
+ self._possibly_invalidate_import_caches()
+
+ def _possibly_invalidate_import_caches(self):
+ # invalidate caches if we can (py33 and above)
+ try:
+ import importlib
+ except ImportError:
+ pass
+ else:
+ if hasattr(importlib, "invalidate_caches"):
+ importlib.invalidate_caches()
+
+ def mkdir(self, name):
+ """Create a new (sub)directory."""
+ return self.tmpdir.mkdir(name)
+
+ def mkpydir(self, name):
+ """Create a new python package.
+
+ This creates a (sub)directory with an empty ``__init__.py``
+ file so that is recognised as a python package.
+
+ """
+ p = self.mkdir(name)
+ p.ensure("__init__.py")
+ return p
+
+ Session = Session
+
+ def getnode(self, config, arg):
+ """Return the collection node of a file.
+
+ :param config: :py:class:`_pytest.config.Config` instance, see
+ :py:meth:`parseconfig` and :py:meth:`parseconfigure` to
+ create the configuration.
+
+ :param arg: A :py:class:`py.path.local` instance of the file.
+
+ """
+ session = Session(config)
+ assert '::' not in str(arg)
+ p = py.path.local(arg)
+ config.hook.pytest_sessionstart(session=session)
+ res = session.perform_collect([str(p)], genitems=False)[0]
+ config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK)
+ return res
+
+ def getpathnode(self, path):
+ """Return the collection node of a file.
+
+ This is like :py:meth:`getnode` but uses
+ :py:meth:`parseconfigure` to create the (configured) pytest
+ Config instance.
+
+ :param path: A :py:class:`py.path.local` instance of the file.
+
+ """
+ config = self.parseconfigure(path)
+ session = Session(config)
+ x = session.fspath.bestrelpath(path)
+ config.hook.pytest_sessionstart(session=session)
+ res = session.perform_collect([x], genitems=False)[0]
+ config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK)
+ return res
+
+ def genitems(self, colitems):
+ """Generate all test items from a collection node.
+
+ This recurses into the collection node and returns a list of
+ all the test items contained within.
+
+ """
+ session = colitems[0].session
+ result = []
+ for colitem in colitems:
+ result.extend(session.genitems(colitem))
+ return result
+
+ def runitem(self, source):
+ """Run the "test_func" Item.
+
+ The calling test instance (the class which contains the test
+ method) must provide a ``.getrunner()`` method which should
+ return a runner which can run the test protocol for a single
+ item, like e.g. :py:func:`_pytest.runner.runtestprotocol`.
+
+ """
+ # used from runner functional tests
+ item = self.getitem(source)
+ # the test class where we are called from wants to provide the runner
+ testclassinstance = self.request.instance
+ runner = testclassinstance.getrunner()
+ return runner(item)
+
+ def inline_runsource(self, source, *cmdlineargs):
+ """Run a test module in process using ``pytest.main()``.
+
+ This run writes "source" into a temporary file and runs
+ ``pytest.main()`` on it, returning a :py:class:`HookRecorder`
+ instance for the result.
+
+ :param source: The source code of the test module.
+
+ :param cmdlineargs: Any extra command line arguments to use.
+
+ :return: :py:class:`HookRecorder` instance of the result.
+
+ """
+ p = self.makepyfile(source)
+ values = list(cmdlineargs) + [p]
+ return self.inline_run(*values)
+
+ def inline_genitems(self, *args):
+ """Run ``pytest.main(['--collectonly'])`` in-process.
+
+ Returns a tuple of the collected items and a
+ :py:class:`HookRecorder` instance.
+
+ This runs the :py:func:`pytest.main` function to run all of
+ pytest inside the test process itself like
+ :py:meth:`inline_run`. However the return value is a tuple of
+ the collection items and a :py:class:`HookRecorder` instance.
+
+ """
+ rec = self.inline_run("--collect-only", *args)
+ items = [x.item for x in rec.getcalls("pytest_itemcollected")]
+ return items, rec
+
+ def inline_run(self, *args, **kwargs):
+ """Run ``pytest.main()`` in-process, returning a HookRecorder.
+
+ This runs the :py:func:`pytest.main` function to run all of
+ pytest inside the test process itself. This means it can
+ return a :py:class:`HookRecorder` instance which gives more
+ detailed results from then run then can be done by matching
+ stdout/stderr from :py:meth:`runpytest`.
+
+ :param args: Any command line arguments to pass to
+ :py:func:`pytest.main`.
+
+ :param plugin: (keyword-only) Extra plugin instances the
+ ``pytest.main()`` instance should use.
+
+ :return: A :py:class:`HookRecorder` instance.
+ """
+ # When running py.test inline any plugins active in the main
+ # test process are already imported. So this disables the
+ # warning which will trigger to say they can no longer be
+ # rewritten, which is fine as they are already rewritten.
+ orig_warn = AssertionRewritingHook._warn_already_imported
+
+ def revert():
+ AssertionRewritingHook._warn_already_imported = orig_warn
+
+ self.request.addfinalizer(revert)
+ AssertionRewritingHook._warn_already_imported = lambda *a: None
+
+ rec = []
+
+ class Collect:
+ def pytest_configure(x, config):
+ rec.append(self.make_hook_recorder(config.pluginmanager))
+
+ plugins = kwargs.get("plugins") or []
+ plugins.append(Collect())
+ ret = pytest.main(list(args), plugins=plugins)
+ self.delete_loaded_modules()
+ if len(rec) == 1:
+ reprec = rec.pop()
+ else:
+ class reprec:
+ pass
+ reprec.ret = ret
+
+ # typically we reraise keyboard interrupts from the child run
+ # because it's our user requesting interruption of the testing
+ if ret == 2 and not kwargs.get("no_reraise_ctrlc"):
+ calls = reprec.getcalls("pytest_keyboard_interrupt")
+ if calls and calls[-1].excinfo.type == KeyboardInterrupt:
+ raise KeyboardInterrupt()
+ return reprec
+
+ def runpytest_inprocess(self, *args, **kwargs):
+ """ Return result of running pytest in-process, providing a similar
+ interface to what self.runpytest() provides. """
+ if kwargs.get("syspathinsert"):
+ self.syspathinsert()
+ now = time.time()
+ capture = MultiCapture(Capture=SysCapture)
+ capture.start_capturing()
+ try:
+ try:
+ reprec = self.inline_run(*args, **kwargs)
+ except SystemExit as e:
+
+ class reprec:
+ ret = e.args[0]
+
+ except Exception:
+ traceback.print_exc()
+
+ class reprec:
+ ret = 3
+ finally:
+ out, err = capture.readouterr()
+ capture.stop_capturing()
+ sys.stdout.write(out)
+ sys.stderr.write(err)
+
+ res = RunResult(reprec.ret,
+ out.split("\n"), err.split("\n"),
+ time.time() - now)
+ res.reprec = reprec
+ return res
+
+ def runpytest(self, *args, **kwargs):
+ """ Run pytest inline or in a subprocess, depending on the command line
+ option "--runpytest" and return a :py:class:`RunResult`.
+
+ """
+ args = self._ensure_basetemp(args)
+ return self._runpytest_method(*args, **kwargs)
+
+ def _ensure_basetemp(self, args):
+ args = [str(x) for x in args]
+ for x in args:
+ if str(x).startswith('--basetemp'):
+ # print("basedtemp exists: %s" %(args,))
+ break
+ else:
+ args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
+ # print("added basetemp: %s" %(args,))
+ return args
+
+ def parseconfig(self, *args):
+ """Return a new pytest Config instance from given commandline args.
+
+ This invokes the pytest bootstrapping code in _pytest.config
+ to create a new :py:class:`_pytest.core.PluginManager` and
+ call the pytest_cmdline_parse hook to create new
+ :py:class:`_pytest.config.Config` instance.
+
+ If :py:attr:`plugins` has been populated they should be plugin
+ modules which will be registered with the PluginManager.
+
+ """
+ args = self._ensure_basetemp(args)
+
+ import _pytest.config
+ config = _pytest.config._prepareconfig(args, self.plugins)
+ # we don't know what the test will do with this half-setup config
+ # object and thus we make sure it gets unconfigured properly in any
+ # case (otherwise capturing could still be active, for example)
+ self.request.addfinalizer(config._ensure_unconfigure)
+ return config
+
+ def parseconfigure(self, *args):
+ """Return a new pytest configured Config instance.
+
+ This returns a new :py:class:`_pytest.config.Config` instance
+ like :py:meth:`parseconfig`, but also calls the
+ pytest_configure hook.
+
+ """
+ config = self.parseconfig(*args)
+ config._do_configure()
+ self.request.addfinalizer(config._ensure_unconfigure)
+ return config
+
+ def getitem(self, source, funcname="test_func"):
+ """Return the test item for a test function.
+
+ This writes the source to a python file and runs pytest's
+ collection on the resulting module, returning the test item
+ for the requested function name.
+
+ :param source: The module source.
+
+ :param funcname: The name of the test function for which the
+ Item must be returned.
+
+ """
+ items = self.getitems(source)
+ for item in items:
+ if item.name == funcname:
+ return item
+ assert 0, "%r item not found in module:\n%s\nitems: %s" % (
+ funcname, source, items)
+
+ def getitems(self, source):
+ """Return all test items collected from the module.
+
+ This writes the source to a python file and runs pytest's
+ collection on the resulting module, returning all test items
+ contained within.
+
+ """
+ modcol = self.getmodulecol(source)
+ return self.genitems([modcol])
+
+ def getmodulecol(self, source, configargs=(), withinit=False):
+ """Return the module collection node for ``source``.
+
+ This writes ``source`` to a file using :py:meth:`makepyfile`
+ and then runs the pytest collection on it, returning the
+ collection node for the test module.
+
+ :param source: The source code of the module to collect.
+
+ :param configargs: Any extra arguments to pass to
+ :py:meth:`parseconfigure`.
+
+ :param withinit: Whether to also write a ``__init__.py`` file
+ to the temporary directory to ensure it is a package.
+
+ """
+ kw = {self.request.function.__name__: Source(source).strip()}
+ path = self.makepyfile(**kw)
+ if withinit:
+ self.makepyfile(__init__="#")
+ self.config = config = self.parseconfigure(path, *configargs)
+ node = self.getnode(config, path)
+
+ return node
+
+ def collect_by_name(self, modcol, name):
+ """Return the collection node for name from the module collection.
+
+ This will search a module collection node for a collection
+ node matching the given name.
+
+ :param modcol: A module collection node, see
+ :py:meth:`getmodulecol`.
+
+ :param name: The name of the node to return.
+
+ """
+ if modcol not in self._mod_collections:
+ self._mod_collections[modcol] = list(modcol.collect())
+ for colitem in self._mod_collections[modcol]:
+ if colitem.name == name:
+ return colitem
+
+ def popen(self, cmdargs, stdout, stderr, **kw):
+ """Invoke subprocess.Popen.
+
+ This calls subprocess.Popen making sure the current working
+ directory is the PYTHONPATH.
+
+ You probably want to use :py:meth:`run` instead.
+
+ """
+ env = os.environ.copy()
+ env['PYTHONPATH'] = os.pathsep.join(filter(None, [
+ str(os.getcwd()), env.get('PYTHONPATH', '')]))
+ kw['env'] = env
+
+ popen = subprocess.Popen(cmdargs, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr, **kw)
+ popen.stdin.close()
+
+ return popen
+
+ def run(self, *cmdargs):
+ """Run a command with arguments.
+
+ Run a process using subprocess.Popen saving the stdout and
+ stderr.
+
+ Returns a :py:class:`RunResult`.
+
+ """
+ return self._run(*cmdargs)
+
+ def _run(self, *cmdargs):
+ cmdargs = [str(x) for x in cmdargs]
+ p1 = self.tmpdir.join("stdout")
+ p2 = self.tmpdir.join("stderr")
+ print("running:", ' '.join(cmdargs))
+ print(" in:", str(py.path.local()))
+ f1 = codecs.open(str(p1), "w", encoding="utf8")
+ f2 = codecs.open(str(p2), "w", encoding="utf8")
+ try:
+ now = time.time()
+ popen = self.popen(cmdargs, stdout=f1, stderr=f2,
+ close_fds=(sys.platform != "win32"))
+ ret = popen.wait()
+ finally:
+ f1.close()
+ f2.close()
+ f1 = codecs.open(str(p1), "r", encoding="utf8")
+ f2 = codecs.open(str(p2), "r", encoding="utf8")
+ try:
+ out = f1.read().splitlines()
+ err = f2.read().splitlines()
+ finally:
+ f1.close()
+ f2.close()
+ self._dump_lines(out, sys.stdout)
+ self._dump_lines(err, sys.stderr)
+ return RunResult(ret, out, err, time.time() - now)
+
+ def _dump_lines(self, lines, fp):
+ try:
+ for line in lines:
+ print(line, file=fp)
+ except UnicodeEncodeError:
+ print("couldn't print to %s because of encoding" % (fp,))
+
+ def _getpytestargs(self):
+ # we cannot use "(sys.executable,script)"
+ # because on windows the script is e.g. a pytest.exe
+ return (sys.executable, PYTEST_FULLPATH) # noqa
+
+ def runpython(self, script):
+ """Run a python script using sys.executable as interpreter.
+
+ Returns a :py:class:`RunResult`.
+ """
+ return self.run(sys.executable, script)
+
+ def runpython_c(self, command):
+ """Run python -c "command", return a :py:class:`RunResult`."""
+ return self.run(sys.executable, "-c", command)
+
+ def runpytest_subprocess(self, *args, **kwargs):
+ """Run pytest as a subprocess with given arguments.
+
+ Any plugins added to the :py:attr:`plugins` list will added
+ using the ``-p`` command line option. Addtionally
+ ``--basetemp`` is used put any temporary files and directories
+ in a numbered directory prefixed with "runpytest-" so they do
+ not conflict with the normal numberd pytest location for
+ temporary files and directories.
+
+ Returns a :py:class:`RunResult`.
+
+ """
+ p = py.path.local.make_numbered_dir(prefix="runpytest-",
+ keep=None, rootdir=self.tmpdir)
+ args = ('--basetemp=%s' % p, ) + args
+ # for x in args:
+ # if '--confcutdir' in str(x):
+ # break
+ # else:
+ # pass
+ # args = ('--confcutdir=.',) + args
+ plugins = [x for x in self.plugins if isinstance(x, str)]
+ if plugins:
+ args = ('-p', plugins[0]) + args
+ args = self._getpytestargs() + args
+ return self.run(*args)
+
+ def spawn_pytest(self, string, expect_timeout=10.0):
+ """Run pytest using pexpect.
+
+ This makes sure to use the right pytest and sets up the
+ temporary directory locations.
+
+ The pexpect child is returned.
+
+ """
+ basetemp = self.tmpdir.mkdir("temp-pexpect")
+ invoke = " ".join(map(str, self._getpytestargs()))
+ cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string)
+ return self.spawn(cmd, expect_timeout=expect_timeout)
+
+ def spawn(self, cmd, expect_timeout=10.0):
+ """Run a command using pexpect.
+
+ The pexpect child is returned.
+ """
+ pexpect = pytest.importorskip("pexpect", "3.0")
+ if hasattr(sys, 'pypy_version_info') and '64' in platform.machine():
+ pytest.skip("pypy-64 bit not supported")
+ if sys.platform.startswith("freebsd"):
+ pytest.xfail("pexpect does not work reliably on freebsd")
+ logfile = self.tmpdir.join("spawn.out").open("wb")
+ child = pexpect.spawn(cmd, logfile=logfile)
+ self.request.addfinalizer(logfile.close)
+ child.timeout = expect_timeout
+ return child
+
+
+def getdecoded(out):
+ try:
+ return out.decode("utf-8")
+ except UnicodeDecodeError:
+ return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % (
+ py.io.saferepr(out),)
+
+
+class LineComp:
+ def __init__(self):
+ self.stringio = py.io.TextIO()
+
+ def assert_contains_lines(self, lines2):
+ """ assert that lines2 are contained (linearly) in lines1.
+ return a list of extralines found.
+ """
+ __tracebackhide__ = True
+ val = self.stringio.getvalue()
+ self.stringio.truncate(0)
+ self.stringio.seek(0)
+ lines1 = val.split("\n")
+ return LineMatcher(lines1).fnmatch_lines(lines2)
+
+
+class LineMatcher:
+ """Flexible matching of text.
+
+ This is a convenience class to test large texts like the output of
+ commands.
+
+ The constructor takes a list of lines without their trailing
+ newlines, i.e. ``text.splitlines()``.
+
+ """
+
+ def __init__(self, lines):
+ self.lines = lines
+ self._log_output = []
+
+ def str(self):
+ """Return the entire original text."""
+ return "\n".join(self.lines)
+
+ def _getlines(self, lines2):
+ if isinstance(lines2, str):
+ lines2 = Source(lines2)
+ if isinstance(lines2, Source):
+ lines2 = lines2.strip().lines
+ return lines2
+
+ def fnmatch_lines_random(self, lines2):
+ """Check lines exist in the output using ``fnmatch.fnmatch``, in any order.
+
+ The argument is a list of lines which have to occur in the
+ output, in any order.
+ """
+ self._match_lines_random(lines2, fnmatch)
+
+ def re_match_lines_random(self, lines2):
+ """Check lines exist in the output using ``re.match``, in any order.
+
+ The argument is a list of lines which have to occur in the
+ output, in any order.
+
+ """
+ self._match_lines_random(lines2, lambda name, pat: re.match(pat, name))
+
+ def _match_lines_random(self, lines2, match_func):
+ """Check lines exist in the output.
+
+ The argument is a list of lines which have to occur in the
+ output, in any order. Each line can contain glob whildcards.
+
+ """
+ lines2 = self._getlines(lines2)
+ for line in lines2:
+ for x in self.lines:
+ if line == x or match_func(x, line):
+ self._log("matched: ", repr(line))
+ break
+ else:
+ self._log("line %r not found in output" % line)
+ raise ValueError(self._log_text)
+
+ def get_lines_after(self, fnline):
+ """Return all lines following the given line in the text.
+
+ The given line can contain glob wildcards.
+ """
+ for i, line in enumerate(self.lines):
+ if fnline == line or fnmatch(line, fnline):
+ return self.lines[i + 1:]
+ raise ValueError("line %r not found in output" % fnline)
+
+ def _log(self, *args):
+ self._log_output.append(' '.join((str(x) for x in args)))
+
+ @property
+ def _log_text(self):
+ return '\n'.join(self._log_output)
+
+ def fnmatch_lines(self, lines2):
+ """Search captured text for matching lines using ``fnmatch.fnmatch``.
+
+ The argument is a list of lines which have to match and can
+ use glob wildcards. If they do not match a pytest.fail() is
+ called. The matches and non-matches are also printed on
+ stdout.
+
+ """
+ self._match_lines(lines2, fnmatch, 'fnmatch')
+
+ def re_match_lines(self, lines2):
+ """Search captured text for matching lines using ``re.match``.
+
+ The argument is a list of lines which have to match using ``re.match``.
+ If they do not match a pytest.fail() is called.
+
+ The matches and non-matches are also printed on
+ stdout.
+ """
+ self._match_lines(lines2, lambda name, pat: re.match(pat, name), 're.match')
+
+ def _match_lines(self, lines2, match_func, match_nickname):
+ """Underlying implementation of ``fnmatch_lines`` and ``re_match_lines``.
+
+ :param list[str] lines2: list of string patterns to match. The actual format depends on
+ ``match_func``.
+ :param match_func: a callable ``match_func(line, pattern)`` where line is the captured
+ line from stdout/stderr and pattern is the matching pattern.
+
+ :param str match_nickname: the nickname for the match function that will be logged
+ to stdout when a match occurs.
+ """
+ lines2 = self._getlines(lines2)
+ lines1 = self.lines[:]
+ nextline = None
+ extralines = []
+ __tracebackhide__ = True
+ for line in lines2:
+ nomatchprinted = False
+ while lines1:
+ nextline = lines1.pop(0)
+ if line == nextline:
+ self._log("exact match:", repr(line))
+ break
+ elif match_func(nextline, line):
+ self._log("%s:" % match_nickname, repr(line))
+ self._log(" with:", repr(nextline))
+ break
+ else:
+ if not nomatchprinted:
+ self._log("nomatch:", repr(line))
+ nomatchprinted = True
+ self._log(" and:", repr(nextline))
+ extralines.append(nextline)
+ else:
+ self._log("remains unmatched: %r" % (line,))
+ pytest.fail(self._log_text)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/python.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/python.py
new file mode 100644
index 00000000000..650171a9e9c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/python.py
@@ -0,0 +1,1175 @@
+""" Python test discovery, setup and run of test functions. """
+from __future__ import absolute_import, division, print_function
+
+import fnmatch
+import inspect
+import sys
+import os
+import collections
+import warnings
+from textwrap import dedent
+from itertools import count
+
+
+import py
+import six
+from _pytest.mark import MarkerError
+from _pytest.config import hookimpl
+
+import _pytest
+import pluggy
+from _pytest import fixtures
+from _pytest import main
+from _pytest import deprecated
+from _pytest.compat import (
+ isclass, isfunction, is_generator, ascii_escaped,
+ REGEX_TYPE, STRING_TYPES, NoneType, NOTSET,
+ get_real_func, getfslineno, safe_getattr,
+ safe_str, getlocation, enum,
+)
+from _pytest.outcomes import fail
+from _pytest.mark import transfer_markers
+
+cutdir1 = py.path.local(pluggy.__file__.rstrip("oc"))
+cutdir2 = py.path.local(_pytest.__file__).dirpath()
+cutdir3 = py.path.local(py.__file__).dirpath()
+
+
+def filter_traceback(entry):
+ """Return True if a TracebackEntry instance should be removed from tracebacks:
+ * dynamically generated code (no code to show up for it);
+ * internal traceback from pytest or its internal libraries, py and pluggy.
+ """
+ # entry.path might sometimes return a str object when the entry
+ # points to dynamically generated code
+ # see https://bitbucket.org/pytest-dev/py/issues/71
+ raw_filename = entry.frame.code.raw.co_filename
+ is_generated = '<' in raw_filename and '>' in raw_filename
+ if is_generated:
+ return False
+ # entry.path might point to an inexisting file, in which case it will
+ # alsso return a str object. see #1133
+ p = py.path.local(entry.path)
+ return p != cutdir1 and not p.relto(cutdir2) and not p.relto(cutdir3)
+
+
+def pyobj_property(name):
+ def get(self):
+ node = self.getparent(getattr(__import__('pytest'), name))
+ if node is not None:
+ return node.obj
+ doc = "python %s object this node was collected from (can be None)." % (
+ name.lower(),)
+ return property(get, None, None, doc)
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("general")
+ group.addoption('--fixtures', '--funcargs',
+ action="store_true", dest="showfixtures", default=False,
+ help="show available fixtures, sorted by plugin appearance")
+ group.addoption(
+ '--fixtures-per-test',
+ action="store_true",
+ dest="show_fixtures_per_test",
+ default=False,
+ help="show fixtures per test",
+ )
+ parser.addini("usefixtures", type="args", default=[],
+ help="list of default fixtures to be used with this project")
+ parser.addini("python_files", type="args",
+ default=['test_*.py', '*_test.py'],
+ help="glob-style file patterns for Python test module discovery")
+ parser.addini("python_classes", type="args", default=["Test", ],
+ help="prefixes or glob names for Python test class discovery")
+ parser.addini("python_functions", type="args", default=["test", ],
+ help="prefixes or glob names for Python test function and "
+ "method discovery")
+
+ group.addoption("--import-mode", default="prepend",
+ choices=["prepend", "append"], dest="importmode",
+ help="prepend/append to sys.path when importing test modules, "
+ "default is to prepend.")
+
+
+def pytest_cmdline_main(config):
+ if config.option.showfixtures:
+ showfixtures(config)
+ return 0
+ if config.option.show_fixtures_per_test:
+ show_fixtures_per_test(config)
+ return 0
+
+
+def pytest_generate_tests(metafunc):
+ # those alternative spellings are common - raise a specific error to alert
+ # the user
+ alt_spellings = ['parameterize', 'parametrise', 'parameterise']
+ for attr in alt_spellings:
+ if hasattr(metafunc.function, attr):
+ msg = "{0} has '{1}', spelling should be 'parametrize'"
+ raise MarkerError(msg.format(metafunc.function.__name__, attr))
+ try:
+ markers = metafunc.function.parametrize
+ except AttributeError:
+ return
+ for marker in markers:
+ metafunc.parametrize(*marker.args, **marker.kwargs)
+
+
+def pytest_configure(config):
+ config.addinivalue_line("markers",
+ "parametrize(argnames, argvalues): call a test function multiple "
+ "times passing in different arguments in turn. argvalues generally "
+ "needs to be a list of values if argnames specifies only one name "
+ "or a list of tuples of values if argnames specifies multiple names. "
+ "Example: @parametrize('arg1', [1,2]) would lead to two calls of the "
+ "decorated test function, one with arg1=1 and another with arg1=2."
+ "see http://pytest.org/latest/parametrize.html for more info and "
+ "examples."
+ )
+ config.addinivalue_line("markers",
+ "usefixtures(fixturename1, fixturename2, ...): mark tests as needing "
+ "all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures "
+ )
+
+
+@hookimpl(trylast=True)
+def pytest_pyfunc_call(pyfuncitem):
+ testfunction = pyfuncitem.obj
+ if pyfuncitem._isyieldedfunction():
+ testfunction(*pyfuncitem._args)
+ else:
+ funcargs = pyfuncitem.funcargs
+ testargs = {}
+ for arg in pyfuncitem._fixtureinfo.argnames:
+ testargs[arg] = funcargs[arg]
+ testfunction(**testargs)
+ return True
+
+
+def pytest_collect_file(path, parent):
+ ext = path.ext
+ if ext == ".py":
+ if not parent.session.isinitpath(path):
+ for pat in parent.config.getini('python_files'):
+ if path.fnmatch(pat):
+ break
+ else:
+ return
+ ihook = parent.session.gethookproxy(path)
+ return ihook.pytest_pycollect_makemodule(path=path, parent=parent)
+
+
+def pytest_pycollect_makemodule(path, parent):
+ return Module(path, parent)
+
+
+@hookimpl(hookwrapper=True)
+def pytest_pycollect_makeitem(collector, name, obj):
+ outcome = yield
+ res = outcome.get_result()
+ if res is not None:
+ return
+ # nothing was collected elsewhere, let's do it here
+ if isclass(obj):
+ if collector.istestclass(obj, name):
+ Class = collector._getcustomclass("Class")
+ outcome.force_result(Class(name, parent=collector))
+ elif collector.istestfunction(obj, name):
+ # mock seems to store unbound methods (issue473), normalize it
+ obj = getattr(obj, "__func__", obj)
+ # We need to try and unwrap the function if it's a functools.partial
+ # or a funtools.wrapped.
+ # We musn't if it's been wrapped with mock.patch (python 2 only)
+ if not (isfunction(obj) or isfunction(get_real_func(obj))):
+ collector.warn(code="C2", message="cannot collect %r because it is not a function."
+ % name, )
+ elif getattr(obj, "__test__", True):
+ if is_generator(obj):
+ res = Generator(name, parent=collector)
+ else:
+ res = list(collector._genfunctions(name, obj))
+ outcome.force_result(res)
+
+
+def pytest_make_parametrize_id(config, val, argname=None):
+ return None
+
+
+class PyobjContext(object):
+ module = pyobj_property("Module")
+ cls = pyobj_property("Class")
+ instance = pyobj_property("Instance")
+
+
+class PyobjMixin(PyobjContext):
+ def obj():
+ def fget(self):
+ obj = getattr(self, '_obj', None)
+ if obj is None:
+ self._obj = obj = self._getobj()
+ return obj
+
+ def fset(self, value):
+ self._obj = value
+
+ return property(fget, fset, None, "underlying python object")
+
+ obj = obj()
+
+ def _getobj(self):
+ return getattr(self.parent.obj, self.name)
+
+ def getmodpath(self, stopatmodule=True, includemodule=False):
+ """ return python path relative to the containing module. """
+ chain = self.listchain()
+ chain.reverse()
+ parts = []
+ for node in chain:
+ if isinstance(node, Instance):
+ continue
+ name = node.name
+ if isinstance(node, Module):
+ name = os.path.splitext(name)[0]
+ if stopatmodule:
+ if includemodule:
+ parts.append(name)
+ break
+ parts.append(name)
+ parts.reverse()
+ s = ".".join(parts)
+ return s.replace(".[", "[")
+
+ def _getfslineno(self):
+ return getfslineno(self.obj)
+
+ def reportinfo(self):
+ # XXX caching?
+ obj = self.obj
+ compat_co_firstlineno = getattr(obj, 'compat_co_firstlineno', None)
+ if isinstance(compat_co_firstlineno, int):
+ # nose compatibility
+ fspath = sys.modules[obj.__module__].__file__
+ if fspath.endswith(".pyc"):
+ fspath = fspath[:-1]
+ lineno = compat_co_firstlineno
+ else:
+ fspath, lineno = getfslineno(obj)
+ modpath = self.getmodpath()
+ assert isinstance(lineno, int)
+ return fspath, lineno, modpath
+
+
+class PyCollector(PyobjMixin, main.Collector):
+
+ def funcnamefilter(self, name):
+ return self._matches_prefix_or_glob_option('python_functions', name)
+
+ def isnosetest(self, obj):
+ """ Look for the __test__ attribute, which is applied by the
+ @nose.tools.istest decorator
+ """
+ # We explicitly check for "is True" here to not mistakenly treat
+ # classes with a custom __getattr__ returning something truthy (like a
+ # function) as test classes.
+ return safe_getattr(obj, '__test__', False) is True
+
+ def classnamefilter(self, name):
+ return self._matches_prefix_or_glob_option('python_classes', name)
+
+ def istestfunction(self, obj, name):
+ if self.funcnamefilter(name) or self.isnosetest(obj):
+ if isinstance(obj, staticmethod):
+ # static methods need to be unwrapped
+ obj = safe_getattr(obj, '__func__', False)
+ if obj is False:
+ # Python 2.6 wraps in a different way that we won't try to handle
+ msg = "cannot collect static method %r because " \
+ "it is not a function (always the case in Python 2.6)"
+ self.warn(
+ code="C2", message=msg % name)
+ return False
+ return (
+ safe_getattr(obj, "__call__", False) and fixtures.getfixturemarker(obj) is None
+ )
+ else:
+ return False
+
+ def istestclass(self, obj, name):
+ return self.classnamefilter(name) or self.isnosetest(obj)
+
+ def _matches_prefix_or_glob_option(self, option_name, name):
+ """
+ checks if the given name matches the prefix or glob-pattern defined
+ in ini configuration.
+ """
+ for option in self.config.getini(option_name):
+ if name.startswith(option):
+ return True
+ # check that name looks like a glob-string before calling fnmatch
+ # because this is called for every name in each collected module,
+ # and fnmatch is somewhat expensive to call
+ elif ('*' in option or '?' in option or '[' in option) and \
+ fnmatch.fnmatch(name, option):
+ return True
+ return False
+
+ def collect(self):
+ if not getattr(self.obj, "__test__", True):
+ return []
+
+ # NB. we avoid random getattrs and peek in the __dict__ instead
+ # (XXX originally introduced from a PyPy need, still true?)
+ dicts = [getattr(self.obj, '__dict__', {})]
+ for basecls in inspect.getmro(self.obj.__class__):
+ dicts.append(basecls.__dict__)
+ seen = {}
+ values = []
+ for dic in dicts:
+ for name, obj in list(dic.items()):
+ if name in seen:
+ continue
+ seen[name] = True
+ res = self._makeitem(name, obj)
+ if res is None:
+ continue
+ if not isinstance(res, list):
+ res = [res]
+ values.extend(res)
+ values.sort(key=lambda item: item.reportinfo()[:2])
+ return values
+
+ def makeitem(self, name, obj):
+ warnings.warn(deprecated.COLLECTOR_MAKEITEM, stacklevel=2)
+ self._makeitem(name, obj)
+
+ def _makeitem(self, name, obj):
+ # assert self.ihook.fspath == self.fspath, self
+ return self.ihook.pytest_pycollect_makeitem(
+ collector=self, name=name, obj=obj)
+
+ def _genfunctions(self, name, funcobj):
+ module = self.getparent(Module).obj
+ clscol = self.getparent(Class)
+ cls = clscol and clscol.obj or None
+ transfer_markers(funcobj, cls, module)
+ fm = self.session._fixturemanager
+ fixtureinfo = fm.getfixtureinfo(self, funcobj, cls)
+ metafunc = Metafunc(funcobj, fixtureinfo, self.config,
+ cls=cls, module=module)
+ methods = []
+ if hasattr(module, "pytest_generate_tests"):
+ methods.append(module.pytest_generate_tests)
+ if hasattr(cls, "pytest_generate_tests"):
+ methods.append(cls().pytest_generate_tests)
+ if methods:
+ self.ihook.pytest_generate_tests.call_extra(methods,
+ dict(metafunc=metafunc))
+ else:
+ self.ihook.pytest_generate_tests(metafunc=metafunc)
+
+ Function = self._getcustomclass("Function")
+ if not metafunc._calls:
+ yield Function(name, parent=self, fixtureinfo=fixtureinfo)
+ else:
+ # add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs
+ fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm)
+
+ for callspec in metafunc._calls:
+ subname = "%s[%s]" % (name, callspec.id)
+ yield Function(name=subname, parent=self,
+ callspec=callspec, callobj=funcobj,
+ fixtureinfo=fixtureinfo,
+ keywords={callspec.id: True},
+ originalname=name,
+ )
+
+
+class Module(main.File, PyCollector):
+ """ Collector for test classes and functions. """
+
+ def _getobj(self):
+ return self._importtestmodule()
+
+ def collect(self):
+ self.session._fixturemanager.parsefactories(self)
+ return super(Module, self).collect()
+
+ def _importtestmodule(self):
+ # we assume we are only called once per module
+ importmode = self.config.getoption("--import-mode")
+ try:
+ mod = self.fspath.pyimport(ensuresyspath=importmode)
+ except SyntaxError:
+ raise self.CollectError(
+ _pytest._code.ExceptionInfo().getrepr(style="short"))
+ except self.fspath.ImportMismatchError:
+ e = sys.exc_info()[1]
+ raise self.CollectError(
+ "import file mismatch:\n"
+ "imported module %r has this __file__ attribute:\n"
+ " %s\n"
+ "which is not the same as the test file we want to collect:\n"
+ " %s\n"
+ "HINT: remove __pycache__ / .pyc files and/or use a "
+ "unique basename for your test file modules"
+ % e.args
+ )
+ except ImportError:
+ from _pytest._code.code import ExceptionInfo
+ exc_info = ExceptionInfo()
+ if self.config.getoption('verbose') < 2:
+ exc_info.traceback = exc_info.traceback.filter(filter_traceback)
+ exc_repr = exc_info.getrepr(style='short') if exc_info.traceback else exc_info.exconly()
+ formatted_tb = safe_str(exc_repr)
+ raise self.CollectError(
+ "ImportError while importing test module '{fspath}'.\n"
+ "Hint: make sure your test modules/packages have valid Python names.\n"
+ "Traceback:\n"
+ "{traceback}".format(fspath=self.fspath, traceback=formatted_tb)
+ )
+ except _pytest.runner.Skipped as e:
+ if e.allow_module_level:
+ raise
+ raise self.CollectError(
+ "Using pytest.skip outside of a test is not allowed. "
+ "To decorate a test function, use the @pytest.mark.skip "
+ "or @pytest.mark.skipif decorators instead, and to skip a "
+ "module use `pytestmark = pytest.mark.{skip,skipif}."
+ )
+ self.config.pluginmanager.consider_module(mod)
+ return mod
+
+ def setup(self):
+ setup_module = _get_xunit_setup_teardown(self.obj, "setUpModule")
+ if setup_module is None:
+ setup_module = _get_xunit_setup_teardown(self.obj, "setup_module")
+ if setup_module is not None:
+ setup_module()
+
+ teardown_module = _get_xunit_setup_teardown(self.obj, 'tearDownModule')
+ if teardown_module is None:
+ teardown_module = _get_xunit_setup_teardown(self.obj, 'teardown_module')
+ if teardown_module is not None:
+ self.addfinalizer(teardown_module)
+
+
+def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):
+ """
+ Return a callable to perform xunit-style setup or teardown if
+ the function exists in the ``holder`` object.
+ The ``param_obj`` parameter is the parameter which will be passed to the function
+ when the callable is called without arguments, defaults to the ``holder`` object.
+ Return ``None`` if a suitable callable is not found.
+ """
+ param_obj = param_obj if param_obj is not None else holder
+ result = _get_xunit_func(holder, attr_name)
+ if result is not None:
+ arg_count = result.__code__.co_argcount
+ if inspect.ismethod(result):
+ arg_count -= 1
+ if arg_count:
+ return lambda: result(param_obj)
+ else:
+ return result
+
+
+def _get_xunit_func(obj, name):
+ """Return the attribute from the given object to be used as a setup/teardown
+ xunit-style function, but only if not marked as a fixture to
+ avoid calling it twice.
+ """
+ meth = getattr(obj, name, None)
+ if fixtures.getfixturemarker(meth) is None:
+ return meth
+
+
+class Class(PyCollector):
+ """ Collector for test methods. """
+
+ def collect(self):
+ if not safe_getattr(self.obj, "__test__", True):
+ return []
+ if hasinit(self.obj):
+ self.warn("C1", "cannot collect test class %r because it has a "
+ "__init__ constructor" % self.obj.__name__)
+ return []
+ elif hasnew(self.obj):
+ self.warn("C1", "cannot collect test class %r because it has a "
+ "__new__ constructor" % self.obj.__name__)
+ return []
+ return [self._getcustomclass("Instance")(name="()", parent=self)]
+
+ def setup(self):
+ setup_class = _get_xunit_func(self.obj, 'setup_class')
+ if setup_class is not None:
+ setup_class = getattr(setup_class, 'im_func', setup_class)
+ setup_class = getattr(setup_class, '__func__', setup_class)
+ setup_class(self.obj)
+
+ fin_class = getattr(self.obj, 'teardown_class', None)
+ if fin_class is not None:
+ fin_class = getattr(fin_class, 'im_func', fin_class)
+ fin_class = getattr(fin_class, '__func__', fin_class)
+ self.addfinalizer(lambda: fin_class(self.obj))
+
+
+class Instance(PyCollector):
+ def _getobj(self):
+ return self.parent.obj()
+
+ def collect(self):
+ self.session._fixturemanager.parsefactories(self)
+ return super(Instance, self).collect()
+
+ def newinstance(self):
+ self.obj = self._getobj()
+ return self.obj
+
+
+class FunctionMixin(PyobjMixin):
+ """ mixin for the code common to Function and Generator.
+ """
+
+ def setup(self):
+ """ perform setup for this test function. """
+ if hasattr(self, '_preservedparent'):
+ obj = self._preservedparent
+ elif isinstance(self.parent, Instance):
+ obj = self.parent.newinstance()
+ self.obj = self._getobj()
+ else:
+ obj = self.parent.obj
+ if inspect.ismethod(self.obj):
+ setup_name = 'setup_method'
+ teardown_name = 'teardown_method'
+ else:
+ setup_name = 'setup_function'
+ teardown_name = 'teardown_function'
+ setup_func_or_method = _get_xunit_setup_teardown(obj, setup_name, param_obj=self.obj)
+ if setup_func_or_method is not None:
+ setup_func_or_method()
+ teardown_func_or_method = _get_xunit_setup_teardown(obj, teardown_name, param_obj=self.obj)
+ if teardown_func_or_method is not None:
+ self.addfinalizer(teardown_func_or_method)
+
+ def _prunetraceback(self, excinfo):
+ if hasattr(self, '_obj') and not self.config.option.fulltrace:
+ code = _pytest._code.Code(get_real_func(self.obj))
+ path, firstlineno = code.path, code.firstlineno
+ traceback = excinfo.traceback
+ ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
+ if ntraceback == traceback:
+ ntraceback = ntraceback.cut(path=path)
+ if ntraceback == traceback:
+ # ntraceback = ntraceback.cut(excludepath=cutdir2)
+ ntraceback = ntraceback.filter(filter_traceback)
+ if not ntraceback:
+ ntraceback = traceback
+
+ excinfo.traceback = ntraceback.filter()
+ # issue364: mark all but first and last frames to
+ # only show a single-line message for each frame
+ if self.config.option.tbstyle == "auto":
+ if len(excinfo.traceback) > 2:
+ for entry in excinfo.traceback[1:-1]:
+ entry.set_repr_style('short')
+
+ def _repr_failure_py(self, excinfo, style="long"):
+ if excinfo.errisinstance(fail.Exception):
+ if not excinfo.value.pytrace:
+ return py._builtin._totext(excinfo.value)
+ return super(FunctionMixin, self)._repr_failure_py(excinfo,
+ style=style)
+
+ def repr_failure(self, excinfo, outerr=None):
+ assert outerr is None, "XXX outerr usage is deprecated"
+ style = self.config.option.tbstyle
+ if style == "auto":
+ style = "long"
+ return self._repr_failure_py(excinfo, style=style)
+
+
+class Generator(FunctionMixin, PyCollector):
+ def collect(self):
+ # test generators are seen as collectors but they also
+ # invoke setup/teardown on popular request
+ # (induced by the common "test_*" naming shared with normal tests)
+ from _pytest import deprecated
+ self.session._setupstate.prepare(self)
+ # see FunctionMixin.setup and test_setupstate_is_preserved_134
+ self._preservedparent = self.parent.obj
+ values = []
+ seen = {}
+ for i, x in enumerate(self.obj()):
+ name, call, args = self.getcallargs(x)
+ if not callable(call):
+ raise TypeError("%r yielded non callable test %r" % (self.obj, call,))
+ if name is None:
+ name = "[%d]" % i
+ else:
+ name = "['%s']" % name
+ if name in seen:
+ raise ValueError("%r generated tests with non-unique name %r" % (self, name))
+ seen[name] = True
+ values.append(self.Function(name, self, args=args, callobj=call))
+ self.warn('C1', deprecated.YIELD_TESTS)
+ return values
+
+ def getcallargs(self, obj):
+ if not isinstance(obj, (tuple, list)):
+ obj = (obj,)
+ # explicit naming
+ if isinstance(obj[0], six.string_types):
+ name = obj[0]
+ obj = obj[1:]
+ else:
+ name = None
+ call, args = obj[0], obj[1:]
+ return name, call, args
+
+
+def hasinit(obj):
+ init = getattr(obj, '__init__', None)
+ if init:
+ return init != object.__init__
+
+
+def hasnew(obj):
+ new = getattr(obj, '__new__', None)
+ if new:
+ return new != object.__new__
+
+
+class CallSpec2(object):
+ def __init__(self, metafunc):
+ self.metafunc = metafunc
+ self.funcargs = {}
+ self._idlist = []
+ self.params = {}
+ self._globalid = NOTSET
+ self._globalid_args = set()
+ self._globalparam = NOTSET
+ self._arg2scopenum = {} # used for sorting parametrized resources
+ self.marks = []
+ self.indices = {}
+
+ def copy(self, metafunc):
+ cs = CallSpec2(self.metafunc)
+ cs.funcargs.update(self.funcargs)
+ cs.params.update(self.params)
+ cs.marks.extend(self.marks)
+ cs.indices.update(self.indices)
+ cs._arg2scopenum.update(self._arg2scopenum)
+ cs._idlist = list(self._idlist)
+ cs._globalid = self._globalid
+ cs._globalid_args = self._globalid_args
+ cs._globalparam = self._globalparam
+ return cs
+
+ def _checkargnotcontained(self, arg):
+ if arg in self.params or arg in self.funcargs:
+ raise ValueError("duplicate %r" % (arg,))
+
+ def getparam(self, name):
+ try:
+ return self.params[name]
+ except KeyError:
+ if self._globalparam is NOTSET:
+ raise ValueError(name)
+ return self._globalparam
+
+ @property
+ def id(self):
+ return "-".join(map(str, filter(None, self._idlist)))
+
+ def setmulti2(self, valtypes, argnames, valset, id, marks, scopenum,
+ param_index):
+ for arg, val in zip(argnames, valset):
+ self._checkargnotcontained(arg)
+ valtype_for_arg = valtypes[arg]
+ getattr(self, valtype_for_arg)[arg] = val
+ self.indices[arg] = param_index
+ self._arg2scopenum[arg] = scopenum
+ self._idlist.append(id)
+ self.marks.extend(marks)
+
+ def setall(self, funcargs, id, param):
+ for x in funcargs:
+ self._checkargnotcontained(x)
+ self.funcargs.update(funcargs)
+ if id is not NOTSET:
+ self._idlist.append(id)
+ if param is not NOTSET:
+ assert self._globalparam is NOTSET
+ self._globalparam = param
+ for arg in funcargs:
+ self._arg2scopenum[arg] = fixtures.scopenum_function
+
+
+class Metafunc(fixtures.FuncargnamesCompatAttr):
+ """
+ Metafunc objects are passed to the ``pytest_generate_tests`` hook.
+ They help to inspect a test function and to generate tests according to
+ test configuration or values specified in the class or module where a
+ test function is defined.
+ """
+
+ def __init__(self, function, fixtureinfo, config, cls=None, module=None):
+ #: access to the :class:`_pytest.config.Config` object for the test session
+ self.config = config
+
+ #: the module object where the test function is defined in.
+ self.module = module
+
+ #: underlying python test function
+ self.function = function
+
+ #: set of fixture names required by the test function
+ self.fixturenames = fixtureinfo.names_closure
+
+ #: class object where the test function is defined in or ``None``.
+ self.cls = cls
+
+ self._calls = []
+ self._ids = set()
+ self._arg2fixturedefs = fixtureinfo.name2fixturedefs
+
+ def parametrize(self, argnames, argvalues, indirect=False, ids=None,
+ scope=None):
+ """ Add new invocations to the underlying test function using the list
+ of argvalues for the given argnames. Parametrization is performed
+ during the collection phase. If you need to setup expensive resources
+ see about setting indirect to do it rather at test setup time.
+
+ :arg argnames: a comma-separated string denoting one or more argument
+ names, or a list/tuple of argument strings.
+
+ :arg argvalues: The list of argvalues determines how often a
+ test is invoked with different argument values. If only one
+ argname was specified argvalues is a list of values. If N
+ argnames were specified, argvalues must be a list of N-tuples,
+ where each tuple-element specifies a value for its respective
+ argname.
+
+ :arg indirect: The list of argnames or boolean. A list of arguments'
+ names (subset of argnames). If True the list contains all names from
+ the argnames. Each argvalue corresponding to an argname in this list will
+ be passed as request.param to its respective argname fixture
+ function so that it can perform more expensive setups during the
+ setup phase of a test rather than at collection time.
+
+ :arg ids: list of string ids, or a callable.
+ If strings, each is corresponding to the argvalues so that they are
+ part of the test id. If None is given as id of specific test, the
+ automatically generated id for that argument will be used.
+ If callable, it should take one argument (a single argvalue) and return
+ a string or return None. If None, the automatically generated id for that
+ argument will be used.
+ If no ids are provided they will be generated automatically from
+ the argvalues.
+
+ :arg scope: if specified it denotes the scope of the parameters.
+ The scope is used for grouping tests by parameter instances.
+ It will also override any fixture-function defined scope, allowing
+ to set a dynamic scope using test context or configuration.
+ """
+ from _pytest.fixtures import scope2index
+ from _pytest.mark import ParameterSet
+ from py.io import saferepr
+ argnames, parameters = ParameterSet._for_parameterize(
+ argnames, argvalues, self.function)
+ del argvalues
+
+ if scope is None:
+ scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)
+
+ scopenum = scope2index(scope, descr='call to {0}'.format(self.parametrize))
+ valtypes = {}
+ for arg in argnames:
+ if arg not in self.fixturenames:
+ if isinstance(indirect, (tuple, list)):
+ name = 'fixture' if arg in indirect else 'argument'
+ else:
+ name = 'fixture' if indirect else 'argument'
+ raise ValueError(
+ "%r uses no %s %r" % (
+ self.function, name, arg))
+
+ if indirect is True:
+ valtypes = dict.fromkeys(argnames, "params")
+ elif indirect is False:
+ valtypes = dict.fromkeys(argnames, "funcargs")
+ elif isinstance(indirect, (tuple, list)):
+ valtypes = dict.fromkeys(argnames, "funcargs")
+ for arg in indirect:
+ if arg not in argnames:
+ raise ValueError("indirect given to %r: fixture %r doesn't exist" % (
+ self.function, arg))
+ valtypes[arg] = "params"
+ idfn = None
+ if callable(ids):
+ idfn = ids
+ ids = None
+ if ids:
+ if len(ids) != len(parameters):
+ raise ValueError('%d tests specified with %d ids' % (
+ len(parameters), len(ids)))
+ for id_value in ids:
+ if id_value is not None and not isinstance(id_value, six.string_types):
+ msg = 'ids must be list of strings, found: %s (type: %s)'
+ raise ValueError(msg % (saferepr(id_value), type(id_value).__name__))
+ ids = idmaker(argnames, parameters, idfn, ids, self.config)
+ newcalls = []
+ for callspec in self._calls or [CallSpec2(self)]:
+ elements = zip(ids, parameters, count())
+ for a_id, param, param_index in elements:
+ if len(param.values) != len(argnames):
+ raise ValueError(
+ 'In "parametrize" the number of values ({0}) must be '
+ 'equal to the number of names ({1})'.format(
+ param.values, argnames))
+ newcallspec = callspec.copy(self)
+ newcallspec.setmulti2(valtypes, argnames, param.values, a_id,
+ param.marks, scopenum, param_index)
+ newcalls.append(newcallspec)
+ self._calls = newcalls
+
+ def addcall(self, funcargs=None, id=NOTSET, param=NOTSET):
+ """ Add a new call to the underlying test function during the collection phase of a test run.
+
+ .. deprecated:: 3.3
+
+ Use :meth:`parametrize` instead.
+
+ Note that request.addcall() is called during the test collection phase prior and
+ independently to actual test execution. You should only use addcall()
+ if you need to specify multiple arguments of a test function.
+
+ :arg funcargs: argument keyword dictionary used when invoking
+ the test function.
+
+ :arg id: used for reporting and identification purposes. If you
+ don't supply an `id` an automatic unique id will be generated.
+
+ :arg param: a parameter which will be exposed to a later fixture function
+ invocation through the ``request.param`` attribute.
+ """
+ if self.config:
+ self.config.warn('C1', message=deprecated.METAFUNC_ADD_CALL, fslocation=None)
+ assert funcargs is None or isinstance(funcargs, dict)
+ if funcargs is not None:
+ for name in funcargs:
+ if name not in self.fixturenames:
+ fail("funcarg %r not used in this function." % name)
+ else:
+ funcargs = {}
+ if id is None:
+ raise ValueError("id=None not allowed")
+ if id is NOTSET:
+ id = len(self._calls)
+ id = str(id)
+ if id in self._ids:
+ raise ValueError("duplicate id %r" % id)
+ self._ids.add(id)
+
+ cs = CallSpec2(self)
+ cs.setall(funcargs, id, param)
+ self._calls.append(cs)
+
+
+def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
+ """Find the most appropriate scope for a parametrized call based on its arguments.
+
+ When there's at least one direct argument, always use "function" scope.
+
+ When a test function is parametrized and all its arguments are indirect
+ (e.g. fixtures), return the most narrow scope based on the fixtures used.
+
+ Related to issue #1832, based on code posted by @Kingdread.
+ """
+ from _pytest.fixtures import scopes
+ indirect_as_list = isinstance(indirect, (list, tuple))
+ all_arguments_are_fixtures = indirect is True or \
+ indirect_as_list and len(indirect) == argnames
+ if all_arguments_are_fixtures:
+ fixturedefs = arg2fixturedefs or {}
+ used_scopes = [fixturedef[0].scope for name, fixturedef in fixturedefs.items()]
+ if used_scopes:
+ # Takes the most narrow scope from used fixtures
+ for scope in reversed(scopes):
+ if scope in used_scopes:
+ return scope
+
+ return 'function'
+
+
+def _idval(val, argname, idx, idfn, config=None):
+ if idfn:
+ s = None
+ try:
+ s = idfn(val)
+ except Exception:
+ # See issue https://github.com/pytest-dev/pytest/issues/2169
+ import warnings
+ msg = "Raised while trying to determine id of parameter %s at position %d." % (argname, idx)
+ msg += '\nUpdate your code as this will raise an error in pytest-4.0.'
+ warnings.warn(msg, DeprecationWarning)
+ if s:
+ return ascii_escaped(s)
+
+ if config:
+ hook_id = config.hook.pytest_make_parametrize_id(
+ config=config, val=val, argname=argname)
+ if hook_id:
+ return hook_id
+
+ if isinstance(val, STRING_TYPES):
+ return ascii_escaped(val)
+ elif isinstance(val, (float, int, bool, NoneType)):
+ return str(val)
+ elif isinstance(val, REGEX_TYPE):
+ return ascii_escaped(val.pattern)
+ elif enum is not None and isinstance(val, enum.Enum):
+ return str(val)
+ elif isclass(val) and hasattr(val, '__name__'):
+ return val.__name__
+ return str(argname) + str(idx)
+
+
+def _idvalset(idx, parameterset, argnames, idfn, ids, config=None):
+ if parameterset.id is not None:
+ return parameterset.id
+ if ids is None or (idx >= len(ids) or ids[idx] is None):
+ this_id = [_idval(val, argname, idx, idfn, config)
+ for val, argname in zip(parameterset.values, argnames)]
+ return "-".join(this_id)
+ else:
+ return ascii_escaped(ids[idx])
+
+
+def idmaker(argnames, parametersets, idfn=None, ids=None, config=None):
+ ids = [_idvalset(valindex, parameterset, argnames, idfn, ids, config)
+ for valindex, parameterset in enumerate(parametersets)]
+ if len(set(ids)) != len(ids):
+ # The ids are not unique
+ duplicates = [testid for testid in ids if ids.count(testid) > 1]
+ counters = collections.defaultdict(lambda: 0)
+ for index, testid in enumerate(ids):
+ if testid in duplicates:
+ ids[index] = testid + str(counters[testid])
+ counters[testid] += 1
+ return ids
+
+
+def show_fixtures_per_test(config):
+ from _pytest.main import wrap_session
+ return wrap_session(config, _show_fixtures_per_test)
+
+
+def _show_fixtures_per_test(config, session):
+ import _pytest.config
+ session.perform_collect()
+ curdir = py.path.local()
+ tw = _pytest.config.create_terminal_writer(config)
+ verbose = config.getvalue("verbose")
+
+ def get_best_relpath(func):
+ loc = getlocation(func, curdir)
+ return curdir.bestrelpath(loc)
+
+ def write_fixture(fixture_def):
+ argname = fixture_def.argname
+ if verbose <= 0 and argname.startswith("_"):
+ return
+ if verbose > 0:
+ bestrel = get_best_relpath(fixture_def.func)
+ funcargspec = "{0} -- {1}".format(argname, bestrel)
+ else:
+ funcargspec = argname
+ tw.line(funcargspec, green=True)
+ fixture_doc = fixture_def.func.__doc__
+ if fixture_doc:
+ write_docstring(tw, fixture_doc)
+ else:
+ tw.line(' no docstring available', red=True)
+
+ def write_item(item):
+ try:
+ info = item._fixtureinfo
+ except AttributeError:
+ # doctests items have no _fixtureinfo attribute
+ return
+ if not info.name2fixturedefs:
+ # this test item does not use any fixtures
+ return
+ tw.line()
+ tw.sep('-', 'fixtures used by {0}'.format(item.name))
+ tw.sep('-', '({0})'.format(get_best_relpath(item.function)))
+ # dict key not used in loop but needed for sorting
+ for _, fixturedefs in sorted(info.name2fixturedefs.items()):
+ assert fixturedefs is not None
+ if not fixturedefs:
+ continue
+ # last item is expected to be the one used by the test item
+ write_fixture(fixturedefs[-1])
+
+ for session_item in session.items:
+ write_item(session_item)
+
+
+def showfixtures(config):
+ from _pytest.main import wrap_session
+ return wrap_session(config, _showfixtures_main)
+
+
+def _showfixtures_main(config, session):
+ import _pytest.config
+ session.perform_collect()
+ curdir = py.path.local()
+ tw = _pytest.config.create_terminal_writer(config)
+ verbose = config.getvalue("verbose")
+
+ fm = session._fixturemanager
+
+ available = []
+ seen = set()
+
+ for argname, fixturedefs in fm._arg2fixturedefs.items():
+ assert fixturedefs is not None
+ if not fixturedefs:
+ continue
+ for fixturedef in fixturedefs:
+ loc = getlocation(fixturedef.func, curdir)
+ if (fixturedef.argname, loc) in seen:
+ continue
+ seen.add((fixturedef.argname, loc))
+ available.append((len(fixturedef.baseid),
+ fixturedef.func.__module__,
+ curdir.bestrelpath(loc),
+ fixturedef.argname, fixturedef))
+
+ available.sort()
+ currentmodule = None
+ for baseid, module, bestrel, argname, fixturedef in available:
+ if currentmodule != module:
+ if not module.startswith("_pytest."):
+ tw.line()
+ tw.sep("-", "fixtures defined from %s" % (module,))
+ currentmodule = module
+ if verbose <= 0 and argname[0] == "_":
+ continue
+ if verbose > 0:
+ funcargspec = "%s -- %s" % (argname, bestrel,)
+ else:
+ funcargspec = argname
+ tw.line(funcargspec, green=True)
+ loc = getlocation(fixturedef.func, curdir)
+ doc = fixturedef.func.__doc__ or ""
+ if doc:
+ write_docstring(tw, doc)
+ else:
+ tw.line(" %s: no docstring available" % (loc,),
+ red=True)
+
+
+def write_docstring(tw, doc):
+ INDENT = " "
+ doc = doc.rstrip()
+ if "\n" in doc:
+ firstline, rest = doc.split("\n", 1)
+ else:
+ firstline, rest = doc, ""
+
+ if firstline.strip():
+ tw.line(INDENT + firstline.strip())
+
+ if rest:
+ for line in dedent(rest).split("\n"):
+ tw.write(INDENT + line + "\n")
+
+
+class Function(FunctionMixin, main.Item, fixtures.FuncargnamesCompatAttr):
+ """ a Function Item is responsible for setting up and executing a
+ Python test function.
+ """
+ _genid = None
+
+ def __init__(self, name, parent, args=None, config=None,
+ callspec=None, callobj=NOTSET, keywords=None, session=None,
+ fixtureinfo=None, originalname=None):
+ super(Function, self).__init__(name, parent, config=config,
+ session=session)
+ self._args = args
+ if callobj is not NOTSET:
+ self.obj = callobj
+
+ self.keywords.update(self.obj.__dict__)
+ if callspec:
+ self.callspec = callspec
+ # this is total hostile and a mess
+ # keywords are broken by design by now
+ # this will be redeemed later
+ for mark in callspec.marks:
+ # feel free to cry, this was broken for years before
+ # and keywords cant fix it per design
+ self.keywords[mark.name] = mark
+ if keywords:
+ self.keywords.update(keywords)
+
+ if fixtureinfo is None:
+ fixtureinfo = self.session._fixturemanager.getfixtureinfo(
+ self.parent, self.obj, self.cls,
+ funcargs=not self._isyieldedfunction())
+ self._fixtureinfo = fixtureinfo
+ self.fixturenames = fixtureinfo.names_closure
+ self._initrequest()
+
+ #: original function name, without any decorations (for example
+ #: parametrization adds a ``"[...]"`` suffix to function names).
+ #:
+ #: .. versionadded:: 3.0
+ self.originalname = originalname
+
+ def _initrequest(self):
+ self.funcargs = {}
+ if self._isyieldedfunction():
+ assert not hasattr(self, "callspec"), (
+ "yielded functions (deprecated) cannot have funcargs")
+ else:
+ if hasattr(self, "callspec"):
+ callspec = self.callspec
+ assert not callspec.funcargs
+ self._genid = callspec.id
+ if hasattr(callspec, "param"):
+ self.param = callspec.param
+ self._request = fixtures.FixtureRequest(self)
+
+ @property
+ def function(self):
+ "underlying python 'function' object"
+ return getattr(self.obj, 'im_func', self.obj)
+
+ def _getobj(self):
+ name = self.name
+ i = name.find("[") # parametrization
+ if i != -1:
+ name = name[:i]
+ return getattr(self.parent.obj, name)
+
+ @property
+ def _pyfuncitem(self):
+ "(compatonly) for code expecting pytest-2.2 style request objects"
+ return self
+
+ def _isyieldedfunction(self):
+ return getattr(self, "_args", None) is not None
+
+ def runtest(self):
+ """ execute the underlying test function. """
+ self.ihook.pytest_pyfunc_call(pyfuncitem=self)
+
+ def setup(self):
+ super(Function, self).setup()
+ fixtures.fillfixtures(self)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/python_api.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/python_api.py
new file mode 100644
index 00000000000..81960295b38
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/python_api.py
@@ -0,0 +1,619 @@
+import math
+import sys
+
+import py
+from six.moves import zip
+
+from _pytest.compat import isclass
+from _pytest.outcomes import fail
+import _pytest._code
+
+
+def _cmp_raises_type_error(self, other):
+ """__cmp__ implementation which raises TypeError. Used
+ by Approx base classes to implement only == and != and raise a
+ TypeError for other comparisons.
+
+ Needed in Python 2 only, Python 3 all it takes is not implementing the
+ other operators at all.
+ """
+ __tracebackhide__ = True
+ raise TypeError('Comparison operators other than == and != not supported by approx objects')
+
+
+# builtin pytest.approx helper
+
+
+class ApproxBase(object):
+ """
+ Provide shared utilities for making approximate comparisons between numbers
+ or sequences of numbers.
+ """
+
+ def __init__(self, expected, rel=None, abs=None, nan_ok=False):
+ self.expected = expected
+ self.abs = abs
+ self.rel = rel
+ self.nan_ok = nan_ok
+
+ def __repr__(self):
+ raise NotImplementedError
+
+ def __eq__(self, actual):
+ return all(
+ a == self._approx_scalar(x)
+ for a, x in self._yield_comparisons(actual))
+
+ __hash__ = None
+
+ def __ne__(self, actual):
+ return not (actual == self)
+
+ if sys.version_info[0] == 2:
+ __cmp__ = _cmp_raises_type_error
+
+ def _approx_scalar(self, x):
+ return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok)
+
+ def _yield_comparisons(self, actual):
+ """
+ Yield all the pairs of numbers to be compared. This is used to
+ implement the `__eq__` method.
+ """
+ raise NotImplementedError
+
+
+class ApproxNumpy(ApproxBase):
+ """
+ Perform approximate comparisons for numpy arrays.
+ """
+
+ # Tell numpy to use our `__eq__` operator instead of its.
+ __array_priority__ = 100
+
+ def __repr__(self):
+ # It might be nice to rewrite this function to account for the
+ # shape of the array...
+ return "approx({0!r})".format(list(
+ self._approx_scalar(x) for x in self.expected))
+
+ if sys.version_info[0] == 2:
+ __cmp__ = _cmp_raises_type_error
+
+ def __eq__(self, actual):
+ import numpy as np
+
+ try:
+ actual = np.asarray(actual)
+ except: # noqa
+ raise TypeError("cannot compare '{0}' to numpy.ndarray".format(actual))
+
+ if actual.shape != self.expected.shape:
+ return False
+
+ return ApproxBase.__eq__(self, actual)
+
+ def _yield_comparisons(self, actual):
+ import numpy as np
+
+ # We can be sure that `actual` is a numpy array, because it's
+ # casted in `__eq__` before being passed to `ApproxBase.__eq__`,
+ # which is the only method that calls this one.
+ for i in np.ndindex(self.expected.shape):
+ yield actual[i], self.expected[i]
+
+
+class ApproxMapping(ApproxBase):
+ """
+ Perform approximate comparisons for mappings where the values are numbers
+ (the keys can be anything).
+ """
+
+ def __repr__(self):
+ return "approx({0!r})".format(dict(
+ (k, self._approx_scalar(v))
+ for k, v in self.expected.items()))
+
+ def __eq__(self, actual):
+ if set(actual.keys()) != set(self.expected.keys()):
+ return False
+
+ return ApproxBase.__eq__(self, actual)
+
+ def _yield_comparisons(self, actual):
+ for k in self.expected.keys():
+ yield actual[k], self.expected[k]
+
+
+class ApproxSequence(ApproxBase):
+ """
+ Perform approximate comparisons for sequences of numbers.
+ """
+
+ # Tell numpy to use our `__eq__` operator instead of its.
+ __array_priority__ = 100
+
+ def __repr__(self):
+ seq_type = type(self.expected)
+ if seq_type not in (tuple, list, set):
+ seq_type = list
+ return "approx({0!r})".format(seq_type(
+ self._approx_scalar(x) for x in self.expected))
+
+ def __eq__(self, actual):
+ if len(actual) != len(self.expected):
+ return False
+ return ApproxBase.__eq__(self, actual)
+
+ def _yield_comparisons(self, actual):
+ return zip(actual, self.expected)
+
+
+class ApproxScalar(ApproxBase):
+ """
+ Perform approximate comparisons for single numbers only.
+ """
+
+ def __repr__(self):
+ """
+ Return a string communicating both the expected value and the tolerance
+ for the comparison being made, e.g. '1.0 +- 1e-6'. Use the unicode
+ plus/minus symbol if this is python3 (it's too hard to get right for
+ python2).
+ """
+ if isinstance(self.expected, complex):
+ return str(self.expected)
+
+ # Infinities aren't compared using tolerances, so don't show a
+ # tolerance.
+ if math.isinf(self.expected):
+ return str(self.expected)
+
+ # If a sensible tolerance can't be calculated, self.tolerance will
+ # raise a ValueError. In this case, display '???'.
+ try:
+ vetted_tolerance = '{:.1e}'.format(self.tolerance)
+ except ValueError:
+ vetted_tolerance = '???'
+
+ if sys.version_info[0] == 2:
+ return '{0} +- {1}'.format(self.expected, vetted_tolerance)
+ else:
+ return u'{0} \u00b1 {1}'.format(self.expected, vetted_tolerance)
+
+ def __eq__(self, actual):
+ """
+ Return true if the given value is equal to the expected value within
+ the pre-specified tolerance.
+ """
+
+ # Short-circuit exact equality.
+ if actual == self.expected:
+ return True
+
+ # Allow the user to control whether NaNs are considered equal to each
+ # other or not. The abs() calls are for compatibility with complex
+ # numbers.
+ if math.isnan(abs(self.expected)):
+ return self.nan_ok and math.isnan(abs(actual))
+
+ # Infinity shouldn't be approximately equal to anything but itself, but
+ # if there's a relative tolerance, it will be infinite and infinity
+ # will seem approximately equal to everything. The equal-to-itself
+ # case would have been short circuited above, so here we can just
+ # return false if the expected value is infinite. The abs() call is
+ # for compatibility with complex numbers.
+ if math.isinf(abs(self.expected)):
+ return False
+
+ # Return true if the two numbers are within the tolerance.
+ return abs(self.expected - actual) <= self.tolerance
+
+ __hash__ = None
+
+ @property
+ def tolerance(self):
+ """
+ Return the tolerance for the comparison. This could be either an
+ absolute tolerance or a relative tolerance, depending on what the user
+ specified or which would be larger.
+ """
+ def set_default(x, default):
+ return x if x is not None else default
+
+ # Figure out what the absolute tolerance should be. ``self.abs`` is
+ # either None or a value specified by the user.
+ absolute_tolerance = set_default(self.abs, 1e-12)
+
+ if absolute_tolerance < 0:
+ raise ValueError("absolute tolerance can't be negative: {}".format(absolute_tolerance))
+ if math.isnan(absolute_tolerance):
+ raise ValueError("absolute tolerance can't be NaN.")
+
+ # If the user specified an absolute tolerance but not a relative one,
+ # just return the absolute tolerance.
+ if self.rel is None:
+ if self.abs is not None:
+ return absolute_tolerance
+
+ # Figure out what the relative tolerance should be. ``self.rel`` is
+ # either None or a value specified by the user. This is done after
+ # we've made sure the user didn't ask for an absolute tolerance only,
+ # because we don't want to raise errors about the relative tolerance if
+ # we aren't even going to use it.
+ relative_tolerance = set_default(self.rel, 1e-6) * abs(self.expected)
+
+ if relative_tolerance < 0:
+ raise ValueError("relative tolerance can't be negative: {}".format(absolute_tolerance))
+ if math.isnan(relative_tolerance):
+ raise ValueError("relative tolerance can't be NaN.")
+
+ # Return the larger of the relative and absolute tolerances.
+ return max(relative_tolerance, absolute_tolerance)
+
+
+def approx(expected, rel=None, abs=None, nan_ok=False):
+ """
+ Assert that two numbers (or two sets of numbers) are equal to each other
+ within some tolerance.
+
+ Due to the `intricacies of floating-point arithmetic`__, numbers that we
+ would intuitively expect to be equal are not always so::
+
+ >>> 0.1 + 0.2 == 0.3
+ False
+
+ __ https://docs.python.org/3/tutorial/floatingpoint.html
+
+ This problem is commonly encountered when writing tests, e.g. when making
+ sure that floating-point values are what you expect them to be. One way to
+ deal with this problem is to assert that two floating-point numbers are
+ equal to within some appropriate tolerance::
+
+ >>> abs((0.1 + 0.2) - 0.3) < 1e-6
+ True
+
+ However, comparisons like this are tedious to write and difficult to
+ understand. Furthermore, absolute comparisons like the one above are
+ usually discouraged because there's no tolerance that works well for all
+ situations. ``1e-6`` is good for numbers around ``1``, but too small for
+ very big numbers and too big for very small ones. It's better to express
+ the tolerance as a fraction of the expected value, but relative comparisons
+ like that are even more difficult to write correctly and concisely.
+
+ The ``approx`` class performs floating-point comparisons using a syntax
+ that's as intuitive as possible::
+
+ >>> from pytest import approx
+ >>> 0.1 + 0.2 == approx(0.3)
+ True
+
+ The same syntax also works for sequences of numbers::
+
+ >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6))
+ True
+
+ Dictionary *values*::
+
+ >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6})
+ True
+
+ And ``numpy`` arrays::
+
+ >>> import numpy as np # doctest: +SKIP
+ >>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6])) # doctest: +SKIP
+ True
+
+ By default, ``approx`` considers numbers within a relative tolerance of
+ ``1e-6`` (i.e. one part in a million) of its expected value to be equal.
+ This treatment would lead to surprising results if the expected value was
+ ``0.0``, because nothing but ``0.0`` itself is relatively close to ``0.0``.
+ To handle this case less surprisingly, ``approx`` also considers numbers
+ within an absolute tolerance of ``1e-12`` of its expected value to be
+ equal. Infinity and NaN are special cases. Infinity is only considered
+ equal to itself, regardless of the relative tolerance. NaN is not
+ considered equal to anything by default, but you can make it be equal to
+ itself by setting the ``nan_ok`` argument to True. (This is meant to
+ facilitate comparing arrays that use NaN to mean "no data".)
+
+ Both the relative and absolute tolerances can be changed by passing
+ arguments to the ``approx`` constructor::
+
+ >>> 1.0001 == approx(1)
+ False
+ >>> 1.0001 == approx(1, rel=1e-3)
+ True
+ >>> 1.0001 == approx(1, abs=1e-3)
+ True
+
+ If you specify ``abs`` but not ``rel``, the comparison will not consider
+ the relative tolerance at all. In other words, two numbers that are within
+ the default relative tolerance of ``1e-6`` will still be considered unequal
+ if they exceed the specified absolute tolerance. If you specify both
+ ``abs`` and ``rel``, the numbers will be considered equal if either
+ tolerance is met::
+
+ >>> 1 + 1e-8 == approx(1)
+ True
+ >>> 1 + 1e-8 == approx(1, abs=1e-12)
+ False
+ >>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12)
+ True
+
+ If you're thinking about using ``approx``, then you might want to know how
+ it compares to other good ways of comparing floating-point numbers. All of
+ these algorithms are based on relative and absolute tolerances and should
+ agree for the most part, but they do have meaningful differences:
+
+ - ``math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)``: True if the relative
+ tolerance is met w.r.t. either ``a`` or ``b`` or if the absolute
+ tolerance is met. Because the relative tolerance is calculated w.r.t.
+ both ``a`` and ``b``, this test is symmetric (i.e. neither ``a`` nor
+ ``b`` is a "reference value"). You have to specify an absolute tolerance
+ if you want to compare to ``0.0`` because there is no tolerance by
+ default. Only available in python>=3.5. `More information...`__
+
+ __ https://docs.python.org/3/library/math.html#math.isclose
+
+ - ``numpy.isclose(a, b, rtol=1e-5, atol=1e-8)``: True if the difference
+ between ``a`` and ``b`` is less that the sum of the relative tolerance
+ w.r.t. ``b`` and the absolute tolerance. Because the relative tolerance
+ is only calculated w.r.t. ``b``, this test is asymmetric and you can
+ think of ``b`` as the reference value. Support for comparing sequences
+ is provided by ``numpy.allclose``. `More information...`__
+
+ __ http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.isclose.html
+
+ - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b``
+ are within an absolute tolerance of ``1e-7``. No relative tolerance is
+ considered and the absolute tolerance cannot be changed, so this function
+ is not appropriate for very large or very small numbers. Also, it's only
+ available in subclasses of ``unittest.TestCase`` and it's ugly because it
+ doesn't follow PEP8. `More information...`__
+
+ __ https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertAlmostEqual
+
+ - ``a == pytest.approx(b, rel=1e-6, abs=1e-12)``: True if the relative
+ tolerance is met w.r.t. ``b`` or if the absolute tolerance is met.
+ Because the relative tolerance is only calculated w.r.t. ``b``, this test
+ is asymmetric and you can think of ``b`` as the reference value. In the
+ special case that you explicitly specify an absolute tolerance but not a
+ relative tolerance, only the absolute tolerance is considered.
+
+ .. warning::
+
+ .. versionchanged:: 3.2
+
+ In order to avoid inconsistent behavior, ``TypeError`` is
+ raised for ``>``, ``>=``, ``<`` and ``<=`` comparisons.
+ The example below illustrates the problem::
+
+ assert approx(0.1) > 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10)
+ assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10)
+
+ In the second example one expects ``approx(0.1).__le__(0.1 + 1e-10)``
+ to be called. But instead, ``approx(0.1).__lt__(0.1 + 1e-10)`` is used to
+ comparison. This is because the call hierarchy of rich comparisons
+ follows a fixed behavior. `More information...`__
+
+ __ https://docs.python.org/3/reference/datamodel.html#object.__ge__
+ """
+
+ from collections import Mapping, Sequence
+ from _pytest.compat import STRING_TYPES as String
+
+ # Delegate the comparison to a class that knows how to deal with the type
+ # of the expected value (e.g. int, float, list, dict, numpy.array, etc).
+ #
+ # This architecture is really driven by the need to support numpy arrays.
+ # The only way to override `==` for arrays without requiring that approx be
+ # the left operand is to inherit the approx object from `numpy.ndarray`.
+ # But that can't be a general solution, because it requires (1) numpy to be
+ # installed and (2) the expected value to be a numpy array. So the general
+ # solution is to delegate each type of expected value to a different class.
+ #
+ # This has the advantage that it made it easy to support mapping types
+ # (i.e. dict). The old code accepted mapping types, but would only compare
+ # their keys, which is probably not what most people would expect.
+
+ if _is_numpy_array(expected):
+ cls = ApproxNumpy
+ elif isinstance(expected, Mapping):
+ cls = ApproxMapping
+ elif isinstance(expected, Sequence) and not isinstance(expected, String):
+ cls = ApproxSequence
+ else:
+ cls = ApproxScalar
+
+ return cls(expected, rel, abs, nan_ok)
+
+
+def _is_numpy_array(obj):
+ """
+ Return true if the given object is a numpy array. Make a special effort to
+ avoid importing numpy unless it's really necessary.
+ """
+ import inspect
+
+ for cls in inspect.getmro(type(obj)):
+ if cls.__module__ == 'numpy':
+ try:
+ import numpy as np
+ return isinstance(obj, np.ndarray)
+ except ImportError:
+ pass
+
+ return False
+
+
+# builtin pytest.raises helper
+
+def raises(expected_exception, *args, **kwargs):
+ """
+ Assert that a code block/function call raises ``expected_exception``
+ and raise a failure exception otherwise.
+
+ This helper produces a ``ExceptionInfo()`` object (see below).
+
+ You may use this function as a context manager::
+
+ >>> with raises(ZeroDivisionError):
+ ... 1/0
+
+ .. versionchanged:: 2.10
+
+ In the context manager form you may use the keyword argument
+ ``message`` to specify a custom failure message::
+
+ >>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):
+ ... pass
+ Traceback (most recent call last):
+ ...
+ Failed: Expecting ZeroDivisionError
+
+ .. note::
+
+ When using ``pytest.raises`` as a context manager, it's worthwhile to
+ note that normal context manager rules apply and that the exception
+ raised *must* be the final line in the scope of the context manager.
+ Lines of code after that, within the scope of the context manager will
+ not be executed. For example::
+
+ >>> value = 15
+ >>> with raises(ValueError) as exc_info:
+ ... if value > 10:
+ ... raise ValueError("value must be <= 10")
+ ... assert exc_info.type == ValueError # this will not execute
+
+ Instead, the following approach must be taken (note the difference in
+ scope)::
+
+ >>> with raises(ValueError) as exc_info:
+ ... if value > 10:
+ ... raise ValueError("value must be <= 10")
+ ...
+ >>> assert exc_info.type == ValueError
+
+
+ Since version ``3.1`` you can use the keyword argument ``match`` to assert that the
+ exception matches a text or regex::
+
+ >>> with raises(ValueError, match='must be 0 or None'):
+ ... raise ValueError("value must be 0 or None")
+
+ >>> with raises(ValueError, match=r'must be \d+$'):
+ ... raise ValueError("value must be 42")
+
+ **Legacy forms**
+
+ The forms below are fully supported but are discouraged for new code because the
+ context manager form is regarded as more readable and less error-prone.
+
+ It is possible to specify a callable by passing a to-be-called lambda::
+
+ >>> raises(ZeroDivisionError, lambda: 1/0)
+ <ExceptionInfo ...>
+
+ or you can specify an arbitrary callable with arguments::
+
+ >>> def f(x): return 1/x
+ ...
+ >>> raises(ZeroDivisionError, f, 0)
+ <ExceptionInfo ...>
+ >>> raises(ZeroDivisionError, f, x=0)
+ <ExceptionInfo ...>
+
+ It is also possible to pass a string to be evaluated at runtime::
+
+ >>> raises(ZeroDivisionError, "f(0)")
+ <ExceptionInfo ...>
+
+ The string will be evaluated using the same ``locals()`` and ``globals()``
+ at the moment of the ``raises`` call.
+
+ .. autoclass:: _pytest._code.ExceptionInfo
+ :members:
+
+ .. note::
+ Similar to caught exception objects in Python, explicitly clearing
+ local references to returned ``ExceptionInfo`` objects can
+ help the Python interpreter speed up its garbage collection.
+
+ Clearing those references breaks a reference cycle
+ (``ExceptionInfo`` --> caught exception --> frame stack raising
+ the exception --> current frame stack --> local variables -->
+ ``ExceptionInfo``) which makes Python keep all objects referenced
+ from that cycle (including all local variables in the current
+ frame) alive until the next cyclic garbage collection run. See the
+ official Python ``try`` statement documentation for more detailed
+ information.
+
+ """
+ __tracebackhide__ = True
+ msg = ("exceptions must be old-style classes or"
+ " derived from BaseException, not %s")
+ if isinstance(expected_exception, tuple):
+ for exc in expected_exception:
+ if not isclass(exc):
+ raise TypeError(msg % type(exc))
+ elif not isclass(expected_exception):
+ raise TypeError(msg % type(expected_exception))
+
+ message = "DID NOT RAISE {0}".format(expected_exception)
+ match_expr = None
+
+ if not args:
+ if "message" in kwargs:
+ message = kwargs.pop("message")
+ if "match" in kwargs:
+ match_expr = kwargs.pop("match")
+ message += " matching '{0}'".format(match_expr)
+ return RaisesContext(expected_exception, message, match_expr)
+ elif isinstance(args[0], str):
+ code, = args
+ assert isinstance(code, str)
+ frame = sys._getframe(1)
+ loc = frame.f_locals.copy()
+ loc.update(kwargs)
+ # print "raises frame scope: %r" % frame.f_locals
+ try:
+ code = _pytest._code.Source(code).compile()
+ py.builtin.exec_(code, frame.f_globals, loc)
+ # XXX didn'T mean f_globals == f_locals something special?
+ # this is destroyed here ...
+ except expected_exception:
+ return _pytest._code.ExceptionInfo()
+ else:
+ func = args[0]
+ try:
+ func(*args[1:], **kwargs)
+ except expected_exception:
+ return _pytest._code.ExceptionInfo()
+ fail(message)
+
+
+raises.Exception = fail.Exception
+
+
+class RaisesContext(object):
+ def __init__(self, expected_exception, message, match_expr):
+ self.expected_exception = expected_exception
+ self.message = message
+ self.match_expr = match_expr
+ self.excinfo = None
+
+ def __enter__(self):
+ self.excinfo = object.__new__(_pytest._code.ExceptionInfo)
+ return self.excinfo
+
+ def __exit__(self, *tp):
+ __tracebackhide__ = True
+ if tp[0] is None:
+ fail(self.message)
+ self.excinfo.__init__(tp)
+ suppress_exception = issubclass(self.excinfo.type, self.expected_exception)
+ if sys.version_info[0] == 2 and suppress_exception:
+ sys.exc_clear()
+ if self.match_expr:
+ self.excinfo.match(self.match_expr)
+ return suppress_exception
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/recwarn.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/recwarn.py
new file mode 100644
index 00000000000..4fceb10a7f3
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/recwarn.py
@@ -0,0 +1,236 @@
+""" recording warnings during test function execution. """
+from __future__ import absolute_import, division, print_function
+
+import inspect
+
+import _pytest._code
+import py
+import sys
+import warnings
+
+import re
+
+from _pytest.fixtures import yield_fixture
+from _pytest.outcomes import fail
+
+
+@yield_fixture
+def recwarn():
+ """Return a WarningsRecorder instance that provides these methods:
+
+ * ``pop(category=None)``: return last warning matching the category.
+ * ``clear()``: clear list of warnings
+
+ See http://docs.python.org/library/warnings.html for information
+ on warning categories.
+ """
+ wrec = WarningsRecorder()
+ with wrec:
+ warnings.simplefilter('default')
+ yield wrec
+
+
+def deprecated_call(func=None, *args, **kwargs):
+ """context manager that can be used to ensure a block of code triggers a
+ ``DeprecationWarning`` or ``PendingDeprecationWarning``::
+
+ >>> import warnings
+ >>> def api_call_v2():
+ ... warnings.warn('use v3 of this api', DeprecationWarning)
+ ... return 200
+
+ >>> with deprecated_call():
+ ... assert api_call_v2() == 200
+
+ ``deprecated_call`` can also be used by passing a function and ``*args`` and ``*kwargs``,
+ in which case it will ensure calling ``func(*args, **kwargs)`` produces one of the warnings
+ types above.
+ """
+ if not func:
+ return _DeprecatedCallContext()
+ else:
+ __tracebackhide__ = True
+ with _DeprecatedCallContext():
+ return func(*args, **kwargs)
+
+
+class _DeprecatedCallContext(object):
+ """Implements the logic to capture deprecation warnings as a context manager."""
+
+ def __enter__(self):
+ self._captured_categories = []
+ self._old_warn = warnings.warn
+ self._old_warn_explicit = warnings.warn_explicit
+ warnings.warn_explicit = self._warn_explicit
+ warnings.warn = self._warn
+
+ def _warn_explicit(self, message, category, *args, **kwargs):
+ self._captured_categories.append(category)
+
+ def _warn(self, message, category=None, *args, **kwargs):
+ if isinstance(message, Warning):
+ self._captured_categories.append(message.__class__)
+ else:
+ self._captured_categories.append(category)
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ warnings.warn_explicit = self._old_warn_explicit
+ warnings.warn = self._old_warn
+
+ if exc_type is None:
+ deprecation_categories = (DeprecationWarning, PendingDeprecationWarning)
+ if not any(issubclass(c, deprecation_categories) for c in self._captured_categories):
+ __tracebackhide__ = True
+ msg = "Did not produce DeprecationWarning or PendingDeprecationWarning"
+ raise AssertionError(msg)
+
+
+def warns(expected_warning, *args, **kwargs):
+ """Assert that code raises a particular class of warning.
+
+ Specifically, the input @expected_warning can be a warning class or
+ tuple of warning classes, and the code must return that warning
+ (if a single class) or one of those warnings (if a tuple).
+
+ This helper produces a list of ``warnings.WarningMessage`` objects,
+ one for each warning raised.
+
+ This function can be used as a context manager, or any of the other ways
+ ``pytest.raises`` can be used::
+
+ >>> with warns(RuntimeWarning):
+ ... warnings.warn("my warning", RuntimeWarning)
+
+ In the context manager form you may use the keyword argument ``match`` to assert
+ that the exception matches a text or regex::
+
+ >>> with warns(UserWarning, match='must be 0 or None'):
+ ... warnings.warn("value must be 0 or None", UserWarning)
+
+ >>> with warns(UserWarning, match=r'must be \d+$'):
+ ... warnings.warn("value must be 42", UserWarning)
+
+ >>> with warns(UserWarning, match=r'must be \d+$'):
+ ... warnings.warn("this is not here", UserWarning)
+ Traceback (most recent call last):
+ ...
+ Failed: DID NOT WARN. No warnings of type ...UserWarning... was emitted...
+
+ """
+ match_expr = None
+ if not args:
+ if "match" in kwargs:
+ match_expr = kwargs.pop("match")
+ return WarningsChecker(expected_warning, match_expr=match_expr)
+ elif isinstance(args[0], str):
+ code, = args
+ assert isinstance(code, str)
+ frame = sys._getframe(1)
+ loc = frame.f_locals.copy()
+ loc.update(kwargs)
+
+ with WarningsChecker(expected_warning, match_expr=match_expr):
+ code = _pytest._code.Source(code).compile()
+ py.builtin.exec_(code, frame.f_globals, loc)
+ else:
+ func = args[0]
+ with WarningsChecker(expected_warning, match_expr=match_expr):
+ return func(*args[1:], **kwargs)
+
+
+class WarningsRecorder(warnings.catch_warnings):
+ """A context manager to record raised warnings.
+
+ Adapted from `warnings.catch_warnings`.
+ """
+
+ def __init__(self):
+ super(WarningsRecorder, self).__init__(record=True)
+ self._entered = False
+ self._list = []
+
+ @property
+ def list(self):
+ """The list of recorded warnings."""
+ return self._list
+
+ def __getitem__(self, i):
+ """Get a recorded warning by index."""
+ return self._list[i]
+
+ def __iter__(self):
+ """Iterate through the recorded warnings."""
+ return iter(self._list)
+
+ def __len__(self):
+ """The number of recorded warnings."""
+ return len(self._list)
+
+ def pop(self, cls=Warning):
+ """Pop the first recorded warning, raise exception if not exists."""
+ for i, w in enumerate(self._list):
+ if issubclass(w.category, cls):
+ return self._list.pop(i)
+ __tracebackhide__ = True
+ raise AssertionError("%r not found in warning list" % cls)
+
+ def clear(self):
+ """Clear the list of recorded warnings."""
+ self._list[:] = []
+
+ def __enter__(self):
+ if self._entered:
+ __tracebackhide__ = True
+ raise RuntimeError("Cannot enter %r twice" % self)
+ self._list = super(WarningsRecorder, self).__enter__()
+ warnings.simplefilter('always')
+ return self
+
+ def __exit__(self, *exc_info):
+ if not self._entered:
+ __tracebackhide__ = True
+ raise RuntimeError("Cannot exit %r without entering first" % self)
+ super(WarningsRecorder, self).__exit__(*exc_info)
+
+
+class WarningsChecker(WarningsRecorder):
+ def __init__(self, expected_warning=None, match_expr=None):
+ super(WarningsChecker, self).__init__()
+
+ msg = ("exceptions must be old-style classes or "
+ "derived from Warning, not %s")
+ if isinstance(expected_warning, tuple):
+ for exc in expected_warning:
+ if not inspect.isclass(exc):
+ raise TypeError(msg % type(exc))
+ elif inspect.isclass(expected_warning):
+ expected_warning = (expected_warning,)
+ elif expected_warning is not None:
+ raise TypeError(msg % type(expected_warning))
+
+ self.expected_warning = expected_warning
+ self.match_expr = match_expr
+
+ def __exit__(self, *exc_info):
+ super(WarningsChecker, self).__exit__(*exc_info)
+
+ # only check if we're not currently handling an exception
+ if all(a is None for a in exc_info):
+ if self.expected_warning is not None:
+ if not any(issubclass(r.category, self.expected_warning)
+ for r in self):
+ __tracebackhide__ = True
+ fail("DID NOT WARN. No warnings of type {0} was emitted. "
+ "The list of emitted warnings is: {1}.".format(
+ self.expected_warning,
+ [each.message for each in self]))
+ elif self.match_expr is not None:
+ for r in self:
+ if issubclass(r.category, self.expected_warning):
+ if re.compile(self.match_expr).search(str(r.message)):
+ break
+ else:
+ fail("DID NOT WARN. No warnings of type {0} matching"
+ " ('{1}') was emitted. The list of emitted warnings"
+ " is: {2}.".format(self.expected_warning, self.match_expr,
+ [each.message for each in self]))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/resultlog.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/resultlog.py
new file mode 100644
index 00000000000..9f9c2d1f653
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/resultlog.py
@@ -0,0 +1,113 @@
+""" log machine-parseable test session result information in a plain
+text file.
+"""
+from __future__ import absolute_import, division, print_function
+
+import py
+import os
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("terminal reporting", "resultlog plugin options")
+ group.addoption('--resultlog', '--result-log', action="store",
+ metavar="path", default=None,
+ help="DEPRECATED path for machine-readable result log.")
+
+
+def pytest_configure(config):
+ resultlog = config.option.resultlog
+ # prevent opening resultlog on slave nodes (xdist)
+ if resultlog and not hasattr(config, 'slaveinput'):
+ dirname = os.path.dirname(os.path.abspath(resultlog))
+ if not os.path.isdir(dirname):
+ os.makedirs(dirname)
+ logfile = open(resultlog, 'w', 1) # line buffered
+ config._resultlog = ResultLog(config, logfile)
+ config.pluginmanager.register(config._resultlog)
+
+ from _pytest.deprecated import RESULT_LOG
+ config.warn('C1', RESULT_LOG)
+
+
+def pytest_unconfigure(config):
+ resultlog = getattr(config, '_resultlog', None)
+ if resultlog:
+ resultlog.logfile.close()
+ del config._resultlog
+ config.pluginmanager.unregister(resultlog)
+
+
+def generic_path(item):
+ chain = item.listchain()
+ gpath = [chain[0].name]
+ fspath = chain[0].fspath
+ fspart = False
+ for node in chain[1:]:
+ newfspath = node.fspath
+ if newfspath == fspath:
+ if fspart:
+ gpath.append(':')
+ fspart = False
+ else:
+ gpath.append('.')
+ else:
+ gpath.append('/')
+ fspart = True
+ name = node.name
+ if name[0] in '([':
+ gpath.pop()
+ gpath.append(name)
+ fspath = newfspath
+ return ''.join(gpath)
+
+
+class ResultLog(object):
+ def __init__(self, config, logfile):
+ self.config = config
+ self.logfile = logfile # preferably line buffered
+
+ def write_log_entry(self, testpath, lettercode, longrepr):
+ print("%s %s" % (lettercode, testpath), file=self.logfile)
+ for line in longrepr.splitlines():
+ print(" %s" % line, file=self.logfile)
+
+ def log_outcome(self, report, lettercode, longrepr):
+ testpath = getattr(report, 'nodeid', None)
+ if testpath is None:
+ testpath = report.fspath
+ self.write_log_entry(testpath, lettercode, longrepr)
+
+ def pytest_runtest_logreport(self, report):
+ if report.when != "call" and report.passed:
+ return
+ res = self.config.hook.pytest_report_teststatus(report=report)
+ code = res[1]
+ if code == 'x':
+ longrepr = str(report.longrepr)
+ elif code == 'X':
+ longrepr = ''
+ elif report.passed:
+ longrepr = ""
+ elif report.failed:
+ longrepr = str(report.longrepr)
+ elif report.skipped:
+ longrepr = str(report.longrepr[2])
+ self.log_outcome(report, code, longrepr)
+
+ def pytest_collectreport(self, report):
+ if not report.passed:
+ if report.failed:
+ code = "F"
+ longrepr = str(report.longrepr)
+ else:
+ assert report.skipped
+ code = "S"
+ longrepr = "%s:%d: %s" % report.longrepr
+ self.log_outcome(report, code, longrepr)
+
+ def pytest_internalerror(self, excrepr):
+ reprcrash = getattr(excrepr, 'reprcrash', None)
+ path = getattr(reprcrash, "path", None)
+ if path is None:
+ path = "cwd:%s" % py.path.local()
+ self.write_log_entry(path, '!', str(excrepr))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/runner.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/runner.py
new file mode 100644
index 00000000000..e07ed2a248b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/runner.py
@@ -0,0 +1,506 @@
+""" basic collect and runtest protocol implementations """
+from __future__ import absolute_import, division, print_function
+
+import bdb
+import os
+import sys
+from time import time
+
+import py
+from _pytest._code.code import TerminalRepr, ExceptionInfo
+from _pytest.outcomes import skip, Skipped, TEST_OUTCOME
+
+#
+# pytest plugin hooks
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("terminal reporting", "reporting", after="general")
+ group.addoption('--durations',
+ action="store", type=int, default=None, metavar="N",
+ help="show N slowest setup/test durations (N=0 for all)."),
+
+
+def pytest_terminal_summary(terminalreporter):
+ durations = terminalreporter.config.option.durations
+ if durations is None:
+ return
+ tr = terminalreporter
+ dlist = []
+ for replist in tr.stats.values():
+ for rep in replist:
+ if hasattr(rep, 'duration'):
+ dlist.append(rep)
+ if not dlist:
+ return
+ dlist.sort(key=lambda x: x.duration)
+ dlist.reverse()
+ if not durations:
+ tr.write_sep("=", "slowest test durations")
+ else:
+ tr.write_sep("=", "slowest %s test durations" % durations)
+ dlist = dlist[:durations]
+
+ for rep in dlist:
+ nodeid = rep.nodeid.replace("::()::", "::")
+ tr.write_line("%02.2fs %-8s %s" %
+ (rep.duration, rep.when, nodeid))
+
+
+def pytest_sessionstart(session):
+ session._setupstate = SetupState()
+
+
+def pytest_sessionfinish(session):
+ session._setupstate.teardown_all()
+
+
+def pytest_runtest_protocol(item, nextitem):
+ item.ihook.pytest_runtest_logstart(
+ nodeid=item.nodeid, location=item.location,
+ )
+ runtestprotocol(item, nextitem=nextitem)
+ return True
+
+
+def runtestprotocol(item, log=True, nextitem=None):
+ hasrequest = hasattr(item, "_request")
+ if hasrequest and not item._request:
+ item._initrequest()
+ rep = call_and_report(item, "setup", log)
+ reports = [rep]
+ if rep.passed:
+ if item.config.option.setupshow:
+ show_test_item(item)
+ if not item.config.option.setuponly:
+ reports.append(call_and_report(item, "call", log))
+ reports.append(call_and_report(item, "teardown", log,
+ nextitem=nextitem))
+ # after all teardown hooks have been called
+ # want funcargs and request info to go away
+ if hasrequest:
+ item._request = False
+ item.funcargs = None
+ return reports
+
+
+def show_test_item(item):
+ """Show test function, parameters and the fixtures of the test item."""
+ tw = item.config.get_terminal_writer()
+ tw.line()
+ tw.write(' ' * 8)
+ tw.write(item._nodeid)
+ used_fixtures = sorted(item._fixtureinfo.name2fixturedefs.keys())
+ if used_fixtures:
+ tw.write(' (fixtures used: {0})'.format(', '.join(used_fixtures)))
+
+
+def pytest_runtest_setup(item):
+ _update_current_test_var(item, 'setup')
+ item.session._setupstate.prepare(item)
+
+
+def pytest_runtest_call(item):
+ _update_current_test_var(item, 'call')
+ try:
+ item.runtest()
+ except Exception:
+ # Store trace info to allow postmortem debugging
+ type, value, tb = sys.exc_info()
+ tb = tb.tb_next # Skip *this* frame
+ sys.last_type = type
+ sys.last_value = value
+ sys.last_traceback = tb
+ del tb # Get rid of it in this namespace
+ raise
+
+
+def pytest_runtest_teardown(item, nextitem):
+ _update_current_test_var(item, 'teardown')
+ item.session._setupstate.teardown_exact(item, nextitem)
+ _update_current_test_var(item, None)
+
+
+def _update_current_test_var(item, when):
+ """
+ Update PYTEST_CURRENT_TEST to reflect the current item and stage.
+
+ If ``when`` is None, delete PYTEST_CURRENT_TEST from the environment.
+ """
+ var_name = 'PYTEST_CURRENT_TEST'
+ if when:
+ value = '{0} ({1})'.format(item.nodeid, when)
+ # don't allow null bytes on environment variables (see #2644, #2957)
+ value = value.replace('\x00', '(null)')
+ os.environ[var_name] = value
+ else:
+ os.environ.pop(var_name)
+
+
+def pytest_report_teststatus(report):
+ if report.when in ("setup", "teardown"):
+ if report.failed:
+ # category, shortletter, verbose-word
+ return "error", "E", "ERROR"
+ elif report.skipped:
+ return "skipped", "s", "SKIPPED"
+ else:
+ return "", "", ""
+
+
+#
+# Implementation
+
+def call_and_report(item, when, log=True, **kwds):
+ call = call_runtest_hook(item, when, **kwds)
+ hook = item.ihook
+ report = hook.pytest_runtest_makereport(item=item, call=call)
+ if log:
+ hook.pytest_runtest_logreport(report=report)
+ if check_interactive_exception(call, report):
+ hook.pytest_exception_interact(node=item, call=call, report=report)
+ return report
+
+
+def check_interactive_exception(call, report):
+ return call.excinfo and not (
+ hasattr(report, "wasxfail") or
+ call.excinfo.errisinstance(skip.Exception) or
+ call.excinfo.errisinstance(bdb.BdbQuit))
+
+
+def call_runtest_hook(item, when, **kwds):
+ hookname = "pytest_runtest_" + when
+ ihook = getattr(item.ihook, hookname)
+ return CallInfo(lambda: ihook(item=item, **kwds), when=when)
+
+
+class CallInfo:
+ """ Result/Exception info a function invocation. """
+ #: None or ExceptionInfo object.
+ excinfo = None
+
+ def __init__(self, func, when):
+ #: context of invocation: one of "setup", "call",
+ #: "teardown", "memocollect"
+ self.when = when
+ self.start = time()
+ try:
+ self.result = func()
+ except KeyboardInterrupt:
+ self.stop = time()
+ raise
+ except: # noqa
+ self.excinfo = ExceptionInfo()
+ self.stop = time()
+
+ def __repr__(self):
+ if self.excinfo:
+ status = "exception: %s" % str(self.excinfo.value)
+ else:
+ status = "result: %r" % (self.result,)
+ return "<CallInfo when=%r %s>" % (self.when, status)
+
+
+def getslaveinfoline(node):
+ try:
+ return node._slaveinfocache
+ except AttributeError:
+ d = node.slaveinfo
+ ver = "%s.%s.%s" % d['version_info'][:3]
+ node._slaveinfocache = s = "[%s] %s -- Python %s %s" % (
+ d['id'], d['sysplatform'], ver, d['executable'])
+ return s
+
+
+class BaseReport(object):
+
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+ def toterminal(self, out):
+ if hasattr(self, 'node'):
+ out.line(getslaveinfoline(self.node))
+
+ longrepr = self.longrepr
+ if longrepr is None:
+ return
+
+ if hasattr(longrepr, 'toterminal'):
+ longrepr.toterminal(out)
+ else:
+ try:
+ out.line(longrepr)
+ except UnicodeEncodeError:
+ out.line("<unprintable longrepr>")
+
+ def get_sections(self, prefix):
+ for name, content in self.sections:
+ if name.startswith(prefix):
+ yield prefix, content
+
+ @property
+ def longreprtext(self):
+ """
+ Read-only property that returns the full string representation
+ of ``longrepr``.
+
+ .. versionadded:: 3.0
+ """
+ tw = py.io.TerminalWriter(stringio=True)
+ tw.hasmarkup = False
+ self.toterminal(tw)
+ exc = tw.stringio.getvalue()
+ return exc.strip()
+
+ @property
+ def capstdout(self):
+ """Return captured text from stdout, if capturing is enabled
+
+ .. versionadded:: 3.0
+ """
+ return ''.join(content for (prefix, content) in self.get_sections('Captured stdout'))
+
+ @property
+ def capstderr(self):
+ """Return captured text from stderr, if capturing is enabled
+
+ .. versionadded:: 3.0
+ """
+ return ''.join(content for (prefix, content) in self.get_sections('Captured stderr'))
+
+ passed = property(lambda x: x.outcome == "passed")
+ failed = property(lambda x: x.outcome == "failed")
+ skipped = property(lambda x: x.outcome == "skipped")
+
+ @property
+ def fspath(self):
+ return self.nodeid.split("::")[0]
+
+
+def pytest_runtest_makereport(item, call):
+ when = call.when
+ duration = call.stop - call.start
+ keywords = dict([(x, 1) for x in item.keywords])
+ excinfo = call.excinfo
+ sections = []
+ if not call.excinfo:
+ outcome = "passed"
+ longrepr = None
+ else:
+ if not isinstance(excinfo, ExceptionInfo):
+ outcome = "failed"
+ longrepr = excinfo
+ elif excinfo.errisinstance(skip.Exception):
+ outcome = "skipped"
+ r = excinfo._getreprcrash()
+ longrepr = (str(r.path), r.lineno, r.message)
+ else:
+ outcome = "failed"
+ if call.when == "call":
+ longrepr = item.repr_failure(excinfo)
+ else: # exception in setup or teardown
+ longrepr = item._repr_failure_py(excinfo,
+ style=item.config.option.tbstyle)
+ for rwhen, key, content in item._report_sections:
+ sections.append(("Captured %s %s" % (key, rwhen), content))
+ return TestReport(item.nodeid, item.location,
+ keywords, outcome, longrepr, when,
+ sections, duration)
+
+
+class TestReport(BaseReport):
+ """ Basic test report object (also used for setup and teardown calls if
+ they fail).
+ """
+
+ def __init__(self, nodeid, location, keywords, outcome,
+ longrepr, when, sections=(), duration=0, **extra):
+ #: normalized collection node id
+ self.nodeid = nodeid
+
+ #: a (filesystempath, lineno, domaininfo) tuple indicating the
+ #: actual location of a test item - it might be different from the
+ #: collected one e.g. if a method is inherited from a different module.
+ self.location = location
+
+ #: a name -> value dictionary containing all keywords and
+ #: markers associated with a test invocation.
+ self.keywords = keywords
+
+ #: test outcome, always one of "passed", "failed", "skipped".
+ self.outcome = outcome
+
+ #: None or a failure representation.
+ self.longrepr = longrepr
+
+ #: one of 'setup', 'call', 'teardown' to indicate runtest phase.
+ self.when = when
+
+ #: list of pairs ``(str, str)`` of extra information which needs to
+ #: marshallable. Used by pytest to add captured text
+ #: from ``stdout`` and ``stderr``, but may be used by other plugins
+ #: to add arbitrary information to reports.
+ self.sections = list(sections)
+
+ #: time it took to run just the test
+ self.duration = duration
+
+ self.__dict__.update(extra)
+
+ def __repr__(self):
+ return "<TestReport %r when=%r outcome=%r>" % (
+ self.nodeid, self.when, self.outcome)
+
+
+class TeardownErrorReport(BaseReport):
+ outcome = "failed"
+ when = "teardown"
+
+ def __init__(self, longrepr, **extra):
+ self.longrepr = longrepr
+ self.sections = []
+ self.__dict__.update(extra)
+
+
+def pytest_make_collect_report(collector):
+ call = CallInfo(
+ lambda: list(collector.collect()),
+ 'collect')
+ longrepr = None
+ if not call.excinfo:
+ outcome = "passed"
+ else:
+ from _pytest import nose
+ skip_exceptions = (Skipped,) + nose.get_skip_exceptions()
+ if call.excinfo.errisinstance(skip_exceptions):
+ outcome = "skipped"
+ r = collector._repr_failure_py(call.excinfo, "line").reprcrash
+ longrepr = (str(r.path), r.lineno, r.message)
+ else:
+ outcome = "failed"
+ errorinfo = collector.repr_failure(call.excinfo)
+ if not hasattr(errorinfo, "toterminal"):
+ errorinfo = CollectErrorRepr(errorinfo)
+ longrepr = errorinfo
+ rep = CollectReport(collector.nodeid, outcome, longrepr,
+ getattr(call, 'result', None))
+ rep.call = call # see collect_one_node
+ return rep
+
+
+class CollectReport(BaseReport):
+ def __init__(self, nodeid, outcome, longrepr, result,
+ sections=(), **extra):
+ self.nodeid = nodeid
+ self.outcome = outcome
+ self.longrepr = longrepr
+ self.result = result or []
+ self.sections = list(sections)
+ self.__dict__.update(extra)
+
+ @property
+ def location(self):
+ return (self.fspath, None, self.fspath)
+
+ def __repr__(self):
+ return "<CollectReport %r lenresult=%s outcome=%r>" % (
+ self.nodeid, len(self.result), self.outcome)
+
+
+class CollectErrorRepr(TerminalRepr):
+ def __init__(self, msg):
+ self.longrepr = msg
+
+ def toterminal(self, out):
+ out.line(self.longrepr, red=True)
+
+
+class SetupState(object):
+ """ shared state for setting up/tearing down test items or collectors. """
+
+ def __init__(self):
+ self.stack = []
+ self._finalizers = {}
+
+ def addfinalizer(self, finalizer, colitem):
+ """ attach a finalizer to the given colitem.
+ if colitem is None, this will add a finalizer that
+ is called at the end of teardown_all().
+ """
+ assert colitem and not isinstance(colitem, tuple)
+ assert callable(finalizer)
+ # assert colitem in self.stack # some unit tests don't setup stack :/
+ self._finalizers.setdefault(colitem, []).append(finalizer)
+
+ def _pop_and_teardown(self):
+ colitem = self.stack.pop()
+ self._teardown_with_finalization(colitem)
+
+ def _callfinalizers(self, colitem):
+ finalizers = self._finalizers.pop(colitem, None)
+ exc = None
+ while finalizers:
+ fin = finalizers.pop()
+ try:
+ fin()
+ except TEST_OUTCOME:
+ # XXX Only first exception will be seen by user,
+ # ideally all should be reported.
+ if exc is None:
+ exc = sys.exc_info()
+ if exc:
+ py.builtin._reraise(*exc)
+
+ def _teardown_with_finalization(self, colitem):
+ self._callfinalizers(colitem)
+ if hasattr(colitem, "teardown"):
+ colitem.teardown()
+ for colitem in self._finalizers:
+ assert colitem is None or colitem in self.stack \
+ or isinstance(colitem, tuple)
+
+ def teardown_all(self):
+ while self.stack:
+ self._pop_and_teardown()
+ for key in list(self._finalizers):
+ self._teardown_with_finalization(key)
+ assert not self._finalizers
+
+ def teardown_exact(self, item, nextitem):
+ needed_collectors = nextitem and nextitem.listchain() or []
+ self._teardown_towards(needed_collectors)
+
+ def _teardown_towards(self, needed_collectors):
+ while self.stack:
+ if self.stack == needed_collectors[:len(self.stack)]:
+ break
+ self._pop_and_teardown()
+
+ def prepare(self, colitem):
+ """ setup objects along the collector chain to the test-method
+ and teardown previously setup objects."""
+ needed_collectors = colitem.listchain()
+ self._teardown_towards(needed_collectors)
+
+ # check if the last collection node has raised an error
+ for col in self.stack:
+ if hasattr(col, '_prepare_exc'):
+ py.builtin._reraise(*col._prepare_exc)
+ for col in needed_collectors[len(self.stack):]:
+ self.stack.append(col)
+ try:
+ col.setup()
+ except TEST_OUTCOME:
+ col._prepare_exc = sys.exc_info()
+ raise
+
+
+def collect_one_node(collector):
+ ihook = collector.ihook
+ ihook.pytest_collectstart(collector=collector)
+ rep = ihook.pytest_make_collect_report(collector=collector)
+ call = rep.__dict__.pop("call", None)
+ if call and check_interactive_exception(call, rep):
+ ihook.pytest_exception_interact(node=collector, call=call, report=rep)
+ return rep
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/setuponly.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/setuponly.py
new file mode 100644
index 00000000000..a1c7457d7e5
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/setuponly.py
@@ -0,0 +1,74 @@
+from __future__ import absolute_import, division, print_function
+
+import pytest
+import sys
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("debugconfig")
+ group.addoption('--setuponly', '--setup-only', action="store_true",
+ help="only setup fixtures, do not execute tests.")
+ group.addoption('--setupshow', '--setup-show', action="store_true",
+ help="show setup of fixtures while executing tests.")
+
+
+@pytest.hookimpl(hookwrapper=True)
+def pytest_fixture_setup(fixturedef, request):
+ yield
+ config = request.config
+ if config.option.setupshow:
+ if hasattr(request, 'param'):
+ # Save the fixture parameter so ._show_fixture_action() can
+ # display it now and during the teardown (in .finish()).
+ if fixturedef.ids:
+ if callable(fixturedef.ids):
+ fixturedef.cached_param = fixturedef.ids(request.param)
+ else:
+ fixturedef.cached_param = fixturedef.ids[
+ request.param_index]
+ else:
+ fixturedef.cached_param = request.param
+ _show_fixture_action(fixturedef, 'SETUP')
+
+
+def pytest_fixture_post_finalizer(fixturedef):
+ if hasattr(fixturedef, "cached_result"):
+ config = fixturedef._fixturemanager.config
+ if config.option.setupshow:
+ _show_fixture_action(fixturedef, 'TEARDOWN')
+ if hasattr(fixturedef, "cached_param"):
+ del fixturedef.cached_param
+
+
+def _show_fixture_action(fixturedef, msg):
+ config = fixturedef._fixturemanager.config
+ capman = config.pluginmanager.getplugin('capturemanager')
+ if capman:
+ out, err = capman.suspend_global_capture()
+
+ tw = config.get_terminal_writer()
+ tw.line()
+ tw.write(' ' * 2 * fixturedef.scopenum)
+ tw.write('{step} {scope} {fixture}'.format(
+ step=msg.ljust(8), # align the output to TEARDOWN
+ scope=fixturedef.scope[0].upper(),
+ fixture=fixturedef.argname))
+
+ if msg == 'SETUP':
+ deps = sorted(arg for arg in fixturedef.argnames if arg != 'request')
+ if deps:
+ tw.write(' (fixtures used: {0})'.format(', '.join(deps)))
+
+ if hasattr(fixturedef, 'cached_param'):
+ tw.write('[{0}]'.format(fixturedef.cached_param))
+
+ if capman:
+ capman.resume_global_capture()
+ sys.stdout.write(out)
+ sys.stderr.write(err)
+
+
+@pytest.hookimpl(tryfirst=True)
+def pytest_cmdline_main(config):
+ if config.option.setuponly:
+ config.option.setupshow = True
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/setupplan.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/setupplan.py
new file mode 100644
index 00000000000..e11bd40698b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/setupplan.py
@@ -0,0 +1,25 @@
+from __future__ import absolute_import, division, print_function
+
+import pytest
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("debugconfig")
+ group.addoption('--setupplan', '--setup-plan', action="store_true",
+ help="show what fixtures and tests would be executed but "
+ "don't execute anything.")
+
+
+@pytest.hookimpl(tryfirst=True)
+def pytest_fixture_setup(fixturedef, request):
+ # Will return a dummy fixture if the setuponly option is provided.
+ if request.config.option.setupplan:
+ fixturedef.cached_result = (None, None, None)
+ return fixturedef.cached_result
+
+
+@pytest.hookimpl(tryfirst=True)
+def pytest_cmdline_main(config):
+ if config.option.setupplan:
+ config.option.setuponly = True
+ config.option.setupshow = True
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/skipping.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/skipping.py
new file mode 100644
index 00000000000..a1e5b43800b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/skipping.py
@@ -0,0 +1,397 @@
+""" support for skip/xfail functions and markers. """
+from __future__ import absolute_import, division, print_function
+
+import os
+import six
+import sys
+import traceback
+
+from _pytest.config import hookimpl
+from _pytest.mark import MarkInfo, MarkDecorator
+from _pytest.outcomes import fail, skip, xfail, TEST_OUTCOME
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("general")
+ group.addoption('--runxfail',
+ action="store_true", dest="runxfail", default=False,
+ help="run tests even if they are marked xfail")
+
+ parser.addini("xfail_strict", "default for the strict parameter of xfail "
+ "markers when not given explicitly (default: "
+ "False)",
+ default=False,
+ type="bool")
+
+
+def pytest_configure(config):
+ if config.option.runxfail:
+ # yay a hack
+ import pytest
+ old = pytest.xfail
+ config._cleanup.append(lambda: setattr(pytest, "xfail", old))
+
+ def nop(*args, **kwargs):
+ pass
+
+ nop.Exception = xfail.Exception
+ setattr(pytest, "xfail", nop)
+
+ config.addinivalue_line("markers",
+ "skip(reason=None): skip the given test function with an optional reason. "
+ "Example: skip(reason=\"no way of currently testing this\") skips the "
+ "test."
+ )
+ config.addinivalue_line("markers",
+ "skipif(condition): skip the given test function if eval(condition) "
+ "results in a True value. Evaluation happens within the "
+ "module global context. Example: skipif('sys.platform == \"win32\"') "
+ "skips the test if we are on the win32 platform. see "
+ "http://pytest.org/latest/skipping.html"
+ )
+ config.addinivalue_line("markers",
+ "xfail(condition, reason=None, run=True, raises=None, strict=False): "
+ "mark the test function as an expected failure if eval(condition) "
+ "has a True value. Optionally specify a reason for better reporting "
+ "and run=False if you don't even want to execute the test function. "
+ "If only specific exception(s) are expected, you can list them in "
+ "raises, and if the test fails in other ways, it will be reported as "
+ "a true failure. See http://pytest.org/latest/skipping.html"
+ )
+
+
+class MarkEvaluator(object):
+ def __init__(self, item, name):
+ self.item = item
+ self._marks = None
+ self._mark = None
+ self._mark_name = name
+
+ def __bool__(self):
+ self._marks = self._get_marks()
+ return bool(self._marks)
+ __nonzero__ = __bool__
+
+ def wasvalid(self):
+ return not hasattr(self, 'exc')
+
+ def _get_marks(self):
+
+ keyword = self.item.keywords.get(self._mark_name)
+ if isinstance(keyword, MarkDecorator):
+ return [keyword.mark]
+ elif isinstance(keyword, MarkInfo):
+ return [x.combined for x in keyword]
+ else:
+ return []
+
+ def invalidraise(self, exc):
+ raises = self.get('raises')
+ if not raises:
+ return
+ return not isinstance(exc, raises)
+
+ def istrue(self):
+ try:
+ return self._istrue()
+ except TEST_OUTCOME:
+ self.exc = sys.exc_info()
+ if isinstance(self.exc[1], SyntaxError):
+ msg = [" " * (self.exc[1].offset + 4) + "^", ]
+ msg.append("SyntaxError: invalid syntax")
+ else:
+ msg = traceback.format_exception_only(*self.exc[:2])
+ fail("Error evaluating %r expression\n"
+ " %s\n"
+ "%s"
+ % (self._mark_name, self.expr, "\n".join(msg)),
+ pytrace=False)
+
+ def _getglobals(self):
+ d = {'os': os, 'sys': sys, 'config': self.item.config}
+ if hasattr(self.item, 'obj'):
+ d.update(self.item.obj.__globals__)
+ return d
+
+ def _istrue(self):
+ if hasattr(self, 'result'):
+ return self.result
+ self._marks = self._get_marks()
+
+ if self._marks:
+ self.result = False
+ for mark in self._marks:
+ self._mark = mark
+ if 'condition' in mark.kwargs:
+ args = (mark.kwargs['condition'],)
+ else:
+ args = mark.args
+
+ for expr in args:
+ self.expr = expr
+ if isinstance(expr, six.string_types):
+ d = self._getglobals()
+ result = cached_eval(self.item.config, expr, d)
+ else:
+ if "reason" not in mark.kwargs:
+ # XXX better be checked at collection time
+ msg = "you need to specify reason=STRING " \
+ "when using booleans as conditions."
+ fail(msg)
+ result = bool(expr)
+ if result:
+ self.result = True
+ self.reason = mark.kwargs.get('reason', None)
+ self.expr = expr
+ return self.result
+
+ if not args:
+ self.result = True
+ self.reason = mark.kwargs.get('reason', None)
+ return self.result
+ return False
+
+ def get(self, attr, default=None):
+ if self._mark is None:
+ return default
+ return self._mark.kwargs.get(attr, default)
+
+ def getexplanation(self):
+ expl = getattr(self, 'reason', None) or self.get('reason', None)
+ if not expl:
+ if not hasattr(self, 'expr'):
+ return ""
+ else:
+ return "condition: " + str(self.expr)
+ return expl
+
+
+@hookimpl(tryfirst=True)
+def pytest_runtest_setup(item):
+ # Check if skip or skipif are specified as pytest marks
+ item._skipped_by_mark = False
+ skipif_info = item.keywords.get('skipif')
+ if isinstance(skipif_info, (MarkInfo, MarkDecorator)):
+ eval_skipif = MarkEvaluator(item, 'skipif')
+ if eval_skipif.istrue():
+ item._skipped_by_mark = True
+ skip(eval_skipif.getexplanation())
+
+ skip_info = item.keywords.get('skip')
+ if isinstance(skip_info, (MarkInfo, MarkDecorator)):
+ item._skipped_by_mark = True
+ if 'reason' in skip_info.kwargs:
+ skip(skip_info.kwargs['reason'])
+ elif skip_info.args:
+ skip(skip_info.args[0])
+ else:
+ skip("unconditional skip")
+
+ item._evalxfail = MarkEvaluator(item, 'xfail')
+ check_xfail_no_run(item)
+
+
+@hookimpl(hookwrapper=True)
+def pytest_pyfunc_call(pyfuncitem):
+ check_xfail_no_run(pyfuncitem)
+ outcome = yield
+ passed = outcome.excinfo is None
+ if passed:
+ check_strict_xfail(pyfuncitem)
+
+
+def check_xfail_no_run(item):
+ """check xfail(run=False)"""
+ if not item.config.option.runxfail:
+ evalxfail = item._evalxfail
+ if evalxfail.istrue():
+ if not evalxfail.get('run', True):
+ xfail("[NOTRUN] " + evalxfail.getexplanation())
+
+
+def check_strict_xfail(pyfuncitem):
+ """check xfail(strict=True) for the given PASSING test"""
+ evalxfail = pyfuncitem._evalxfail
+ if evalxfail.istrue():
+ strict_default = pyfuncitem.config.getini('xfail_strict')
+ is_strict_xfail = evalxfail.get('strict', strict_default)
+ if is_strict_xfail:
+ del pyfuncitem._evalxfail
+ explanation = evalxfail.getexplanation()
+ fail('[XPASS(strict)] ' + explanation, pytrace=False)
+
+
+@hookimpl(hookwrapper=True)
+def pytest_runtest_makereport(item, call):
+ outcome = yield
+ rep = outcome.get_result()
+ evalxfail = getattr(item, '_evalxfail', None)
+ # unitttest special case, see setting of _unexpectedsuccess
+ if hasattr(item, '_unexpectedsuccess') and rep.when == "call":
+ from _pytest.compat import _is_unittest_unexpected_success_a_failure
+ if item._unexpectedsuccess:
+ rep.longrepr = "Unexpected success: {0}".format(item._unexpectedsuccess)
+ else:
+ rep.longrepr = "Unexpected success"
+ if _is_unittest_unexpected_success_a_failure():
+ rep.outcome = "failed"
+ else:
+ rep.outcome = "passed"
+ rep.wasxfail = rep.longrepr
+ elif item.config.option.runxfail:
+ pass # don't interefere
+ elif call.excinfo and call.excinfo.errisinstance(xfail.Exception):
+ rep.wasxfail = "reason: " + call.excinfo.value.msg
+ rep.outcome = "skipped"
+ elif evalxfail and not rep.skipped and evalxfail.wasvalid() and \
+ evalxfail.istrue():
+ if call.excinfo:
+ if evalxfail.invalidraise(call.excinfo.value):
+ rep.outcome = "failed"
+ else:
+ rep.outcome = "skipped"
+ rep.wasxfail = evalxfail.getexplanation()
+ elif call.when == "call":
+ strict_default = item.config.getini('xfail_strict')
+ is_strict_xfail = evalxfail.get('strict', strict_default)
+ explanation = evalxfail.getexplanation()
+ if is_strict_xfail:
+ rep.outcome = "failed"
+ rep.longrepr = "[XPASS(strict)] {0}".format(explanation)
+ else:
+ rep.outcome = "passed"
+ rep.wasxfail = explanation
+ elif item._skipped_by_mark and rep.skipped and type(rep.longrepr) is tuple:
+ # skipped by mark.skipif; change the location of the failure
+ # to point to the item definition, otherwise it will display
+ # the location of where the skip exception was raised within pytest
+ filename, line, reason = rep.longrepr
+ filename, line = item.location[:2]
+ rep.longrepr = filename, line, reason
+
+# called by terminalreporter progress reporting
+
+
+def pytest_report_teststatus(report):
+ if hasattr(report, "wasxfail"):
+ if report.skipped:
+ return "xfailed", "x", "xfail"
+ elif report.passed:
+ return "xpassed", "X", ("XPASS", {'yellow': True})
+
+# called by the terminalreporter instance/plugin
+
+
+def pytest_terminal_summary(terminalreporter):
+ tr = terminalreporter
+ if not tr.reportchars:
+ # for name in "xfailed skipped failed xpassed":
+ # if not tr.stats.get(name, 0):
+ # tr.write_line("HINT: use '-r' option to see extra "
+ # "summary info about tests")
+ # break
+ return
+
+ lines = []
+ for char in tr.reportchars:
+ if char == "x":
+ show_xfailed(terminalreporter, lines)
+ elif char == "X":
+ show_xpassed(terminalreporter, lines)
+ elif char in "fF":
+ show_simple(terminalreporter, lines, 'failed', "FAIL %s")
+ elif char in "sS":
+ show_skipped(terminalreporter, lines)
+ elif char == "E":
+ show_simple(terminalreporter, lines, 'error', "ERROR %s")
+ elif char == 'p':
+ show_simple(terminalreporter, lines, 'passed', "PASSED %s")
+
+ if lines:
+ tr._tw.sep("=", "short test summary info")
+ for line in lines:
+ tr._tw.line(line)
+
+
+def show_simple(terminalreporter, lines, stat, format):
+ failed = terminalreporter.stats.get(stat)
+ if failed:
+ for rep in failed:
+ pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid)
+ lines.append(format % (pos,))
+
+
+def show_xfailed(terminalreporter, lines):
+ xfailed = terminalreporter.stats.get("xfailed")
+ if xfailed:
+ for rep in xfailed:
+ pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid)
+ reason = rep.wasxfail
+ lines.append("XFAIL %s" % (pos,))
+ if reason:
+ lines.append(" " + str(reason))
+
+
+def show_xpassed(terminalreporter, lines):
+ xpassed = terminalreporter.stats.get("xpassed")
+ if xpassed:
+ for rep in xpassed:
+ pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid)
+ reason = rep.wasxfail
+ lines.append("XPASS %s %s" % (pos, reason))
+
+
+def cached_eval(config, expr, d):
+ if not hasattr(config, '_evalcache'):
+ config._evalcache = {}
+ try:
+ return config._evalcache[expr]
+ except KeyError:
+ import _pytest._code
+ exprcode = _pytest._code.compile(expr, mode="eval")
+ config._evalcache[expr] = x = eval(exprcode, d)
+ return x
+
+
+def folded_skips(skipped):
+ d = {}
+ for event in skipped:
+ key = event.longrepr
+ assert len(key) == 3, (event, key)
+ keywords = getattr(event, 'keywords', {})
+ # folding reports with global pytestmark variable
+ # this is workaround, because for now we cannot identify the scope of a skip marker
+ # TODO: revisit after marks scope would be fixed
+ when = getattr(event, 'when', None)
+ if when == 'setup' and 'skip' in keywords and 'pytestmark' not in keywords:
+ key = (key[0], None, key[2], )
+ d.setdefault(key, []).append(event)
+ values = []
+ for key, events in d.items():
+ values.append((len(events),) + key)
+ return values
+
+
+def show_skipped(terminalreporter, lines):
+ tr = terminalreporter
+ skipped = tr.stats.get('skipped', [])
+ if skipped:
+ # if not tr.hasopt('skipped'):
+ # tr.write_line(
+ # "%d skipped tests, specify -rs for more info" %
+ # len(skipped))
+ # return
+ fskips = folded_skips(skipped)
+ if fskips:
+ # tr.write_sep("_", "skipped test summary")
+ for num, fspath, lineno, reason in fskips:
+ if reason.startswith("Skipped: "):
+ reason = reason[9:]
+ if lineno is not None:
+ lines.append(
+ "SKIP [%d] %s:%d: %s" %
+ (num, fspath, lineno + 1, reason))
+ else:
+ lines.append(
+ "SKIP [%d] %s: %s" %
+ (num, fspath, reason))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/terminal.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/terminal.py
new file mode 100644
index 00000000000..1aba5e845e5
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/terminal.py
@@ -0,0 +1,703 @@
+""" terminal reporting of the full testing process.
+
+This is a good source for looking at the various reporting hooks.
+"""
+from __future__ import absolute_import, division, print_function
+
+import itertools
+import platform
+import sys
+import time
+
+import pluggy
+import py
+import six
+
+import pytest
+from _pytest import nodes
+from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \
+ EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("terminal reporting", "reporting", after="general")
+ group._addoption('-v', '--verbose', action="count",
+ dest="verbose", default=0, help="increase verbosity."),
+ group._addoption('-q', '--quiet', action="count",
+ dest="quiet", default=0, help="decrease verbosity."),
+ group._addoption('-r',
+ action="store", dest="reportchars", default='', metavar="chars",
+ help="show extra test summary info as specified by chars (f)ailed, "
+ "(E)error, (s)skipped, (x)failed, (X)passed, "
+ "(p)passed, (P)passed with output, (a)all except pP. "
+ "Warnings are displayed at all times except when "
+ "--disable-warnings is set")
+ group._addoption('--disable-warnings', '--disable-pytest-warnings', default=False,
+ dest='disable_warnings', action='store_true',
+ help='disable warnings summary')
+ group._addoption('-l', '--showlocals',
+ action="store_true", dest="showlocals", default=False,
+ help="show locals in tracebacks (disabled by default).")
+ group._addoption('--tb', metavar="style",
+ action="store", dest="tbstyle", default='auto',
+ choices=['auto', 'long', 'short', 'no', 'line', 'native'],
+ help="traceback print mode (auto/long/short/line/native/no).")
+ group._addoption('--fulltrace', '--full-trace',
+ action="store_true", default=False,
+ help="don't cut any tracebacks (default is to cut).")
+ group._addoption('--color', metavar="color",
+ action="store", dest="color", default='auto',
+ choices=['yes', 'no', 'auto'],
+ help="color terminal output (yes/no/auto).")
+
+ parser.addini("console_output_style",
+ help="console output: classic or with additional progress information (classic|progress).",
+ default='progress')
+
+
+def pytest_configure(config):
+ config.option.verbose -= config.option.quiet
+ reporter = TerminalReporter(config, sys.stdout)
+ config.pluginmanager.register(reporter, 'terminalreporter')
+ if config.option.debug or config.option.traceconfig:
+ def mywriter(tags, args):
+ msg = " ".join(map(str, args))
+ reporter.write_line("[traceconfig] " + msg)
+ config.trace.root.setprocessor("pytest:config", mywriter)
+
+
+def getreportopt(config):
+ reportopts = ""
+ reportchars = config.option.reportchars
+ if not config.option.disable_warnings and 'w' not in reportchars:
+ reportchars += 'w'
+ elif config.option.disable_warnings and 'w' in reportchars:
+ reportchars = reportchars.replace('w', '')
+ if reportchars:
+ for char in reportchars:
+ if char not in reportopts and char != 'a':
+ reportopts += char
+ elif char == 'a':
+ reportopts = 'fEsxXw'
+ return reportopts
+
+
+def pytest_report_teststatus(report):
+ if report.passed:
+ letter = "."
+ elif report.skipped:
+ letter = "s"
+ elif report.failed:
+ letter = "F"
+ if report.when != "call":
+ letter = "f"
+ return report.outcome, letter, report.outcome.upper()
+
+
+class WarningReport:
+ """
+ Simple structure to hold warnings information captured by ``pytest_logwarning``.
+ """
+
+ def __init__(self, code, message, nodeid=None, fslocation=None):
+ """
+ :param code: unused
+ :param str message: user friendly message about the warning
+ :param str|None nodeid: node id that generated the warning (see ``get_location``).
+ :param tuple|py.path.local fslocation:
+ file system location of the source of the warning (see ``get_location``).
+ """
+ self.code = code
+ self.message = message
+ self.nodeid = nodeid
+ self.fslocation = fslocation
+
+ def get_location(self, config):
+ """
+ Returns the more user-friendly information about the location
+ of a warning, or None.
+ """
+ if self.nodeid:
+ return self.nodeid
+ if self.fslocation:
+ if isinstance(self.fslocation, tuple) and len(self.fslocation) >= 2:
+ filename, linenum = self.fslocation[:2]
+ relpath = py.path.local(filename).relto(config.invocation_dir)
+ return '%s:%s' % (relpath, linenum)
+ else:
+ return str(self.fslocation)
+ return None
+
+
+class TerminalReporter:
+ def __init__(self, config, file=None):
+ import _pytest.config
+ self.config = config
+ self.verbosity = self.config.option.verbose
+ self.showheader = self.verbosity >= 0
+ self.showfspath = self.verbosity >= 0
+ self.showlongtestinfo = self.verbosity > 0
+ self._numcollected = 0
+ self._session = None
+
+ self.stats = {}
+ self.startdir = py.path.local()
+ if file is None:
+ file = sys.stdout
+ self._tw = _pytest.config.create_terminal_writer(config, file)
+ # self.writer will be deprecated in pytest-3.4
+ self.writer = self._tw
+ self._screen_width = self._tw.fullwidth
+ self.currentfspath = None
+ self.reportchars = getreportopt(config)
+ self.hasmarkup = self._tw.hasmarkup
+ self.isatty = file.isatty()
+ self._progress_items_reported = 0
+ self._show_progress_info = self.config.getini('console_output_style') == 'progress'
+
+ def hasopt(self, char):
+ char = {'xfailed': 'x', 'skipped': 's'}.get(char, char)
+ return char in self.reportchars
+
+ def write_fspath_result(self, nodeid, res):
+ fspath = self.config.rootdir.join(nodeid.split("::")[0])
+ if fspath != self.currentfspath:
+ if self.currentfspath is not None:
+ self._write_progress_information_filling_space()
+ self.currentfspath = fspath
+ fspath = self.startdir.bestrelpath(fspath)
+ self._tw.line()
+ self._tw.write(fspath + " ")
+ self._tw.write(res)
+
+ def write_ensure_prefix(self, prefix, extra="", **kwargs):
+ if self.currentfspath != prefix:
+ self._tw.line()
+ self.currentfspath = prefix
+ self._tw.write(prefix)
+ if extra:
+ self._tw.write(extra, **kwargs)
+ self.currentfspath = -2
+ self._write_progress_information_filling_space()
+
+ def ensure_newline(self):
+ if self.currentfspath:
+ self._tw.line()
+ self.currentfspath = None
+
+ def write(self, content, **markup):
+ self._tw.write(content, **markup)
+
+ def write_line(self, line, **markup):
+ if not isinstance(line, six.text_type):
+ line = six.text_type(line, errors="replace")
+ self.ensure_newline()
+ self._tw.line(line, **markup)
+
+ def rewrite(self, line, **markup):
+ """
+ Rewinds the terminal cursor to the beginning and writes the given line.
+
+ :kwarg erase: if True, will also add spaces until the full terminal width to ensure
+ previous lines are properly erased.
+
+ The rest of the keyword arguments are markup instructions.
+ """
+ erase = markup.pop('erase', False)
+ if erase:
+ fill_count = self._tw.fullwidth - len(line) - 1
+ fill = ' ' * fill_count
+ else:
+ fill = ''
+ line = str(line)
+ self._tw.write("\r" + line + fill, **markup)
+
+ def write_sep(self, sep, title=None, **markup):
+ self.ensure_newline()
+ self._tw.sep(sep, title, **markup)
+
+ def section(self, title, sep="=", **kw):
+ self._tw.sep(sep, title, **kw)
+
+ def line(self, msg, **kw):
+ self._tw.line(msg, **kw)
+
+ def pytest_internalerror(self, excrepr):
+ for line in six.text_type(excrepr).split("\n"):
+ self.write_line("INTERNALERROR> " + line)
+ return 1
+
+ def pytest_logwarning(self, code, fslocation, message, nodeid):
+ warnings = self.stats.setdefault("warnings", [])
+ warning = WarningReport(code=code, fslocation=fslocation,
+ message=message, nodeid=nodeid)
+ warnings.append(warning)
+
+ def pytest_plugin_registered(self, plugin):
+ if self.config.option.traceconfig:
+ msg = "PLUGIN registered: %s" % (plugin,)
+ # XXX this event may happen during setup/teardown time
+ # which unfortunately captures our output here
+ # which garbles our output if we use self.write_line
+ self.write_line(msg)
+
+ def pytest_deselected(self, items):
+ self.stats.setdefault('deselected', []).extend(items)
+
+ def pytest_runtest_logstart(self, nodeid, location):
+ # ensure that the path is printed before the
+ # 1st test of a module starts running
+ if self.showlongtestinfo:
+ line = self._locationline(nodeid, *location)
+ self.write_ensure_prefix(line, "")
+ elif self.showfspath:
+ fsid = nodeid.split("::")[0]
+ self.write_fspath_result(fsid, "")
+
+ def pytest_runtest_logreport(self, report):
+ rep = report
+ res = self.config.hook.pytest_report_teststatus(report=rep)
+ cat, letter, word = res
+ if isinstance(word, tuple):
+ word, markup = word
+ else:
+ markup = None
+ self.stats.setdefault(cat, []).append(rep)
+ self._tests_ran = True
+ if not letter and not word:
+ # probably passed setup/teardown
+ return
+ running_xdist = hasattr(rep, 'node')
+ self._progress_items_reported += 1
+ if self.verbosity <= 0:
+ if not running_xdist and self.showfspath:
+ self.write_fspath_result(rep.nodeid, letter)
+ else:
+ self._tw.write(letter)
+ self._write_progress_if_past_edge()
+ else:
+ if markup is None:
+ if rep.passed:
+ markup = {'green': True}
+ elif rep.failed:
+ markup = {'red': True}
+ elif rep.skipped:
+ markup = {'yellow': True}
+ else:
+ markup = {}
+ line = self._locationline(rep.nodeid, *rep.location)
+ if not running_xdist:
+ self.write_ensure_prefix(line, word, **markup)
+ else:
+ self.ensure_newline()
+ self._tw.write("[%s]" % rep.node.gateway.id)
+ if self._show_progress_info:
+ self._tw.write(self._get_progress_information_message() + " ", cyan=True)
+ else:
+ self._tw.write(' ')
+ self._tw.write(word, **markup)
+ self._tw.write(" " + line)
+ self.currentfspath = -2
+
+ def _write_progress_if_past_edge(self):
+ if not self._show_progress_info:
+ return
+ last_item = self._progress_items_reported == self._session.testscollected
+ if last_item:
+ self._write_progress_information_filling_space()
+ return
+
+ past_edge = self._tw.chars_on_current_line + self._PROGRESS_LENGTH + 1 >= self._screen_width
+ if past_edge:
+ msg = self._get_progress_information_message()
+ self._tw.write(msg + '\n', cyan=True)
+
+ _PROGRESS_LENGTH = len(' [100%]')
+
+ def _get_progress_information_message(self):
+ collected = self._session.testscollected
+ if collected:
+ progress = self._progress_items_reported * 100 // collected
+ return ' [{:3d}%]'.format(progress)
+ return ' [100%]'
+
+ def _write_progress_information_filling_space(self):
+ if not self._show_progress_info:
+ return
+ msg = self._get_progress_information_message()
+ fill = ' ' * (self._tw.fullwidth - self._tw.chars_on_current_line - len(msg) - 1)
+ self.write(fill + msg, cyan=True)
+
+ def pytest_collection(self):
+ if not self.isatty and self.config.option.verbose >= 1:
+ self.write("collecting ... ", bold=True)
+
+ def pytest_collectreport(self, report):
+ if report.failed:
+ self.stats.setdefault("error", []).append(report)
+ elif report.skipped:
+ self.stats.setdefault("skipped", []).append(report)
+ items = [x for x in report.result if isinstance(x, pytest.Item)]
+ self._numcollected += len(items)
+ if self.isatty:
+ # self.write_fspath_result(report.nodeid, 'E')
+ self.report_collect()
+
+ def report_collect(self, final=False):
+ if self.config.option.verbose < 0:
+ return
+
+ errors = len(self.stats.get('error', []))
+ skipped = len(self.stats.get('skipped', []))
+ if final:
+ line = "collected "
+ else:
+ line = "collecting "
+ line += str(self._numcollected) + " item" + ('' if self._numcollected == 1 else 's')
+ if errors:
+ line += " / %d errors" % errors
+ if skipped:
+ line += " / %d skipped" % skipped
+ if self.isatty:
+ self.rewrite(line, bold=True, erase=True)
+ if final:
+ self.write('\n')
+ else:
+ self.write_line(line)
+
+ def pytest_collection_modifyitems(self):
+ self.report_collect(True)
+
+ @pytest.hookimpl(trylast=True)
+ def pytest_sessionstart(self, session):
+ self._session = session
+ self._sessionstarttime = time.time()
+ if not self.showheader:
+ return
+ self.write_sep("=", "test session starts", bold=True)
+ verinfo = platform.python_version()
+ msg = "platform %s -- Python %s" % (sys.platform, verinfo)
+ if hasattr(sys, 'pypy_version_info'):
+ verinfo = ".".join(map(str, sys.pypy_version_info[:3]))
+ msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3])
+ msg += ", pytest-%s, py-%s, pluggy-%s" % (
+ pytest.__version__, py.__version__, pluggy.__version__)
+ if self.verbosity > 0 or self.config.option.debug or \
+ getattr(self.config.option, 'pastebin', None):
+ msg += " -- " + str(sys.executable)
+ self.write_line(msg)
+ lines = self.config.hook.pytest_report_header(
+ config=self.config, startdir=self.startdir)
+ self._write_report_lines_from_hooks(lines)
+
+ def _write_report_lines_from_hooks(self, lines):
+ lines.reverse()
+ for line in flatten(lines):
+ self.write_line(line)
+
+ def pytest_report_header(self, config):
+ inifile = ""
+ if config.inifile:
+ inifile = " " + config.rootdir.bestrelpath(config.inifile)
+ lines = ["rootdir: %s, inifile:%s" % (config.rootdir, inifile)]
+
+ plugininfo = config.pluginmanager.list_plugin_distinfo()
+ if plugininfo:
+
+ lines.append(
+ "plugins: %s" % ", ".join(_plugin_nameversions(plugininfo)))
+ return lines
+
+ def pytest_collection_finish(self, session):
+ if self.config.option.collectonly:
+ self._printcollecteditems(session.items)
+ if self.stats.get('failed'):
+ self._tw.sep("!", "collection failures")
+ for rep in self.stats.get('failed'):
+ rep.toterminal(self._tw)
+ return 1
+ return 0
+ lines = self.config.hook.pytest_report_collectionfinish(
+ config=self.config, startdir=self.startdir, items=session.items)
+ self._write_report_lines_from_hooks(lines)
+
+ def _printcollecteditems(self, items):
+ # to print out items and their parent collectors
+ # we take care to leave out Instances aka ()
+ # because later versions are going to get rid of them anyway
+ if self.config.option.verbose < 0:
+ if self.config.option.verbose < -1:
+ counts = {}
+ for item in items:
+ name = item.nodeid.split('::', 1)[0]
+ counts[name] = counts.get(name, 0) + 1
+ for name, count in sorted(counts.items()):
+ self._tw.line("%s: %d" % (name, count))
+ else:
+ for item in items:
+ nodeid = item.nodeid
+ nodeid = nodeid.replace("::()::", "::")
+ self._tw.line(nodeid)
+ return
+ stack = []
+ indent = ""
+ for item in items:
+ needed_collectors = item.listchain()[1:] # strip root node
+ while stack:
+ if stack == needed_collectors[:len(stack)]:
+ break
+ stack.pop()
+ for col in needed_collectors[len(stack):]:
+ stack.append(col)
+ # if col.name == "()":
+ # continue
+ indent = (len(stack) - 1) * " "
+ self._tw.line("%s%s" % (indent, col))
+
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_sessionfinish(self, exitstatus):
+ outcome = yield
+ outcome.get_result()
+ self._tw.line("")
+ summary_exit_codes = (
+ EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, EXIT_USAGEERROR,
+ EXIT_NOTESTSCOLLECTED)
+ if exitstatus in summary_exit_codes:
+ self.config.hook.pytest_terminal_summary(terminalreporter=self,
+ exitstatus=exitstatus)
+ self.summary_errors()
+ self.summary_failures()
+ self.summary_warnings()
+ self.summary_passes()
+ if exitstatus == EXIT_INTERRUPTED:
+ self._report_keyboardinterrupt()
+ del self._keyboardinterrupt_memo
+ self.summary_deselected()
+ self.summary_stats()
+
+ def pytest_keyboard_interrupt(self, excinfo):
+ self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True)
+
+ def pytest_unconfigure(self):
+ if hasattr(self, '_keyboardinterrupt_memo'):
+ self._report_keyboardinterrupt()
+
+ def _report_keyboardinterrupt(self):
+ excrepr = self._keyboardinterrupt_memo
+ msg = excrepr.reprcrash.message
+ self.write_sep("!", msg)
+ if "KeyboardInterrupt" in msg:
+ if self.config.option.fulltrace:
+ excrepr.toterminal(self._tw)
+ else:
+ self._tw.line("to show a full traceback on KeyboardInterrupt use --fulltrace", yellow=True)
+ excrepr.reprcrash.toterminal(self._tw)
+
+ def _locationline(self, nodeid, fspath, lineno, domain):
+ def mkrel(nodeid):
+ line = self.config.cwd_relative_nodeid(nodeid)
+ if domain and line.endswith(domain):
+ line = line[:-len(domain)]
+ values = domain.split("[")
+ values[0] = values[0].replace('.', '::') # don't replace '.' in params
+ line += "[".join(values)
+ return line
+ # collect_fspath comes from testid which has a "/"-normalized path
+
+ if fspath:
+ res = mkrel(nodeid).replace("::()", "") # parens-normalization
+ if nodeid.split("::")[0] != fspath.replace("\\", nodes.SEP):
+ res += " <- " + self.startdir.bestrelpath(fspath)
+ else:
+ res = "[location]"
+ return res + " "
+
+ def _getfailureheadline(self, rep):
+ if hasattr(rep, 'location'):
+ fspath, lineno, domain = rep.location
+ return domain
+ else:
+ return "test session" # XXX?
+
+ def _getcrashline(self, rep):
+ try:
+ return str(rep.longrepr.reprcrash)
+ except AttributeError:
+ try:
+ return str(rep.longrepr)[:50]
+ except AttributeError:
+ return ""
+
+ #
+ # summaries for sessionfinish
+ #
+ def getreports(self, name):
+ values = []
+ for x in self.stats.get(name, []):
+ if not hasattr(x, '_pdbshown'):
+ values.append(x)
+ return values
+
+ def summary_warnings(self):
+ if self.hasopt("w"):
+ all_warnings = self.stats.get("warnings")
+ if not all_warnings:
+ return
+
+ grouped = itertools.groupby(all_warnings, key=lambda wr: wr.get_location(self.config))
+
+ self.write_sep("=", "warnings summary", yellow=True, bold=False)
+ for location, warning_records in grouped:
+ self._tw.line(str(location) or '<undetermined location>')
+ for w in warning_records:
+ lines = w.message.splitlines()
+ indented = '\n'.join(' ' + x for x in lines)
+ self._tw.line(indented)
+ self._tw.line()
+ self._tw.line('-- Docs: http://doc.pytest.org/en/latest/warnings.html')
+
+ def summary_passes(self):
+ if self.config.option.tbstyle != "no":
+ if self.hasopt("P"):
+ reports = self.getreports('passed')
+ if not reports:
+ return
+ self.write_sep("=", "PASSES")
+ for rep in reports:
+ msg = self._getfailureheadline(rep)
+ self.write_sep("_", msg)
+ self._outrep_summary(rep)
+
+ def print_teardown_sections(self, rep):
+ for secname, content in rep.sections:
+ if 'teardown' in secname:
+ self._tw.sep('-', secname)
+ if content[-1:] == "\n":
+ content = content[:-1]
+ self._tw.line(content)
+
+ def summary_failures(self):
+ if self.config.option.tbstyle != "no":
+ reports = self.getreports('failed')
+ if not reports:
+ return
+ self.write_sep("=", "FAILURES")
+ for rep in reports:
+ if self.config.option.tbstyle == "line":
+ line = self._getcrashline(rep)
+ self.write_line(line)
+ else:
+ msg = self._getfailureheadline(rep)
+ markup = {'red': True, 'bold': True}
+ self.write_sep("_", msg, **markup)
+ self._outrep_summary(rep)
+ for report in self.getreports(''):
+ if report.nodeid == rep.nodeid and report.when == 'teardown':
+ self.print_teardown_sections(report)
+
+ def summary_errors(self):
+ if self.config.option.tbstyle != "no":
+ reports = self.getreports('error')
+ if not reports:
+ return
+ self.write_sep("=", "ERRORS")
+ for rep in self.stats['error']:
+ msg = self._getfailureheadline(rep)
+ if not hasattr(rep, 'when'):
+ # collect
+ msg = "ERROR collecting " + msg
+ elif rep.when == "setup":
+ msg = "ERROR at setup of " + msg
+ elif rep.when == "teardown":
+ msg = "ERROR at teardown of " + msg
+ self.write_sep("_", msg)
+ self._outrep_summary(rep)
+
+ def _outrep_summary(self, rep):
+ rep.toterminal(self._tw)
+ for secname, content in rep.sections:
+ self._tw.sep("-", secname)
+ if content[-1:] == "\n":
+ content = content[:-1]
+ self._tw.line(content)
+
+ def summary_stats(self):
+ session_duration = time.time() - self._sessionstarttime
+ (line, color) = build_summary_stats_line(self.stats)
+ msg = "%s in %.2f seconds" % (line, session_duration)
+ markup = {color: True, 'bold': True}
+
+ if self.verbosity >= 0:
+ self.write_sep("=", msg, **markup)
+ if self.verbosity == -1:
+ self.write_line(msg, **markup)
+
+ def summary_deselected(self):
+ if 'deselected' in self.stats:
+ self.write_sep("=", "%d tests deselected" % (
+ len(self.stats['deselected'])), bold=True)
+
+
+def repr_pythonversion(v=None):
+ if v is None:
+ v = sys.version_info
+ try:
+ return "%s.%s.%s-%s-%s" % v
+ except (TypeError, ValueError):
+ return str(v)
+
+
+def flatten(values):
+ for x in values:
+ if isinstance(x, (list, tuple)):
+ for y in flatten(x):
+ yield y
+ else:
+ yield x
+
+
+def build_summary_stats_line(stats):
+ keys = ("failed passed skipped deselected "
+ "xfailed xpassed warnings error").split()
+ unknown_key_seen = False
+ for key in stats.keys():
+ if key not in keys:
+ if key: # setup/teardown reports have an empty key, ignore them
+ keys.append(key)
+ unknown_key_seen = True
+ parts = []
+ for key in keys:
+ val = stats.get(key, None)
+ if val:
+ parts.append("%d %s" % (len(val), key))
+
+ if parts:
+ line = ", ".join(parts)
+ else:
+ line = "no tests ran"
+
+ if 'failed' in stats or 'error' in stats:
+ color = 'red'
+ elif 'warnings' in stats or unknown_key_seen:
+ color = 'yellow'
+ elif 'passed' in stats:
+ color = 'green'
+ else:
+ color = 'yellow'
+
+ return (line, color)
+
+
+def _plugin_nameversions(plugininfo):
+ values = []
+ for plugin, dist in plugininfo:
+ # gets us name and version!
+ name = '{dist.project_name}-{dist.version}'.format(dist=dist)
+ # questionable convenience, but it keeps things short
+ if name.startswith("pytest-"):
+ name = name[7:]
+ # we decided to print python package names
+ # they can have more than one plugin
+ if name not in values:
+ values.append(name)
+ return values
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/tmpdir.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/tmpdir.py
new file mode 100644
index 00000000000..da1b032237a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/tmpdir.py
@@ -0,0 +1,126 @@
+""" support for providing temporary directories to test functions. """
+from __future__ import absolute_import, division, print_function
+
+import re
+
+import pytest
+import py
+from _pytest.monkeypatch import MonkeyPatch
+
+
+class TempdirFactory:
+ """Factory for temporary directories under the common base temp directory.
+
+ The base directory can be configured using the ``--basetemp`` option.
+ """
+
+ def __init__(self, config):
+ self.config = config
+ self.trace = config.trace.get("tmpdir")
+
+ def ensuretemp(self, string, dir=1):
+ """ (deprecated) return temporary directory path with
+ the given string as the trailing part. It is usually
+ better to use the 'tmpdir' function argument which
+ provides an empty unique-per-test-invocation directory
+ and is guaranteed to be empty.
+ """
+ # py.log._apiwarn(">1.1", "use tmpdir function argument")
+ return self.getbasetemp().ensure(string, dir=dir)
+
+ def mktemp(self, basename, numbered=True):
+ """Create a subdirectory of the base temporary directory and return it.
+ If ``numbered``, ensure the directory is unique by adding a number
+ prefix greater than any existing one.
+ """
+ basetemp = self.getbasetemp()
+ if not numbered:
+ p = basetemp.mkdir(basename)
+ else:
+ p = py.path.local.make_numbered_dir(prefix=basename,
+ keep=0, rootdir=basetemp, lock_timeout=None)
+ self.trace("mktemp", p)
+ return p
+
+ def getbasetemp(self):
+ """ return base temporary directory. """
+ try:
+ return self._basetemp
+ except AttributeError:
+ basetemp = self.config.option.basetemp
+ if basetemp:
+ basetemp = py.path.local(basetemp)
+ if basetemp.check():
+ basetemp.remove()
+ basetemp.mkdir()
+ else:
+ temproot = py.path.local.get_temproot()
+ user = get_user()
+ if user:
+ # use a sub-directory in the temproot to speed-up
+ # make_numbered_dir() call
+ rootdir = temproot.join('pytest-of-%s' % user)
+ else:
+ rootdir = temproot
+ rootdir.ensure(dir=1)
+ basetemp = py.path.local.make_numbered_dir(prefix='pytest-',
+ rootdir=rootdir)
+ self._basetemp = t = basetemp.realpath()
+ self.trace("new basetemp", t)
+ return t
+
+ def finish(self):
+ self.trace("finish")
+
+
+def get_user():
+ """Return the current user name, or None if getuser() does not work
+ in the current environment (see #1010).
+ """
+ import getpass
+ try:
+ return getpass.getuser()
+ except (ImportError, KeyError):
+ return None
+
+
+# backward compatibility
+TempdirHandler = TempdirFactory
+
+
+def pytest_configure(config):
+ """Create a TempdirFactory and attach it to the config object.
+
+ This is to comply with existing plugins which expect the handler to be
+ available at pytest_configure time, but ideally should be moved entirely
+ to the tmpdir_factory session fixture.
+ """
+ mp = MonkeyPatch()
+ t = TempdirFactory(config)
+ config._cleanup.extend([mp.undo, t.finish])
+ mp.setattr(config, '_tmpdirhandler', t, raising=False)
+ mp.setattr(pytest, 'ensuretemp', t.ensuretemp, raising=False)
+
+
+@pytest.fixture(scope='session')
+def tmpdir_factory(request):
+ """Return a TempdirFactory instance for the test session.
+ """
+ return request.config._tmpdirhandler
+
+
+@pytest.fixture
+def tmpdir(request, tmpdir_factory):
+ """Return a temporary directory path object
+ which is unique to each test function invocation,
+ created as a sub directory of the base temporary
+ directory. The returned object is a `py.path.local`_
+ path object.
+ """
+ name = request.node.name
+ name = re.sub(r"[\W]", "_", name)
+ MAXVAL = 30
+ if len(name) > MAXVAL:
+ name = name[:MAXVAL]
+ x = tmpdir_factory.mktemp(name, numbered=True)
+ return x
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/unittest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/unittest.py
new file mode 100644
index 00000000000..3ddb39495e3
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/unittest.py
@@ -0,0 +1,237 @@
+""" discovery and running of std-library "unittest" style tests. """
+from __future__ import absolute_import, division, print_function
+
+import sys
+import traceback
+
+# for transferring markers
+import _pytest._code
+from _pytest.config import hookimpl
+from _pytest.outcomes import fail, skip, xfail
+from _pytest.python import transfer_markers, Class, Module, Function
+
+
+def pytest_pycollect_makeitem(collector, name, obj):
+ # has unittest been imported and is obj a subclass of its TestCase?
+ try:
+ if not issubclass(obj, sys.modules["unittest"].TestCase):
+ return
+ except Exception:
+ return
+ # yes, so let's collect it
+ return UnitTestCase(name, parent=collector)
+
+
+class UnitTestCase(Class):
+ # marker for fixturemanger.getfixtureinfo()
+ # to declare that our children do not support funcargs
+ nofuncargs = True
+
+ def setup(self):
+ cls = self.obj
+ if getattr(cls, '__unittest_skip__', False):
+ return # skipped
+ setup = getattr(cls, 'setUpClass', None)
+ if setup is not None:
+ setup()
+ teardown = getattr(cls, 'tearDownClass', None)
+ if teardown is not None:
+ self.addfinalizer(teardown)
+ super(UnitTestCase, self).setup()
+
+ def collect(self):
+ from unittest import TestLoader
+ cls = self.obj
+ if not getattr(cls, "__test__", True):
+ return
+ self.session._fixturemanager.parsefactories(self, unittest=True)
+ loader = TestLoader()
+ module = self.getparent(Module).obj
+ foundsomething = False
+ for name in loader.getTestCaseNames(self.obj):
+ x = getattr(self.obj, name)
+ if not getattr(x, '__test__', True):
+ continue
+ funcobj = getattr(x, 'im_func', x)
+ transfer_markers(funcobj, cls, module)
+ yield TestCaseFunction(name, parent=self)
+ foundsomething = True
+
+ if not foundsomething:
+ runtest = getattr(self.obj, 'runTest', None)
+ if runtest is not None:
+ ut = sys.modules.get("twisted.trial.unittest", None)
+ if ut is None or runtest != ut.TestCase.runTest:
+ yield TestCaseFunction('runTest', parent=self)
+
+
+class TestCaseFunction(Function):
+ _excinfo = None
+
+ def setup(self):
+ self._testcase = self.parent.obj(self.name)
+ self._fix_unittest_skip_decorator()
+ self._obj = getattr(self._testcase, self.name)
+ if hasattr(self._testcase, 'setup_method'):
+ self._testcase.setup_method(self._obj)
+ if hasattr(self, "_request"):
+ self._request._fillfixtures()
+
+ def _fix_unittest_skip_decorator(self):
+ """
+ The @unittest.skip decorator calls functools.wraps(self._testcase)
+ The call to functools.wraps() fails unless self._testcase
+ has a __name__ attribute. This is usually automatically supplied
+ if the test is a function or method, but we need to add manually
+ here.
+
+ See issue #1169
+ """
+ if sys.version_info[0] == 2:
+ setattr(self._testcase, "__name__", self.name)
+
+ def teardown(self):
+ if hasattr(self._testcase, 'teardown_method'):
+ self._testcase.teardown_method(self._obj)
+ # Allow garbage collection on TestCase instance attributes.
+ self._testcase = None
+ self._obj = None
+
+ def startTest(self, testcase):
+ pass
+
+ def _addexcinfo(self, rawexcinfo):
+ # unwrap potential exception info (see twisted trial support below)
+ rawexcinfo = getattr(rawexcinfo, '_rawexcinfo', rawexcinfo)
+ try:
+ excinfo = _pytest._code.ExceptionInfo(rawexcinfo)
+ except TypeError:
+ try:
+ try:
+ values = traceback.format_exception(*rawexcinfo)
+ values.insert(0, "NOTE: Incompatible Exception Representation, "
+ "displaying natively:\n\n")
+ fail("".join(values), pytrace=False)
+ except (fail.Exception, KeyboardInterrupt):
+ raise
+ except: # noqa
+ fail("ERROR: Unknown Incompatible Exception "
+ "representation:\n%r" % (rawexcinfo,), pytrace=False)
+ except KeyboardInterrupt:
+ raise
+ except fail.Exception:
+ excinfo = _pytest._code.ExceptionInfo()
+ self.__dict__.setdefault('_excinfo', []).append(excinfo)
+
+ def addError(self, testcase, rawexcinfo):
+ self._addexcinfo(rawexcinfo)
+
+ def addFailure(self, testcase, rawexcinfo):
+ self._addexcinfo(rawexcinfo)
+
+ def addSkip(self, testcase, reason):
+ try:
+ skip(reason)
+ except skip.Exception:
+ self._skipped_by_mark = True
+ self._addexcinfo(sys.exc_info())
+
+ def addExpectedFailure(self, testcase, rawexcinfo, reason=""):
+ try:
+ xfail(str(reason))
+ except xfail.Exception:
+ self._addexcinfo(sys.exc_info())
+
+ def addUnexpectedSuccess(self, testcase, reason=""):
+ self._unexpectedsuccess = reason
+
+ def addSuccess(self, testcase):
+ pass
+
+ def stopTest(self, testcase):
+ pass
+
+ def _handle_skip(self):
+ # implements the skipping machinery (see #2137)
+ # analog to pythons Lib/unittest/case.py:run
+ testMethod = getattr(self._testcase, self._testcase._testMethodName)
+ if (getattr(self._testcase.__class__, "__unittest_skip__", False) or
+ getattr(testMethod, "__unittest_skip__", False)):
+ # If the class or method was skipped.
+ skip_why = (getattr(self._testcase.__class__, '__unittest_skip_why__', '') or
+ getattr(testMethod, '__unittest_skip_why__', ''))
+ try: # PY3, unittest2 on PY2
+ self._testcase._addSkip(self, self._testcase, skip_why)
+ except TypeError: # PY2
+ if sys.version_info[0] != 2:
+ raise
+ self._testcase._addSkip(self, skip_why)
+ return True
+ return False
+
+ def runtest(self):
+ if self.config.pluginmanager.get_plugin("pdbinvoke") is None:
+ self._testcase(result=self)
+ else:
+ # disables tearDown and cleanups for post mortem debugging (see #1890)
+ if self._handle_skip():
+ return
+ self._testcase.debug()
+
+ def _prunetraceback(self, excinfo):
+ Function._prunetraceback(self, excinfo)
+ traceback = excinfo.traceback.filter(
+ lambda x: not x.frame.f_globals.get('__unittest'))
+ if traceback:
+ excinfo.traceback = traceback
+
+
+@hookimpl(tryfirst=True)
+def pytest_runtest_makereport(item, call):
+ if isinstance(item, TestCaseFunction):
+ if item._excinfo:
+ call.excinfo = item._excinfo.pop(0)
+ try:
+ del call.result
+ except AttributeError:
+ pass
+
+# twisted trial support
+
+
+@hookimpl(hookwrapper=True)
+def pytest_runtest_protocol(item):
+ if isinstance(item, TestCaseFunction) and \
+ 'twisted.trial.unittest' in sys.modules:
+ ut = sys.modules['twisted.python.failure']
+ Failure__init__ = ut.Failure.__init__
+ check_testcase_implements_trial_reporter()
+
+ def excstore(self, exc_value=None, exc_type=None, exc_tb=None,
+ captureVars=None):
+ if exc_value is None:
+ self._rawexcinfo = sys.exc_info()
+ else:
+ if exc_type is None:
+ exc_type = type(exc_value)
+ self._rawexcinfo = (exc_type, exc_value, exc_tb)
+ try:
+ Failure__init__(self, exc_value, exc_type, exc_tb,
+ captureVars=captureVars)
+ except TypeError:
+ Failure__init__(self, exc_value, exc_type, exc_tb)
+
+ ut.Failure.__init__ = excstore
+ yield
+ ut.Failure.__init__ = Failure__init__
+ else:
+ yield
+
+
+def check_testcase_implements_trial_reporter(done=[]):
+ if done:
+ return
+ from zope.interface import classImplements
+ from twisted.trial.itrial import IReporter
+ classImplements(TestCaseFunction, IReporter)
+ done.append(1)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/warnings.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/warnings.py
new file mode 100644
index 00000000000..3c2b1914fb6
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/_pytest/warnings.py
@@ -0,0 +1,96 @@
+from __future__ import absolute_import, division, print_function
+
+import warnings
+from contextlib import contextmanager
+
+import pytest
+
+from _pytest import compat
+
+
+def _setoption(wmod, arg):
+ """
+ Copy of the warning._setoption function but does not escape arguments.
+ """
+ parts = arg.split(':')
+ if len(parts) > 5:
+ raise wmod._OptionError("too many fields (max 5): %r" % (arg,))
+ while len(parts) < 5:
+ parts.append('')
+ action, message, category, module, lineno = [s.strip()
+ for s in parts]
+ action = wmod._getaction(action)
+ category = wmod._getcategory(category)
+ if lineno:
+ try:
+ lineno = int(lineno)
+ if lineno < 0:
+ raise ValueError
+ except (ValueError, OverflowError):
+ raise wmod._OptionError("invalid lineno %r" % (lineno,))
+ else:
+ lineno = 0
+ wmod.filterwarnings(action, message, category, module, lineno)
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("pytest-warnings")
+ group.addoption(
+ '-W', '--pythonwarnings', action='append',
+ help="set which warnings to report, see -W option of python itself.")
+ parser.addini("filterwarnings", type="linelist",
+ help="Each line specifies a pattern for "
+ "warnings.filterwarnings. "
+ "Processed after -W and --pythonwarnings.")
+
+
+@contextmanager
+def catch_warnings_for_item(item):
+ """
+ catches the warnings generated during setup/call/teardown execution
+ of the given item and after it is done posts them as warnings to this
+ item.
+ """
+ args = item.config.getoption('pythonwarnings') or []
+ inifilters = item.config.getini("filterwarnings")
+ with warnings.catch_warnings(record=True) as log:
+ for arg in args:
+ warnings._setoption(arg)
+
+ for arg in inifilters:
+ _setoption(warnings, arg)
+
+ mark = item.get_marker('filterwarnings')
+ if mark:
+ for arg in mark.args:
+ warnings._setoption(arg)
+
+ yield
+
+ for warning in log:
+ warn_msg = warning.message
+ unicode_warning = False
+
+ if compat._PY2 and any(isinstance(m, compat.UNICODE_TYPES) for m in warn_msg.args):
+ new_args = []
+ for m in warn_msg.args:
+ new_args.append(compat.ascii_escaped(m) if isinstance(m, compat.UNICODE_TYPES) else m)
+ unicode_warning = list(warn_msg.args) != new_args
+ warn_msg.args = new_args
+
+ msg = warnings.formatwarning(
+ warn_msg, warning.category,
+ warning.filename, warning.lineno, warning.line)
+ item.warn("unused", msg)
+
+ if unicode_warning:
+ warnings.warn(
+ "Warning is using unicode non convertible to ascii, "
+ "converting to a safe representation:\n %s" % msg,
+ UnicodeWarning)
+
+
+@pytest.hookimpl(hookwrapper=True)
+def pytest_runtest_protocol(item):
+ with catch_warnings_for_item(item):
+ yield
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/appveyor.yml b/tests/wpt/web-platform-tests/tools/third_party/pytest/appveyor.yml
new file mode 100644
index 00000000000..4f4afe15c38
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/appveyor.yml
@@ -0,0 +1,44 @@
+environment:
+ COVERALLS_REPO_TOKEN:
+ secure: 2NJ5Ct55cHJ9WEg3xbSqCuv0rdgzzb6pnzOIG5OkMbTndw3wOBrXntWFoQrXiMFi
+ # this is pytest's token in coveralls.io, encrypted
+ # using pytestbot account as detailed here:
+ # https://www.appveyor.com/docs/build-configuration#secure-variables
+
+ matrix:
+ # coveralls is not in the default env list
+ - TOXENV: "coveralls"
+ # note: please use "tox --listenvs" to populate the build matrix below
+ - TOXENV: "linting"
+ - TOXENV: "py27"
+ - TOXENV: "py34"
+ - TOXENV: "py35"
+ - TOXENV: "py36"
+ - TOXENV: "pypy"
+ - TOXENV: "py27-pexpect"
+ - TOXENV: "py27-xdist"
+ - TOXENV: "py27-trial"
+ - TOXENV: "py27-numpy"
+ - TOXENV: "py27-pluggymaster"
+ - TOXENV: "py36-pexpect"
+ - TOXENV: "py36-xdist"
+ - TOXENV: "py36-trial"
+ - TOXENV: "py36-numpy"
+ - TOXENV: "py36-pluggymaster"
+ - TOXENV: "py27-nobyte"
+ - TOXENV: "doctesting"
+ - TOXENV: "py35-freeze"
+ - TOXENV: "docs"
+
+install:
+ - echo Installed Pythons
+ - dir c:\Python*
+
+ - if "%TOXENV%" == "pypy" call scripts\install-pypy.bat
+
+ - C:\Python36\python -m pip install --upgrade --pre tox
+
+build: false # Not a C# project, build stuff at the test step instead.
+
+test_script:
+ - call scripts\call-tox.bat
diff --git a/tests/wpt/web-platform-tests/tools/pytest/bench/bench.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/bench/bench.py
index ce94964172f..ce94964172f 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/bench/bench.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/bench/bench.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/bench/bench_argcomplete.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/bench/bench_argcomplete.py
index d66c664f346..d66c664f346 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/bench/bench_argcomplete.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/bench/bench_argcomplete.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/bench/empty.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/bench/empty.py
index ac5e25701da..ac5e25701da 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/bench/empty.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/bench/empty.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/bench/manyparam.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/bench/manyparam.py
index d2bca0e8acd..d2bca0e8acd 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/bench/manyparam.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/bench/manyparam.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/bench/skip.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/bench/skip.py
index 960b308645a..960b308645a 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/bench/skip.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/bench/skip.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2920.bugfix b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2920.bugfix
new file mode 100644
index 00000000000..9c5217278ae
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2920.bugfix
@@ -0,0 +1 @@
+Fix issue about ``-p no:<plugin>`` having no effect.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2949.trivial b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2949.trivial
new file mode 100644
index 00000000000..39789e72b7a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2949.trivial
@@ -0,0 +1 @@
+Update github "bugs" link in CONTRIBUTING.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2956.bugfix b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2956.bugfix
new file mode 100644
index 00000000000..13717657bf1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2956.bugfix
@@ -0,0 +1 @@
+Fix regression with warnings that contained non-strings in their arguments in Python 2.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2957.bugfix b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2957.bugfix
new file mode 100644
index 00000000000..589665b692a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2957.bugfix
@@ -0,0 +1 @@
+Always escape null bytes when setting ``PYTEST_CURRENT_TEST``.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2963.doc b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2963.doc
new file mode 100644
index 00000000000..c9a1d661b64
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2963.doc
@@ -0,0 +1 @@
+Fix broken link to plugin pytest-localserver.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2971.bugfix b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2971.bugfix
new file mode 100644
index 00000000000..36684e8c880
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2971.bugfix
@@ -0,0 +1 @@
+Fix ``ZeroDivisionError`` when using the ``testmon`` plugin when no tests were actually collected.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2984.bugfix b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2984.bugfix
new file mode 100644
index 00000000000..21f5748d523
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/2984.bugfix
@@ -0,0 +1 @@
+Bring back ``TerminalReporter.writer`` as an alias to ``TerminalReporter._tw``. This alias was removed by accident in the ``3.3.0`` release.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/_template.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/_template.rst
new file mode 100644
index 00000000000..a898abc15af
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/changelog/_template.rst
@@ -0,0 +1,40 @@
+{% for section in sections %}
+{% set underline = "-" %}
+{% if section %}
+{{section}}
+{{ underline * section|length }}{% set underline = "~" %}
+
+{% endif %}
+{% if sections[section] %}
+{% for category, val in definitions.items() if category in sections[section] %}
+
+{{ definitions[category]['name'] }}
+{{ underline * definitions[category]['name']|length }}
+
+{% if definitions[category]['showcontent'] %}
+{% for text, values in sections[section][category]|dictsort(by='value') %}
+{% set issue_joiner = joiner(', ') %}
+- {{ text }}{% if category != 'vendor' %} ({% for value in values|sort %}{{ issue_joiner() }}`{{ value }} <https://github.com/pytest-dev/pytest/issues/{{ value[1:] }}>`_{% endfor %}){% endif %}
+
+
+{% endfor %}
+{% else %}
+- {{ sections[section][category]['']|sort|join(', ') }}
+
+
+{% endif %}
+{% if sections[section][category]|length == 0 %}
+
+No significant changes.
+
+
+{% else %}
+{% endif %}
+{% endfor %}
+{% else %}
+
+No significant changes.
+
+
+{% endif %}
+{% endfor %}
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/Makefile b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/Makefile
new file mode 100644
index 00000000000..fa8e8266a29
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/Makefile
@@ -0,0 +1,150 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+REGENDOC_ARGS := \
+ --normalize "/in \d+.\d+ seconds/in 0.12 seconds/" \
+ --normalize "@/tmp/pytest-of-.*/pytest-\d+@PYTEST_TMPDIR@" \
+ --normalize "@pytest-(\d+)\\.[^ ,]+@pytest-\1.x.y@" \
+ --normalize "@(This is pytest version )(\d+)\\.[^ ,]+@\1\2.x.y@" \
+ --normalize "@py-(\d+)\\.[^ ,]+@py-\1.x.y@" \
+ --normalize "@pluggy-(\d+)\\.[.\d,]+@pluggy-\1.x.y@" \
+ --normalize "@hypothesis-(\d+)\\.[.\d,]+@hypothesis-\1.x.y@" \
+ --normalize "@Python (\d+)\\.[^ ,]+@Python \1.x.y@"
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " showtarget to show the pytest.org target directory"
+ @echo " install to install docs to pytest.org/SITETARGET"
+ @echo " install-ldf to install the doc pdf to pytest.org/SITETARGET"
+ @echo " regen to regenerate pytest examples using the installed pytest"
+ @echo " linkcheck to check all external links for integrity"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+regen:
+ PYTHONDONTWRITEBYTECODE=1 PYTEST_ADDOPT=-pno:hypothesis COLUMNS=76 regendoc --update *.rst */*.rst ${REGENDOC_ARGS}
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pytest.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pytest.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/pytest"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pytest"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ make -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+texinfo:
+ mkdir -p $(BUILDDIR)/texinfo
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ mkdir -p $(BUILDDIR)/texinfo
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/globaltoc.html b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/globaltoc.html
new file mode 100644
index 00000000000..fdd4dd59b32
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/globaltoc.html
@@ -0,0 +1,19 @@
+<h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
+
+<ul>
+ <li><a href="{{ pathto('index') }}">Home</a></li>
+ <li><a href="{{ pathto('contents') }}">Contents</a></li>
+ <li><a href="{{ pathto('getting-started') }}">Install</a></li>
+ <li><a href="{{ pathto('example/index') }}">Examples</a></li>
+ <li><a href="{{ pathto('customize') }}">Customize</a></li>
+ <li><a href="{{ pathto('contact') }}">Contact</a></li>
+ <li><a href="{{ pathto('talks') }}">Talks/Posts</a></li>
+ <li><a href="{{ pathto('changelog') }}">Changelog</a></li>
+ <li><a href="{{ pathto('backwards-compatibility') }}">Backwards Compatibility</a></li>
+ <li><a href="{{ pathto('license') }}">License</a></li>
+</ul>
+
+{%- if display_toc %}
+ <hr>
+ {{ toc }}
+{%- endif %}
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/layout.html b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/layout.html
new file mode 100644
index 00000000000..2fc8e2a7fb4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/layout.html
@@ -0,0 +1,20 @@
+{% extends "!layout.html" %}
+{% block header %}
+ {{super()}}
+{% endblock %}
+{% block footer %}
+{{ super() }}
+<script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-7597274-13']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+</script>
+{% endblock %}
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/links.html b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/links.html
new file mode 100644
index 00000000000..d855a013f34
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/links.html
@@ -0,0 +1,11 @@
+<h3>Useful Links</h3>
+<ul>
+ <li><a href="{{ pathto('index') }}">The pytest Website</a></li>
+ <li><a href="{{ pathto('contributing') }}">Contribution Guide</a></li>
+ <li><a href="https://pypi.python.org/pypi/pytest">pytest @ PyPI</a></li>
+ <li><a href="https://github.com/pytest-dev/pytest/">pytest @ GitHub</a></li>
+ <li><a href="http://plugincompat.herokuapp.com/">3rd party plugins</a></li>
+ <li><a href="https://github.com/pytest-dev/pytest/issues">Issue Tracker</a></li>
+ <li><a href="https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf">PDF Documentation</a>
+</ul>
+
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/sidebarintro.html b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/sidebarintro.html
index ae860c172f0..ae860c172f0 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_templates/sidebarintro.html
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_templates/sidebarintro.html
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/.gitignore b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/.gitignore
index 66b6e4c2f3b..66b6e4c2f3b 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/.gitignore
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/.gitignore
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/LICENSE b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/LICENSE
index 8daab7ee6ef..8daab7ee6ef 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/LICENSE
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/LICENSE
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/README b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/README
index b3292bdff8e..b3292bdff8e 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/README
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/README
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/layout.html b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/layout.html
index 19c43fbbefc..19c43fbbefc 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/layout.html
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/layout.html
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/relations.html b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/relations.html
index 3bbcde85bb4..3bbcde85bb4 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/relations.html
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/relations.html
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/static/flasky.css_t b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/static/flasky.css_t
index 6b593da299a..6b593da299a 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/static/flasky.css_t
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/static/flasky.css_t
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/theme.conf b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/theme.conf
index 18c720f804c..18c720f804c 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask/theme.conf
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask/theme.conf
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask_theme_support.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask_theme_support.py
index 33f47449c11..33f47449c11 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/_themes/flask_theme_support.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/_themes/flask_theme_support.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/adopt.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/adopt.rst
new file mode 100644
index 00000000000..710f431be30
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/adopt.rst
@@ -0,0 +1,82 @@
+:orphan:
+
+.. warnings about this file not being included in any toctree will be suppressed by :orphan:
+
+
+April 2015 is "adopt pytest month"
+=============================================
+
+Are you an enthusiastic pytest user, the local testing guru in your workplace? Or are you considering using pytest for your open source project, but not sure how to get started? Then you may be interested in "adopt pytest month"!
+
+We will pair experienced pytest users with open source projects, for a month's effort of getting new development teams started with pytest.
+
+In 2015 we are trying this for the first time. In February and March 2015 we will gather volunteers on both sides, in April we will do the work, and in May we will evaluate how it went. This effort is being coordinated by Brianna Laugher. If you have any questions or comments, you can raise them on the `@pytestdotorg twitter account <https://twitter.com/pytestdotorg>`_ the `issue tracker`_ or the `pytest-dev mailing list`_.
+
+
+.. _`issue tracker`: https://github.com/pytest-dev/pytest/issues/676
+.. _`pytest-dev mailing list`: https://mail.python.org/mailman/listinfo/pytest-dev
+
+
+The ideal pytest helper
+-----------------------------------------
+
+ - will be able to commit 2-4 hours a week to working with their particular project (this might involve joining their mailing list, installing the software and exploring any existing tests, offering advice, writing some example tests)
+ - feels confident in using pytest (e.g. has explored command line options, knows how to write parametrized tests, has an idea about conftest contents)
+ - does not need to be an expert in every aspect!
+
+`Pytest helpers, sign up here`_! (preferably in February, hard deadline 22 March)
+
+
+.. _`Pytest helpers, sign up here`: http://goo.gl/forms/nxqAhqWt1P
+
+
+The ideal partner project
+-----------------------------------------
+
+ - is open source, and predominantly written in Python
+ - has an automated/documented install process for developers
+ - has more than one core developer
+ - has at least one official release (e.g. is available on pypi)
+ - has the support of the core development team, in trying out pytest adoption
+ - has no tests... or 100% test coverage... or somewhere in between!
+
+`Partner projects, sign up here`_! (by 22 March)
+
+
+.. _`Partner projects, sign up here`: http://goo.gl/forms/ZGyqlHiwk3
+
+
+What does it mean to "adopt pytest"?
+-----------------------------------------
+
+There can be many different definitions of "success". Pytest can run many `nose and unittest`_ tests by default, so using pytest as your testrunner may be possible from day 1. Job done, right?
+
+Progressive success might look like:
+
+ - tests can be run (by pytest) without errors (there may be failures)
+ - tests can be run (by pytest) without failures
+ - test runner is integrated into CI server
+ - existing tests are rewritten to take advantage of pytest features - this can happen in several iterations, for example:
+ - changing to native assert_ statements (pycmd_ has a script to help with that, ``pyconvert_unittest.py``)
+ - changing `setUp/tearDown methods`_ to fixtures_
+ - adding markers_
+ - other changes to reduce boilerplate
+ - assess needs for future tests to be written, e.g. new fixtures, distributed_ testing tweaks
+
+"Success" should also include that the development team feels comfortable with their knowledge of how to use pytest. In fact this is probably more important than anything else. So spending a lot of time on communication, giving examples, etc will probably be important - both in running the tests, and in writing them.
+
+It may be after the month is up, the partner project decides that pytest is not right for it. That's okay - hopefully the pytest team will also learn something about its weaknesses or deficiencies.
+
+.. _`nose and unittest`: faq.html#how-does-pytest-relate-to-nose-and-unittest
+.. _assert: asserts.html
+.. _pycmd: https://bitbucket.org/hpk42/pycmd/overview
+.. _`setUp/tearDown methods`: xunit_setup.html
+.. _fixtures: fixture.html
+.. _markers: markers.html
+.. _distributed: xdist.html
+
+
+Other ways to help
+-----------------------------------------
+
+Promote! Do your favourite open source Python projects use pytest? If not, why not tell them about this page?
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/index.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/index.rst
new file mode 100644
index 00000000000..1a5f3760b68
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/index.rst
@@ -0,0 +1,68 @@
+
+Release announcements
+===========================================
+
+.. toctree::
+ :maxdepth: 2
+
+
+ release-3.3.0
+ release-3.2.5
+ release-3.2.4
+ release-3.2.3
+ release-3.2.2
+ release-3.2.1
+ release-3.2.0
+ release-3.1.3
+ release-3.1.2
+ release-3.1.1
+ release-3.1.0
+ release-3.0.7
+ release-3.0.6
+ release-3.0.5
+ release-3.0.4
+ release-3.0.3
+ release-3.0.2
+ release-3.0.1
+ release-3.0.0
+ sprint2016
+ release-2.9.2
+ release-2.9.1
+ release-2.9.0
+ release-2.8.7
+ release-2.8.6
+ release-2.8.5
+ release-2.8.4
+ release-2.8.3
+ release-2.8.2
+ release-2.7.2
+ release-2.7.1
+ release-2.7.0
+ release-2.6.3
+ release-2.6.2
+ release-2.6.1
+ release-2.6.0
+ release-2.5.2
+ release-2.5.1
+ release-2.5.0
+ release-2.4.2
+ release-2.4.1
+ release-2.4.0
+ release-2.3.5
+ release-2.3.4
+ release-2.3.3
+ release-2.3.2
+ release-2.3.1
+ release-2.3.0
+ release-2.2.4
+ release-2.2.2
+ release-2.2.1
+ release-2.2.0
+ release-2.1.3
+ release-2.1.2
+ release-2.1.1
+ release-2.1.0
+ release-2.0.3
+ release-2.0.2
+ release-2.0.1
+ release-2.0.0
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.0.rst
index af745fc59b2..af745fc59b2 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.0.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.0.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.1.rst
index 2f41ef9435e..2f41ef9435e 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.0.1.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.1.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.2.rst
new file mode 100644
index 00000000000..f1f44f34f4f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.2.rst
@@ -0,0 +1,73 @@
+py.test 2.0.2: bug fixes, improved xfail/skip expressions, speed ups
+===========================================================================
+
+Welcome to pytest-2.0.2, a maintenance and bug fix release of pytest,
+a mature testing tool for Python, supporting CPython 2.4-3.2, Jython
+and latest PyPy interpreters. See the extensive docs with tested examples here:
+
+ http://pytest.org/
+
+If you want to install or upgrade pytest, just type one of::
+
+ pip install -U pytest # or
+ easy_install -U pytest
+
+Many thanks to all issue reporters and people asking questions
+or complaining, particularly Jurko for his insistence,
+Laura, Victor and Brianna for helping with improving
+and Ronny for his general advise.
+
+best,
+holger krekel
+
+Changes between 2.0.1 and 2.0.2
+----------------------------------------------
+
+- tackle issue32 - speed up test runs of very quick test functions
+ by reducing the relative overhead
+
+- fix issue30 - extended xfail/skipif handling and improved reporting.
+ If you have a syntax error in your skip/xfail
+ expressions you now get nice error reports.
+
+ Also you can now access module globals from xfail/skipif
+ expressions so that this for example works now::
+
+ import pytest
+ import mymodule
+ @pytest.mark.skipif("mymodule.__version__[0] == "1")
+ def test_function():
+ pass
+
+ This will not run the test function if the module's version string
+ does not start with a "1". Note that specifying a string instead
+ of a boolean expressions allows py.test to report meaningful information
+ when summarizing a test run as to what conditions lead to skipping
+ (or xfail-ing) tests.
+
+- fix issue28 - setup_method and pytest_generate_tests work together
+ The setup_method fixture method now gets called also for
+ test function invocations generated from the pytest_generate_tests
+ hook.
+
+- fix issue27 - collectonly and keyword-selection (-k) now work together
+ Also, if you do "py.test --collectonly -q" you now get a flat list
+ of test ids that you can use to paste to the py.test commandline
+ in order to execute a particular test.
+
+- fix issue25 avoid reported problems with --pdb and python3.2/encodings output
+
+- fix issue23 - tmpdir argument now works on Python3.2 and WindowsXP
+ Starting with Python3.2 os.symlink may be supported. By requiring
+ a newer py lib version the py.path.local() implementation acknowledges
+ this.
+
+- fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular
+ thanks to Laura Creighton who also reviewed parts of the documentation.
+
+- fix slightly wrong output of verbose progress reporting for classes
+ (thanks Amaury)
+
+- more precise (avoiding of) deprecation warnings for node.Class|Function accesses
+
+- avoid std unittest assertion helper code in tracebacks (thanks Ronny)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.3.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.3.rst
new file mode 100644
index 00000000000..9bbfdaab361
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.0.3.rst
@@ -0,0 +1,40 @@
+py.test 2.0.3: bug fixes and speed ups
+===========================================================================
+
+Welcome to pytest-2.0.3, a maintenance and bug fix release of pytest,
+a mature testing tool for Python, supporting CPython 2.4-3.2, Jython
+and latest PyPy interpreters. See the extensive docs with tested examples here:
+
+ http://pytest.org/
+
+If you want to install or upgrade pytest, just type one of::
+
+ pip install -U pytest # or
+ easy_install -U pytest
+
+There also is a bugfix release 1.6 of pytest-xdist, the plugin
+that enables seamless distributed and "looponfail" testing for Python.
+
+best,
+holger krekel
+
+Changes between 2.0.2 and 2.0.3
+----------------------------------------------
+
+- fix issue38: nicer tracebacks on calls to hooks, particularly early
+ configure/sessionstart ones
+
+- fix missing skip reason/meta information in junitxml files, reported
+ via http://lists.idyll.org/pipermail/testing-in-python/2011-March/003928.html
+
+- fix issue34: avoid collection failure with "test" prefixed classes
+ deriving from object.
+
+- don't require zlib (and other libs) for genscript plugin without
+ --genscript actually being used.
+
+- speed up skips (by not doing a full traceback representation
+ internally)
+
+- fix issue37: avoid invalid characters in junitxml's output
+
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.0.rst
index 831548ac2ff..831548ac2ff 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.0.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.0.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.1.rst
index ecdd69f4dc9..ecdd69f4dc9 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.1.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.1.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.2.rst
index 51b7591d366..51b7591d366 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.2.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.2.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.3.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.3.rst
index f4da60b8ba4..f4da60b8ba4 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.1.3.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.1.3.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.0.rst
index 20bfe0a1915..20bfe0a1915 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.0.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.0.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.1.rst
new file mode 100644
index 00000000000..5d28bcb01f4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.1.rst
@@ -0,0 +1,41 @@
+pytest-2.2.1: bug fixes, perfect teardowns
+===========================================================================
+
+
+pytest-2.2.1 is a minor backward-compatible release of the py.test
+testing tool. It contains bug fixes and little improvements, including
+documentation fixes. If you are using the distributed testing
+pluginmake sure to upgrade it to pytest-xdist-1.8.
+
+For general information see here:
+
+ http://pytest.org/
+
+To install or upgrade pytest:
+
+ pip install -U pytest # or
+ easy_install -U pytest
+
+Special thanks for helping on this release to Ronny Pfannschmidt, Jurko
+Gospodnetic and Ralf Schmitt.
+
+best,
+holger krekel
+
+
+Changes between 2.2.0 and 2.2.1
+----------------------------------------
+
+- fix issue99 (in pytest and py) internallerrors with resultlog now
+ produce better output - fixed by normalizing pytest_internalerror
+ input arguments.
+- fix issue97 / traceback issues (in pytest and py) improve traceback output
+ in conjunction with jinja2 and cython which hack tracebacks
+- fix issue93 (in pytest and pytest-xdist) avoid "delayed teardowns":
+ the final test in a test node will now run its teardown directly
+ instead of waiting for the end of the session. Thanks Dave Hunt for
+ the good reporting and feedback. The pytest_runtest_protocol as well
+ as the pytest_runtest_teardown hooks now have "nextitem" available
+ which will be None indicating the end of the test run.
+- fix collection crash due to unknown-source collected items, thanks
+ to Ralf Schmitt (fixed by depending on a more recent pylib)
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.2.rst
index 733aedec413..733aedec413 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.2.2.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.2.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.4.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.4.rst
new file mode 100644
index 00000000000..67f0feb27c7
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.2.4.rst
@@ -0,0 +1,39 @@
+pytest-2.2.4: bug fixes, better junitxml/unittest/python3 compat
+===========================================================================
+
+pytest-2.2.4 is a minor backward-compatible release of the versatile
+py.test testing tool. It contains bug fixes and a few refinements
+to junitxml reporting, better unittest- and python3 compatibility.
+
+For general information see here:
+
+ http://pytest.org/
+
+To install or upgrade pytest:
+
+ pip install -U pytest # or
+ easy_install -U pytest
+
+Special thanks for helping on this release to Ronny Pfannschmidt
+and Benjamin Peterson and the contributors of issues.
+
+best,
+holger krekel
+
+Changes between 2.2.3 and 2.2.4
+-----------------------------------
+
+- fix error message for rewritten assertions involving the % operator
+- fix issue 126: correctly match all invalid xml characters for junitxml
+ binary escape
+- fix issue with unittest: now @unittest.expectedFailure markers should
+ be processed correctly (you can also use @pytest.mark markers)
+- document integration with the extended distribute/setuptools test commands
+- fix issue 140: properly get the real functions
+ of bound classmethods for setup/teardown_class
+- fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net
+- fix issue #143: call unconfigure/sessionfinish always when
+ configure/sessionstart where called
+- fix issue #144: better mangle test ids to junitxml classnames
+- upgrade distribute_setup.py to 0.6.27
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.0.rst
new file mode 100644
index 00000000000..f863aad0ace
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.0.rst
@@ -0,0 +1,134 @@
+pytest-2.3: improved fixtures / better unittest integration
+=============================================================================
+
+pytest-2.3 comes with many major improvements for fixture/funcarg management
+and parametrized testing in Python. It is now easier, more efficient and
+more predicatable to re-run the same tests with different fixture
+instances. Also, you can directly declare the caching "scope" of
+fixtures so that dependent tests throughout your whole test suite can
+re-use database or other expensive fixture objects with ease. Lastly,
+it's possible for fixture functions (formerly known as funcarg
+factories) to use other fixtures, allowing for a completely modular and
+re-useable fixture design.
+
+For detailed info and tutorial-style examples, see:
+
+ http://pytest.org/latest/fixture.html
+
+Moreover, there is now support for using pytest fixtures/funcargs with
+unittest-style suites, see here for examples:
+
+ http://pytest.org/latest/unittest.html
+
+Besides, more unittest-test suites are now expected to "simply work"
+with pytest.
+
+All changes are backward compatible and you should be able to continue
+to run your test suites and 3rd party plugins that worked with
+pytest-2.2.4.
+
+If you are interested in the precise reasoning (including examples) of the
+pytest-2.3 fixture evolution, please consult
+http://pytest.org/latest/funcarg_compare.html
+
+For general info on installation and getting started:
+
+ http://pytest.org/latest/getting-started.html
+
+Docs and PDF access as usual at:
+
+ http://pytest.org
+
+and more details for those already in the knowing of pytest can be found
+in the CHANGELOG below.
+
+Particular thanks for this release go to Floris Bruynooghe, Alex Okrushko
+Carl Meyer, Ronny Pfannschmidt, Benjamin Peterson and Alex Gaynor for helping
+to get the new features right and well integrated. Ronny and Floris
+also helped to fix a number of bugs and yet more people helped by
+providing bug reports.
+
+have fun,
+holger krekel
+
+
+Changes between 2.2.4 and 2.3.0
+-----------------------------------
+
+- fix issue202 - better automatic names for parametrized test functions
+- fix issue139 - introduce @pytest.fixture which allows direct scoping
+ and parametrization of funcarg factories. Introduce new @pytest.setup
+ marker to allow the writing of setup functions which accept funcargs.
+- fix issue198 - conftest fixtures were not found on windows32 in some
+ circumstances with nested directory structures due to path manipulation issues
+- fix issue193 skip test functions with were parametrized with empty
+ parameter sets
+- fix python3.3 compat, mostly reporting bits that previously depended
+ on dict ordering
+- introduce re-ordering of tests by resource and parametrization setup
+ which takes precedence to the usual file-ordering
+- fix issue185 monkeypatching time.time does not cause pytest to fail
+- fix issue172 duplicate call of pytest.setup-decoratored setup_module
+ functions
+- fix junitxml=path construction so that if tests change the
+ current working directory and the path is a relative path
+ it is constructed correctly from the original current working dir.
+- fix "python setup.py test" example to cause a proper "errno" return
+- fix issue165 - fix broken doc links and mention stackoverflow for FAQ
+- catch unicode-issues when writing failure representations
+ to terminal to prevent the whole session from crashing
+- fix xfail/skip confusion: a skip-mark or an imperative pytest.skip
+ will now take precedence before xfail-markers because we
+ can't determine xfail/xpass status in case of a skip. see also:
+ http://stackoverflow.com/questions/11105828/in-py-test-when-i-explicitly-skip-a-test-that-is-marked-as-xfail-how-can-i-get
+
+- always report installed 3rd party plugins in the header of a test run
+
+- fix issue160: a failing setup of an xfail-marked tests should
+ be reported as xfail (not xpass)
+
+- fix issue128: show captured output when capsys/capfd are used
+
+- fix issue179: properly show the dependency chain of factories
+
+- pluginmanager.register(...) now raises ValueError if the
+ plugin has been already registered or the name is taken
+
+- fix issue159: improve http://pytest.org/latest/faq.html
+ especially with respect to the "magic" history, also mention
+ pytest-django, trial and unittest integration.
+
+- make request.keywords and node.keywords writable. All descendant
+ collection nodes will see keyword values. Keywords are dictionaries
+ containing markers and other info.
+
+- fix issue 178: xml binary escapes are now wrapped in py.xml.raw
+
+- fix issue 176: correctly catch the builtin AssertionError
+ even when we replaced AssertionError with a subclass on the
+ python level
+
+- factory discovery no longer fails with magic global callables
+ that provide no sane __code__ object (mock.call for example)
+
+- fix issue 182: testdir.inprocess_run now considers passed plugins
+
+- fix issue 188: ensure sys.exc_info is clear on python2
+ before calling into a test
+
+- fix issue 191: add unittest TestCase runTest method support
+- fix issue 156: monkeypatch correctly handles class level descriptors
+
+- reporting refinements:
+
+ - pytest_report_header now receives a "startdir" so that
+ you can use startdir.bestrelpath(yourpath) to show
+ nice relative path
+
+ - allow plugins to implement both pytest_report_header and
+ pytest_sessionstart (sessionstart is invoked first).
+
+ - don't show deselected reason line if there is none
+
+ - py.test -vv will show all of assert comparisons instead of truncating
+
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.1.rst
index b787dc203f4..b787dc203f4 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.1.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.1.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.2.rst
new file mode 100644
index 00000000000..75312b429cd
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.2.rst
@@ -0,0 +1,57 @@
+pytest-2.3.2: some fixes and more traceback-printing speed
+===========================================================================
+
+pytest-2.3.2 is another stabilization release:
+
+- issue 205: fixes a regression with conftest detection
+- issue 208/29: fixes traceback-printing speed in some bad cases
+- fix teardown-ordering for parametrized setups
+- fix unittest and trial compat behaviour with respect to runTest() methods
+- issue 206 and others: some improvements to packaging
+- fix issue127 and others: improve some docs
+
+See
+
+ http://pytest.org/
+
+for general information. To install or upgrade pytest:
+
+ pip install -U pytest # or
+ easy_install -U pytest
+
+best,
+holger krekel
+
+
+Changes between 2.3.1 and 2.3.2
+-----------------------------------
+
+- fix issue208 and fix issue29 use new py version to avoid long pauses
+ when printing tracebacks in long modules
+
+- fix issue205 - conftests in subdirs customizing
+ pytest_pycollect_makemodule and pytest_pycollect_makeitem
+ now work properly
+
+- fix teardown-ordering for parametrized setups
+
+- fix issue127 - better documentation for pytest_addoption
+ and related objects.
+
+- fix unittest behaviour: TestCase.runtest only called if there are
+ test methods defined
+
+- improve trial support: don't collect its empty
+ unittest.TestCase.runTest() method
+
+- "python setup.py test" now works with pytest itself
+
+- fix/improve internal/packaging related bits:
+
+ - exception message check of test_nose.py now passes on python33 as well
+
+ - issue206 - fix test_assertrewrite.py to work when a global
+ PYTHONDONTWRITEBYTECODE=1 is present
+
+ - add tox.ini to pytest distribution so that ignore-dirs and others config
+ bits are properly distributed for maintainers who run pytest-own tests
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.3.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.3.rst
new file mode 100644
index 00000000000..3a48b6ac4ba
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.3.rst
@@ -0,0 +1,62 @@
+pytest-2.3.3: integration fixes, py24 support, ``*/**`` shown in traceback
+===========================================================================
+
+pytest-2.3.3 is another stabilization release of the py.test tool
+which offers uebersimple assertions, scalable fixture mechanisms
+and deep customization for testing with Python. Particularly,
+this release provides:
+
+- integration fixes and improvements related to flask, numpy, nose,
+ unittest, mock
+
+- makes pytest work on py24 again (yes, people sometimes still need to use it)
+
+- show ``*,**`` args in pytest tracebacks
+
+Thanks to Manuel Jacob, Thomas Waldmann, Ronny Pfannschmidt, Pavel Repin
+and Andreas Taumoefolau for providing patches and all for the issues.
+
+See
+
+ http://pytest.org/
+
+for general information. To install or upgrade pytest:
+
+ pip install -U pytest # or
+ easy_install -U pytest
+
+best,
+holger krekel
+
+Changes between 2.3.2 and 2.3.3
+-----------------------------------
+
+- fix issue214 - parse modules that contain special objects like e. g.
+ flask's request object which blows up on getattr access if no request
+ is active. thanks Thomas Waldmann.
+
+- fix issue213 - allow to parametrize with values like numpy arrays that
+ do not support an __eq__ operator
+
+- fix issue215 - split test_python.org into multiple files
+
+- fix issue148 - @unittest.skip on classes is now recognized and avoids
+ calling setUpClass/tearDownClass, thanks Pavel Repin
+
+- fix issue209 - reintroduce python2.4 support by depending on newer
+ pylib which re-introduced statement-finding for pre-AST interpreters
+
+- nose support: only call setup if it's a callable, thanks Andrew
+ Taumoefolau
+
+- fix issue219 - add py2.4-3.3 classifiers to TROVE list
+
+- in tracebacks *,** arg values are now shown next to normal arguments
+ (thanks Manuel Jacob)
+
+- fix issue217 - support mock.patch with pytest's fixtures - note that
+ you need either mock-1.0.1 or the python3.3 builtin unittest.mock.
+
+- fix issue127 - improve documentation for pytest_addoption() and
+ add a ``config.getoption(name)`` helper function for consistency.
+
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.4.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.4.rst
index d6c597b5489..d6c597b5489 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.3.4.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.4.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.5.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.5.rst
new file mode 100644
index 00000000000..112399ef3ca
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.3.5.rst
@@ -0,0 +1,97 @@
+pytest-2.3.5: bug fixes and little improvements
+===========================================================================
+
+pytest-2.3.5 is a maintenance release with many bug fixes and little
+improvements. See the changelog below for details. No backward
+compatibility issues are foreseen and all plugins which worked with the
+prior version are expected to work unmodified. Speaking of which, a
+few interesting new plugins saw the light last month:
+
+- pytest-instafail: show failure information while tests are running
+- pytest-qt: testing of GUI applications written with QT/Pyside
+- pytest-xprocess: managing external processes across test runs
+- pytest-random: randomize test ordering
+
+And several others like pytest-django saw maintenance releases.
+For a more complete list, check out
+https://pypi.python.org/pypi?%3Aaction=search&term=pytest&submit=search.
+
+For general information see:
+
+ http://pytest.org/
+
+To install or upgrade pytest:
+
+ pip install -U pytest # or
+ easy_install -U pytest
+
+Particular thanks to Floris, Ronny, Benjamin and the many bug reporters
+and fix providers.
+
+may the fixtures be with you,
+holger krekel
+
+
+Changes between 2.3.4 and 2.3.5
+-----------------------------------
+
+- never consider a fixture function for test function collection
+
+- allow re-running of test items / helps to fix pytest-reruntests plugin
+ and also help to keep less fixture/resource references alive
+
+- put captured stdout/stderr into junitxml output even for passing tests
+ (thanks Adam Goucher)
+
+- Issue 265 - integrate nose setup/teardown with setupstate
+ so it doesn't try to teardown if it did not setup
+
+- issue 271 - don't write junitxml on slave nodes
+
+- Issue 274 - don't try to show full doctest example
+ when doctest does not know the example location
+
+- issue 280 - disable assertion rewriting on buggy CPython 2.6.0
+
+- inject "getfixture()" helper to retrieve fixtures from doctests,
+ thanks Andreas Zeidler
+
+- issue 259 - when assertion rewriting, be consistent with the default
+ source encoding of ASCII on Python 2
+
+- issue 251 - report a skip instead of ignoring classes with init
+
+- issue250 unicode/str mixes in parametrization names and values now works
+
+- issue257, assertion-triggered compilation of source ending in a
+ comment line doesn't blow up in python2.5 (fixed through py>=1.4.13.dev6)
+
+- fix --genscript option to generate standalone scripts that also
+ work with python3.3 (importer ordering)
+
+- issue171 - in assertion rewriting, show the repr of some
+ global variables
+
+- fix option help for "-k"
+
+- move long description of distribution into README.rst
+
+- improve docstring for metafunc.parametrize()
+
+- fix bug where using capsys with pytest.set_trace() in a test
+ function would break when looking at capsys.readouterr()
+
+- allow to specify prefixes starting with "_" when
+ customizing python_functions test discovery. (thanks Graham Horler)
+
+- improve PYTEST_DEBUG tracing output by putting
+ extra data on a new lines with additional indent
+
+- ensure OutcomeExceptions like skip/fail have initialized exception attributes
+
+- issue 260 - don't use nose special setup on plain unittest cases
+
+- fix issue134 - print the collect errors that prevent running specified test items
+
+- fix issue266 - accept unicode in MarkEvaluator expressions
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.4.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.4.0.rst
new file mode 100644
index 00000000000..be3aaedb09f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.4.0.rst
@@ -0,0 +1,225 @@
+pytest-2.4.0: new fixture features/hooks and bug fixes
+===========================================================================
+
+The just released pytest-2.4.0 brings many improvements and numerous
+bug fixes while remaining plugin- and test-suite compatible apart
+from a few supposedly very minor incompatibilities. See below for
+a full list of details. A few feature highlights:
+
+- new yield-style fixtures `pytest.yield_fixture
+ <http://pytest.org/latest/yieldfixture.html>`_, allowing to use
+ existing with-style context managers in fixture functions.
+
+- improved pdb support: ``import pdb ; pdb.set_trace()`` now works
+ without requiring prior disabling of stdout/stderr capturing.
+ Also the ``--pdb`` options works now on collection and internal errors
+ and we introduced a new experimental hook for IDEs/plugins to
+ intercept debugging: ``pytest_exception_interact(node, call, report)``.
+
+- shorter monkeypatch variant to allow specifying an import path as
+ a target, for example: ``monkeypatch.setattr("requests.get", myfunc)``
+
+- better unittest/nose compatibility: all teardown methods are now only
+ called if the corresponding setup method succeeded.
+
+- integrate tab-completion on command line options if you
+ have `argcomplete <http://pypi.python.org/pypi/argcomplete>`_
+ configured.
+
+- allow boolean expression directly with skipif/xfail
+ if a "reason" is also specified.
+
+- a new hook ``pytest_load_initial_conftests`` allows plugins like
+ `pytest-django <http://pypi.python.org/pypi/pytest-django>`_ to
+ influence the environment before conftest files import ``django``.
+
+- reporting: color the last line red or green depending if
+ failures/errors occurred or everything passed.
+
+The documentation has been updated to accommodate the changes,
+see `http://pytest.org <http://pytest.org>`_
+
+To install or upgrade pytest::
+
+ pip install -U pytest # or
+ easy_install -U pytest
+
+
+**Many thanks to all who helped, including Floris Bruynooghe,
+Brianna Laugher, Andreas Pelme, Anthon van der Neut, Anatoly Bubenkoff,
+Vladimir Keleshev, Mathieu Agopian, Ronny Pfannschmidt, Christian
+Theunert and many others.**
+
+may passing tests be with you,
+
+holger krekel
+
+Changes between 2.3.5 and 2.4
+-----------------------------------
+
+known incompatibilities:
+
+- if calling --genscript from python2.7 or above, you only get a
+ standalone script which works on python2.7 or above. Use Python2.6
+ to also get a python2.5 compatible version.
+
+- all xunit-style teardown methods (nose-style, pytest-style,
+ unittest-style) will not be called if the corresponding setup method failed,
+ see issue322 below.
+
+- the pytest_plugin_unregister hook wasn't ever properly called
+ and there is no known implementation of the hook - so it got removed.
+
+- pytest.fixture-decorated functions cannot be generators (i.e. use
+ yield) anymore. This change might be reversed in 2.4.1 if it causes
+ unforeseen real-life issues. However, you can always write and return
+ an inner function/generator and change the fixture consumer to iterate
+ over the returned generator. This change was done in lieu of the new
+ ``pytest.yield_fixture`` decorator, see below.
+
+new features:
+
+- experimentally introduce a new ``pytest.yield_fixture`` decorator
+ which accepts exactly the same parameters as pytest.fixture but
+ mandates a ``yield`` statement instead of a ``return statement`` from
+ fixture functions. This allows direct integration with "with-style"
+ context managers in fixture functions and generally avoids registering
+ of finalization callbacks in favour of treating the "after-yield" as
+ teardown code. Thanks Andreas Pelme, Vladimir Keleshev, Floris
+ Bruynooghe, Ronny Pfannschmidt and many others for discussions.
+
+- allow boolean expression directly with skipif/xfail
+ if a "reason" is also specified. Rework skipping documentation
+ to recommend "condition as booleans" because it prevents surprises
+ when importing markers between modules. Specifying conditions
+ as strings will remain fully supported.
+
+- reporting: color the last line red or green depending if
+ failures/errors occurred or everything passed. thanks Christian
+ Theunert.
+
+- make "import pdb ; pdb.set_trace()" work natively wrt capturing (no
+ "-s" needed anymore), making ``pytest.set_trace()`` a mere shortcut.
+
+- fix issue181: --pdb now also works on collect errors (and
+ on internal errors) . This was implemented by a slight internal
+ refactoring and the introduction of a new hook
+ ``pytest_exception_interact`` hook (see next item).
+
+- fix issue341: introduce new experimental hook for IDEs/terminals to
+ intercept debugging: ``pytest_exception_interact(node, call, report)``.
+
+- new monkeypatch.setattr() variant to provide a shorter
+ invocation for patching out classes/functions from modules:
+
+ monkeypatch.setattr("requests.get", myfunc)
+
+ will replace the "get" function of the "requests" module with ``myfunc``.
+
+- fix issue322: tearDownClass is not run if setUpClass failed. Thanks
+ Mathieu Agopian for the initial fix. Also make all of pytest/nose
+ finalizer mimic the same generic behaviour: if a setupX exists and
+ fails, don't run teardownX. This internally introduces a new method
+ "node.addfinalizer()" helper which can only be called during the setup
+ phase of a node.
+
+- simplify pytest.mark.parametrize() signature: allow to pass a
+ CSV-separated string to specify argnames. For example:
+ ``pytest.mark.parametrize("input,expected", [(1,2), (2,3)])``
+ works as well as the previous:
+ ``pytest.mark.parametrize(("input", "expected"), ...)``.
+
+- add support for setUpModule/tearDownModule detection, thanks Brian Okken.
+
+- integrate tab-completion on options through use of "argcomplete".
+ Thanks Anthon van der Neut for the PR.
+
+- change option names to be hyphen-separated long options but keep the
+ old spelling backward compatible. py.test -h will only show the
+ hyphenated version, for example "--collect-only" but "--collectonly"
+ will remain valid as well (for backward-compat reasons). Many thanks to
+ Anthon van der Neut for the implementation and to Hynek Schlawack for
+ pushing us.
+
+- fix issue 308 - allow to mark/xfail/skip individual parameter sets
+ when parametrizing. Thanks Brianna Laugher.
+
+- call new experimental pytest_load_initial_conftests hook to allow
+ 3rd party plugins to do something before a conftest is loaded.
+
+Bug fixes:
+
+- fix issue358 - capturing options are now parsed more properly
+ by using a new parser.parse_known_args method.
+
+- pytest now uses argparse instead of optparse (thanks Anthon) which
+ means that "argparse" is added as a dependency if installing into python2.6
+ environments or below.
+
+- fix issue333: fix a case of bad unittest/pytest hook interaction.
+
+- PR27: correctly handle nose.SkipTest during collection. Thanks
+ Antonio Cuni, Ronny Pfannschmidt.
+
+- fix issue355: junitxml puts name="pytest" attribute to testsuite tag.
+
+- fix issue336: autouse fixture in plugins should work again.
+
+- fix issue279: improve object comparisons on assertion failure
+ for standard datatypes and recognise collections.abc. Thanks to
+ Brianna Laugher and Mathieu Agopian.
+
+- fix issue317: assertion rewriter support for the is_package method
+
+- fix issue335: document py.code.ExceptionInfo() object returned
+ from pytest.raises(), thanks Mathieu Agopian.
+
+- remove implicit distribute_setup support from setup.py.
+
+- fix issue305: ignore any problems when writing pyc files.
+
+- SO-17664702: call fixture finalizers even if the fixture function
+ partially failed (finalizers would not always be called before)
+
+- fix issue320 - fix class scope for fixtures when mixed with
+ module-level functions. Thanks Anatloy Bubenkoff.
+
+- you can specify "-q" or "-qq" to get different levels of "quieter"
+ reporting (thanks Katarzyna Jachim)
+
+- fix issue300 - Fix order of conftest loading when starting py.test
+ in a subdirectory.
+
+- fix issue323 - sorting of many module-scoped arg parametrizations
+
+- make sessionfinish hooks execute with the same cwd-context as at
+ session start (helps fix plugin behaviour which write output files
+ with relative path such as pytest-cov)
+
+- fix issue316 - properly reference collection hooks in docs
+
+- fix issue 306 - cleanup of -k/-m options to only match markers/test
+ names/keywords respectively. Thanks Wouter van Ackooy.
+
+- improved doctest counting for doctests in python modules --
+ files without any doctest items will not show up anymore
+ and doctest examples are counted as separate test items.
+ thanks Danilo Bellini.
+
+- fix issue245 by depending on the released py-1.4.14
+ which fixes py.io.dupfile to work with files with no
+ mode. Thanks Jason R. Coombs.
+
+- fix junitxml generation when test output contains control characters,
+ addressing issue267, thanks Jaap Broekhuizen
+
+- fix issue338: honor --tb style for setup/teardown errors as well. Thanks Maho.
+
+- fix issue307 - use yaml.safe_load in example, thanks Mark Eichin.
+
+- better parametrize error messages, thanks Brianna Laugher
+
+- pytest_terminal_summary(terminalreporter) hooks can now use
+ ".section(title)" and ".line(msg)" methods to print extra
+ information at the end of a test run.
+
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.4.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.4.1.rst
index 64ba170f897..64ba170f897 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.4.1.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.4.1.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.4.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.4.2.rst
index 3b4aa95abbc..3b4aa95abbc 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.4.2.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.4.2.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.5.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.5.0.rst
new file mode 100644
index 00000000000..b04a825cd8e
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.5.0.rst
@@ -0,0 +1,175 @@
+pytest-2.5.0: now down to ZERO reported bugs!
+===========================================================================
+
+pytest-2.5.0 is a big fixing release, the result of two community bug
+fixing days plus numerous additional works from many people and
+reporters. The release should be fully compatible to 2.4.2, existing
+plugins and test suites. We aim at maintaining this level of ZERO reported
+bugs because it's no fun if your testing tool has bugs, is it? Under a
+condition, though: when submitting a bug report please provide
+clear information about the circumstances and a simple example which
+reproduces the problem.
+
+The issue tracker is of course not empty now. We have many remaining
+"enhacement" issues which we'll hopefully can tackle in 2014 with your
+help.
+
+For those who use older Python versions, please note that pytest is not
+automatically tested on python2.5 due to virtualenv, setuptools and tox
+not supporting it anymore. Manual verification shows that it mostly
+works fine but it's not going to be part of the automated release
+process and thus likely to break in the future.
+
+As usual, current docs are at
+
+ http://pytest.org
+
+and you can upgrade from pypi via::
+
+ pip install -U pytest
+
+Particular thanks for helping with this release go to Anatoly Bubenkoff,
+Floris Bruynooghe, Marc Abramowitz, Ralph Schmitt, Ronny Pfannschmidt,
+Donald Stufft, James Lan, Rob Dennis, Jason R. Coombs, Mathieu Agopian,
+Virgil Dupras, Bruno Oliveira, Alex Gaynor and others.
+
+have fun,
+holger krekel
+
+
+2.5.0
+-----------------------------------
+
+- dropped python2.5 from automated release testing of pytest itself
+ which means it's probably going to break soon (but still works
+ with this release we believe).
+
+- simplified and fixed implementation for calling finalizers when
+ parametrized fixtures or function arguments are involved. finalization
+ is now performed lazily at setup time instead of in the "teardown phase".
+ While this might sound odd at first, it helps to ensure that we are
+ correctly handling setup/teardown even in complex code. User-level code
+ should not be affected unless it's implementing the pytest_runtest_teardown
+ hook and expecting certain fixture instances are torn down within (very
+ unlikely and would have been unreliable anyway).
+
+- PR90: add --color=yes|no|auto option to force terminal coloring
+ mode ("auto" is default). Thanks Marc Abramowitz.
+
+- fix issue319 - correctly show unicode in assertion errors. Many
+ thanks to Floris Bruynooghe for the complete PR. Also means
+ we depend on py>=1.4.19 now.
+
+- fix issue396 - correctly sort and finalize class-scoped parametrized
+ tests independently from number of methods on the class.
+
+- refix issue323 in a better way -- parametrization should now never
+ cause Runtime Recursion errors because the underlying algorithm
+ for re-ordering tests per-scope/per-fixture is not recursive
+ anymore (it was tail-call recursive before which could lead
+ to problems for more than >966 non-function scoped parameters).
+
+- fix issue290 - there is preliminary support now for parametrizing
+ with repeated same values (sometimes useful to test if calling
+ a second time works as with the first time).
+
+- close issue240 - document precisely how pytest module importing
+ works, discuss the two common test directory layouts, and how it
+ interacts with PEP420-namespace packages.
+
+- fix issue246 fix finalizer order to be LIFO on independent fixtures
+ depending on a parametrized higher-than-function scoped fixture.
+ (was quite some effort so please bear with the complexity of this sentence :)
+ Thanks Ralph Schmitt for the precise failure example.
+
+- fix issue244 by implementing special index for parameters to only use
+ indices for paramentrized test ids
+
+- fix issue287 by running all finalizers but saving the exception
+ from the first failing finalizer and re-raising it so teardown will
+ still have failed. We reraise the first failing exception because
+ it might be the cause for other finalizers to fail.
+
+- fix ordering when mock.patch or other standard decorator-wrappings
+ are used with test methods. This fixues issue346 and should
+ help with random "xdist" collection failures. Thanks to
+ Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
+
+- fix issue357 - special case "-k" expressions to allow for
+ filtering with simple strings that are not valid python expressions.
+ Examples: "-k 1.3" matches all tests parametrized with 1.3.
+ "-k None" filters all tests that have "None" in their name
+ and conversely "-k 'not None'".
+ Previously these examples would raise syntax errors.
+
+- fix issue384 by removing the trial support code
+ since the unittest compat enhancements allow
+ trial to handle it on its own
+
+- don't hide an ImportError when importing a plugin produces one.
+ fixes issue375.
+
+- fix issue275 - allow usefixtures and autouse fixtures
+ for running doctest text files.
+
+- fix issue380 by making --resultlog only rely on longrepr instead
+ of the "reprcrash" attribute which only exists sometimes.
+
+- address issue122: allow @pytest.fixture(params=iterator) by exploding
+ into a list early on.
+
+- fix pexpect-3.0 compatibility for pytest's own tests.
+ (fixes issue386)
+
+- allow nested parametrize-value markers, thanks James Lan for the PR.
+
+- fix unicode handling with new monkeypatch.setattr(import_path, value)
+ API. Thanks Rob Dennis. Fixes issue371.
+
+- fix unicode handling with junitxml, fixes issue368.
+
+- In assertion rewriting mode on Python 2, fix the detection of coding
+ cookies. See issue #330.
+
+- make "--runxfail" turn imperative pytest.xfail calls into no ops
+ (it already did neutralize pytest.mark.xfail markers)
+
+- refine pytest / pkg_resources interactions: The AssertionRewritingHook
+ PEP302 compliant loader now registers itself with setuptools/pkg_resources
+ properly so that the pkg_resources.resource_stream method works properly.
+ Fixes issue366. Thanks for the investigations and full PR to Jason R. Coombs.
+
+- pytestconfig fixture is now session-scoped as it is the same object during the
+ whole test run. Fixes issue370.
+
+- avoid one surprising case of marker malfunction/confusion::
+
+ @pytest.mark.some(lambda arg: ...)
+ def test_function():
+
+ would not work correctly because pytest assumes @pytest.mark.some
+ gets a function to be decorated already. We now at least detect if this
+ arg is a lambda and thus the example will work. Thanks Alex Gaynor
+ for bringing it up.
+
+- xfail a test on pypy that checks wrong encoding/ascii (pypy does
+ not error out). fixes issue385.
+
+- internally make varnames() deal with classes's __init__,
+ although it's not needed by pytest itself atm. Also
+ fix caching. Fixes issue376.
+
+- fix issue221 - handle importing of namespace-package with no
+ __init__.py properly.
+
+- refactor internal FixtureRequest handling to avoid monkeypatching.
+ One of the positive user-facing effects is that the "request" object
+ can now be used in closures.
+
+- fixed version comparison in pytest.importskip(modname, minverstring)
+
+- fix issue377 by clarifying in the nose-compat docs that pytest
+ does not duplicate the unittest-API into the "plain" namespace.
+
+- fix verbose reporting for @mock'd test functions
+
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.5.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.5.1.rst
index a3a74cec626..a3a74cec626 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.5.1.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.5.1.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.5.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.5.2.rst
new file mode 100644
index 00000000000..d5cfca2dbda
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.5.2.rst
@@ -0,0 +1,64 @@
+pytest-2.5.2: fixes
+===========================================================================
+
+pytest is a mature Python testing tool with more than a 1000 tests
+against itself, passing on many different interpreters and platforms.
+
+The 2.5.2 release fixes a few bugs with two maybe-bugs remaining and
+actively being worked on (and waiting for the bug reporter's input).
+We also have a new contribution guide thanks to Piotr Banaszkiewicz
+and others.
+
+See docs at:
+
+ http://pytest.org
+
+As usual, you can upgrade from pypi via::
+
+ pip install -U pytest
+
+Thanks to the following people who contributed to this release:
+
+ Anatoly Bubenkov
+ Ronny Pfannschmidt
+ Floris Bruynooghe
+ Bruno Oliveira
+ Andreas Pelme
+ Jurko Gospodnetić
+ Piotr Banaszkiewicz
+ Simon Liedtke
+ lakka
+ Lukasz Balcerzak
+ Philippe Muller
+ Daniel Hahler
+
+have fun,
+holger krekel
+
+2.5.2
+-----------------------------------
+
+- fix issue409 -- better interoperate with cx_freeze by not
+ trying to import from collections.abc which causes problems
+ for py27/cx_freeze. Thanks Wolfgang L. for reporting and tracking it down.
+
+- fixed docs and code to use "pytest" instead of "py.test" almost everywhere.
+ Thanks Jurko Gospodnetic for the complete PR.
+
+- fix issue425: mention at end of "py.test -h" that --markers
+ and --fixtures work according to specified test path (or current dir)
+
+- fix issue413: exceptions with unicode attributes are now printed
+ correctly also on python2 and with pytest-xdist runs. (the fix
+ requires py-1.4.20)
+
+- copy, cleanup and integrate py.io capture
+ from pylib 1.4.20.dev2 (rev 13d9af95547e)
+
+- address issue416: clarify docs as to conftest.py loading semantics
+
+- fix issue429: comparing byte strings with non-ascii chars in assert
+ expressions now work better. Thanks Floris Bruynooghe.
+
+- make capfd/capsys.capture private, its unused and shouldn't be exposed
+
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.0.rst
index 36b545a28b4..36b545a28b4 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.0.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.0.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.1.rst
index 6f27c5861ca..6f27c5861ca 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.1.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.1.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.2.rst
index 4efc73a4eaa..4efc73a4eaa 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.6.2.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.2.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.3.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.3.rst
new file mode 100644
index 00000000000..ee0d2692c47
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.6.3.rst
@@ -0,0 +1,52 @@
+pytest-2.6.3: fixes and little improvements
+===========================================================================
+
+pytest is a mature Python testing tool with more than a 1100 tests
+against itself, passing on many different interpreters and platforms.
+This release is drop-in compatible to 2.5.2 and 2.6.X.
+See below for the changes and see docs at:
+
+ http://pytest.org
+
+As usual, you can upgrade from pypi via::
+
+ pip install -U pytest
+
+Thanks to all who contributed, among them:
+
+ Floris Bruynooghe
+ Oleg Sinyavskiy
+ Uwe Schmitt
+ Charles Cloud
+ Wolfgang Schnerring
+
+have fun,
+holger krekel
+
+Changes 2.6.3
+======================
+
+- fix issue575: xunit-xml was reporting collection errors as failures
+ instead of errors, thanks Oleg Sinyavskiy.
+
+- fix issue582: fix setuptools example, thanks Laszlo Papp and Ronny
+ Pfannschmidt.
+
+- Fix infinite recursion bug when pickling capture.EncodedFile, thanks
+ Uwe Schmitt.
+
+- fix issue589: fix bad interaction with numpy and others when showing
+ exceptions. Check for precise "maximum recursion depth exceed" exception
+ instead of presuming any RuntimeError is that one (implemented in py
+ dep). Thanks Charles Cloud for analysing the issue.
+
+- fix conftest related fixture visibility issue: when running with a
+ CWD outside of a test package pytest would get fixture discovery wrong.
+ Thanks to Wolfgang Schnerring for figuring out a reproducible example.
+
+- Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the
+ timeout when interactively entering pdb). Thanks Wolfgang Schnerring.
+
+- check xfail/skip also with non-python function test items. Thanks
+ Floris Bruynooghe.
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.7.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.7.0.rst
new file mode 100644
index 00000000000..4e317ff8f34
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.7.0.rst
@@ -0,0 +1,101 @@
+pytest-2.7.0: fixes, features, speed improvements
+===========================================================================
+
+pytest is a mature Python testing tool with more than a 1100 tests
+against itself, passing on many different interpreters and platforms.
+This release is supposed to be drop-in compatible to 2.6.X.
+
+See below for the changes and see docs at:
+
+ http://pytest.org
+
+As usual, you can upgrade from pypi via::
+
+ pip install -U pytest
+
+Thanks to all who contributed, among them:
+
+ Anatoly Bubenkoff
+ Floris Bruynooghe
+ Brianna Laugher
+ Eric Siegerman
+ Daniel Hahler
+ Charles Cloud
+ Tom Viner
+ Holger Peters
+ Ldiary Translations
+ almarklein
+
+have fun,
+holger krekel
+
+2.7.0 (compared to 2.6.4)
+-----------------------------
+
+- fix issue435: make reload() work when assert rewriting is active.
+ Thanks Daniel Hahler.
+
+- fix issue616: conftest.py files and their contained fixutres are now
+ properly considered for visibility, independently from the exact
+ current working directory and test arguments that are used.
+ Many thanks to Eric Siegerman and his PR235 which contains
+ systematic tests for conftest visibility and now passes.
+ This change also introduces the concept of a ``rootdir`` which
+ is printed as a new pytest header and documented in the pytest
+ customize web page.
+
+- change reporting of "diverted" tests, i.e. tests that are collected
+ in one file but actually come from another (e.g. when tests in a test class
+ come from a base class in a different file). We now show the nodeid
+ and indicate via a postfix the other file.
+
+- add ability to set command line options by environment variable PYTEST_ADDOPTS.
+
+- added documentation on the new pytest-dev teams on bitbucket and
+ github. See https://pytest.org/latest/contributing.html .
+ Thanks to Anatoly for pushing and initial work on this.
+
+- fix issue650: new option ``--docttest-ignore-import-errors`` which
+ will turn import errors in doctests into skips. Thanks Charles Cloud
+ for the complete PR.
+
+- fix issue655: work around different ways that cause python2/3
+ to leak sys.exc_info into fixtures/tests causing failures in 3rd party code
+
+- fix issue615: assertion rewriting did not correctly escape % signs
+ when formatting boolean operations, which tripped over mixing
+ booleans with modulo operators. Thanks to Tom Viner for the report,
+ triaging and fix.
+
+- implement issue351: add ability to specify parametrize ids as a callable
+ to generate custom test ids. Thanks Brianna Laugher for the idea and
+ implementation.
+
+- introduce and document new hookwrapper mechanism useful for plugins
+ which want to wrap the execution of certain hooks for their purposes.
+ This supersedes the undocumented ``__multicall__`` protocol which
+ pytest itself and some external plugins use. Note that pytest-2.8
+ is scheduled to drop supporting the old ``__multicall__``
+ and only support the hookwrapper protocol.
+
+- majorly speed up invocation of plugin hooks
+
+- use hookwrapper mechanism in builtin pytest plugins.
+
+- add a doctest ini option for doctest flags, thanks Holger Peters.
+
+- add note to docs that if you want to mark a parameter and the
+ parameter is a callable, you also need to pass in a reason to disambiguate
+ it from the "decorator" case. Thanks Tom Viner.
+
+- "python_classes" and "python_functions" options now support glob-patterns
+ for test discovery, as discussed in issue600. Thanks Ldiary Translations.
+
+- allow to override parametrized fixtures with non-parametrized ones and vice versa (bubenkoff).
+
+- fix issue463: raise specific error for 'parameterize' misspelling (pfctdayelise).
+
+- On failure, the ``sys.last_value``, ``sys.last_type`` and
+ ``sys.last_traceback`` are set, so that a user can inspect the error
+ via postmortem debugging (almarklein).
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.7.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.7.1.rst
new file mode 100644
index 00000000000..fdc71eebba9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.7.1.rst
@@ -0,0 +1,58 @@
+pytest-2.7.1: bug fixes
+=======================
+
+pytest is a mature Python testing tool with more than a 1100 tests
+against itself, passing on many different interpreters and platforms.
+This release is supposed to be drop-in compatible to 2.7.0.
+
+See below for the changes and see docs at:
+
+ http://pytest.org
+
+As usual, you can upgrade from pypi via::
+
+ pip install -U pytest
+
+Thanks to all who contributed to this release, among them:
+
+ Bruno Oliveira
+ Holger Krekel
+ Ionel Maries Cristian
+ Floris Bruynooghe
+
+Happy testing,
+The py.test Development Team
+
+
+2.7.1 (compared to 2.7.0)
+-------------------------
+
+- fix issue731: do not get confused by the braces which may be present
+ and unbalanced in an object's repr while collapsing False
+ explanations. Thanks Carl Meyer for the report and test case.
+
+- fix issue553: properly handling inspect.getsourcelines failures in
+ FixtureLookupError which would lead to an internal error,
+ obfuscating the original problem. Thanks talljosh for initial
+ diagnose/patch and Bruno Oliveira for final patch.
+
+- fix issue660: properly report scope-mismatch-access errors
+ independently from ordering of fixture arguments. Also
+ avoid the pytest internal traceback which does not provide
+ information to the user. Thanks Holger Krekel.
+
+- streamlined and documented release process. Also all versions
+ (in setup.py and documentation generation) are now read
+ from _pytest/__init__.py. Thanks Holger Krekel.
+
+- fixed docs to remove the notion that yield-fixtures are experimental.
+ They are here to stay :) Thanks Bruno Oliveira.
+
+- Support building wheels by using environment markers for the
+ requirements. Thanks Ionel Maries Cristian.
+
+- fixed regression to 2.6.4 which surfaced e.g. in lost stdout capture printing
+ when tests raised SystemExit. Thanks Holger Krekel.
+
+- reintroduced _pytest fixture of the pytester plugin which is used
+ at least by pytest-xdist.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.7.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.7.2.rst
index 69130ad623f..69130ad623f 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.7.2.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.7.2.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.2.rst
index d7028616142..d7028616142 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.2.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.2.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.3.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.3.rst
index d080ac724c1..d080ac724c1 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.3.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.3.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.4.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.4.rst
index a09629cef09..a09629cef09 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.4.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.4.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.5.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.5.rst
index 7409022a137..7409022a137 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.5.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.5.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.6.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.6.rst
index 215fae51eac..215fae51eac 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.6.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.6.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.7.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.7.rst
index d98d731064b..d98d731064b 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/announce/release-2.8.7.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.8.7.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.0.rst
new file mode 100644
index 00000000000..011b1ffb9d2
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.0.rst
@@ -0,0 +1,159 @@
+pytest-2.9.0
+============
+
+pytest is a mature Python testing tool with more than a 1100 tests
+against itself, passing on many different interpreters and platforms.
+
+See below for the changes and see docs at:
+
+ http://pytest.org
+
+As usual, you can upgrade from pypi via::
+
+ pip install -U pytest
+
+Thanks to all who contributed to this release, among them:
+
+ Anatoly Bubenkov
+ Bruno Oliveira
+ Buck Golemon
+ David Vierra
+ Florian Bruhin
+ Galaczi Endre
+ Georgy Dyuldin
+ Lukas Bednar
+ Luke Murphy
+ Marcin Biernat
+ Matt Williams
+ Michael Aquilina
+ Raphael Pierzina
+ Ronny Pfannschmidt
+ Ryan Wooden
+ Tiemo Kieft
+ TomV
+ holger krekel
+ jab
+
+
+Happy testing,
+The py.test Development Team
+
+
+2.9.0 (compared to 2.8.7)
+-------------------------
+
+**New Features**
+
+* New ``pytest.mark.skip`` mark, which unconditionally skips marked tests.
+ Thanks `@MichaelAquilina`_ for the complete PR (`#1040`_).
+
+* ``--doctest-glob`` may now be passed multiple times in the command-line.
+ Thanks `@jab`_ and `@nicoddemus`_ for the PR.
+
+* New ``-rp`` and ``-rP`` reporting options give the summary and full output
+ of passing tests, respectively. Thanks to `@codewarrior0`_ for the PR.
+
+* ``pytest.mark.xfail`` now has a ``strict`` option which makes ``XPASS``
+ tests to fail the test suite, defaulting to ``False``. There's also a
+ ``xfail_strict`` ini option that can be used to configure it project-wise.
+ Thanks `@rabbbit`_ for the request and `@nicoddemus`_ for the PR (`#1355`_).
+
+* ``Parser.addini`` now supports options of type ``bool``. Thanks
+ `@nicoddemus`_ for the PR.
+
+* New ``ALLOW_BYTES`` doctest option strips ``b`` prefixes from byte strings
+ in doctest output (similar to ``ALLOW_UNICODE``).
+ Thanks `@jaraco`_ for the request and `@nicoddemus`_ for the PR (`#1287`_).
+
+* give a hint on KeyboardInterrupt to use the --fulltrace option to show the errors,
+ this fixes `#1366`_.
+ Thanks to `@hpk42`_ for the report and `@RonnyPfannschmidt`_ for the PR.
+
+* catch IndexError exceptions when getting exception source location. This fixes
+ pytest internal error for dynamically generated code (fixtures and tests)
+ where source lines are fake by intention
+
+**Changes**
+
+* **Important**: `py.code <https://pylib.readthedocs.io/en/latest/code.html>`_ has been
+ merged into the ``pytest`` repository as ``pytest._code``. This decision
+ was made because ``py.code`` had very few uses outside ``pytest`` and the
+ fact that it was in a different repository made it difficult to fix bugs on
+ its code in a timely manner. The team hopes with this to be able to better
+ refactor out and improve that code.
+ This change shouldn't affect users, but it is useful to let users aware
+ if they encounter any strange behavior.
+
+ Keep in mind that the code for ``pytest._code`` is **private** and
+ **experimental**, so you definitely should not import it explicitly!
+
+ Please note that the original ``py.code`` is still available in
+ `pylib <https://pylib.readthedocs.io>`_.
+
+* ``pytest_enter_pdb`` now optionally receives the pytest config object.
+ Thanks `@nicoddemus`_ for the PR.
+
+* Removed code and documentation for Python 2.5 or lower versions,
+ including removal of the obsolete ``_pytest.assertion.oldinterpret`` module.
+ Thanks `@nicoddemus`_ for the PR (`#1226`_).
+
+* Comparisons now always show up in full when ``CI`` or ``BUILD_NUMBER`` is
+ found in the environment, even when -vv isn't used.
+ Thanks `@The-Compiler`_ for the PR.
+
+* ``--lf`` and ``--ff`` now support long names: ``--last-failed`` and
+ ``--failed-first`` respectively.
+ Thanks `@MichaelAquilina`_ for the PR.
+
+* Added expected exceptions to pytest.raises fail message
+
+* Collection only displays progress ("collecting X items") when in a terminal.
+ This avoids cluttering the output when using ``--color=yes`` to obtain
+ colors in CI integrations systems (`#1397`_).
+
+**Bug Fixes**
+
+* The ``-s`` and ``-c`` options should now work under ``xdist``;
+ ``Config.fromdictargs`` now represents its input much more faithfully.
+ Thanks to `@bukzor`_ for the complete PR (`#680`_).
+
+* Fix (`#1290`_): support Python 3.5's ``@`` operator in assertion rewriting.
+ Thanks `@Shinkenjoe`_ for report with test case and `@tomviner`_ for the PR.
+
+* Fix formatting utf-8 explanation messages (`#1379`_).
+ Thanks `@biern`_ for the PR.
+
+* Fix `traceback style docs`_ to describe all of the available options
+ (auto/long/short/line/native/no), with `auto` being the default since v2.6.
+ Thanks `@hackebrot`_ for the PR.
+
+* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records
+ with same name.
+
+
+.. _`traceback style docs`: https://pytest.org/latest/usage.html#modifying-python-traceback-printing
+
+.. _#1422: https://github.com/pytest-dev/pytest/issues/1422
+.. _#1379: https://github.com/pytest-dev/pytest/issues/1379
+.. _#1366: https://github.com/pytest-dev/pytest/issues/1366
+.. _#1040: https://github.com/pytest-dev/pytest/pull/1040
+.. _#680: https://github.com/pytest-dev/pytest/issues/680
+.. _#1287: https://github.com/pytest-dev/pytest/pull/1287
+.. _#1226: https://github.com/pytest-dev/pytest/pull/1226
+.. _#1290: https://github.com/pytest-dev/pytest/pull/1290
+.. _#1355: https://github.com/pytest-dev/pytest/pull/1355
+.. _#1397: https://github.com/pytest-dev/pytest/issues/1397
+.. _@biern: https://github.com/biern
+.. _@MichaelAquilina: https://github.com/MichaelAquilina
+.. _@bukzor: https://github.com/bukzor
+.. _@hpk42: https://github.com/hpk42
+.. _@nicoddemus: https://github.com/nicoddemus
+.. _@jab: https://github.com/jab
+.. _@codewarrior0: https://github.com/codewarrior0
+.. _@jaraco: https://github.com/jaraco
+.. _@The-Compiler: https://github.com/The-Compiler
+.. _@Shinkenjoe: https://github.com/Shinkenjoe
+.. _@tomviner: https://github.com/tomviner
+.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
+.. _@rabbbit: https://github.com/rabbbit
+.. _@hackebrot: https://github.com/hackebrot \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.1.rst
new file mode 100644
index 00000000000..3277da1e9b0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.1.rst
@@ -0,0 +1,67 @@
+pytest-2.9.1
+============
+
+pytest is a mature Python testing tool with more than a 1100 tests
+against itself, passing on many different interpreters and platforms.
+
+See below for the changes and see docs at:
+
+ http://pytest.org
+
+As usual, you can upgrade from pypi via::
+
+ pip install -U pytest
+
+Thanks to all who contributed to this release, among them:
+
+ Bruno Oliveira
+ Daniel Hahler
+ Dmitry Malinovsky
+ Florian Bruhin
+ Floris Bruynooghe
+ Matt Bachmann
+ Ronny Pfannschmidt
+ TomV
+ Vladimir Bolshakov
+ Zearin
+ palaviv
+
+
+Happy testing,
+The py.test Development Team
+
+
+2.9.1 (compared to 2.9.0)
+-------------------------
+
+**Bug Fixes**
+
+* Improve error message when a plugin fails to load.
+ Thanks `@nicoddemus`_ for the PR.
+
+* Fix (`#1178 <https://github.com/pytest-dev/pytest/issues/1178>`_):
+ ``pytest.fail`` with non-ascii characters raises an internal pytest error.
+ Thanks `@nicoddemus`_ for the PR.
+
+* Fix (`#469`_): junit parses report.nodeid incorrectly, when params IDs
+ contain ``::``. Thanks `@tomviner`_ for the PR (`#1431`_).
+
+* Fix (`#578 <https://github.com/pytest-dev/pytest/issues/578>`_): SyntaxErrors
+ containing non-ascii lines at the point of failure generated an internal
+ py.test error.
+ Thanks `@asottile`_ for the report and `@nicoddemus`_ for the PR.
+
+* Fix (`#1437`_): When passing in a bytestring regex pattern to parameterize
+ attempt to decode it as utf-8 ignoring errors.
+
+* Fix (`#649`_): parametrized test nodes cannot be specified to run on the command line.
+
+
+.. _#1437: https://github.com/pytest-dev/pytest/issues/1437
+.. _#469: https://github.com/pytest-dev/pytest/issues/469
+.. _#1431: https://github.com/pytest-dev/pytest/pull/1431
+.. _#649: https://github.com/pytest-dev/pytest/issues/649
+
+.. _@asottile: https://github.com/asottile
+.. _@nicoddemus: https://github.com/nicoddemus
+.. _@tomviner: https://github.com/tomviner
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.2.rst
new file mode 100644
index 00000000000..8f274cdf398
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-2.9.2.rst
@@ -0,0 +1,78 @@
+pytest-2.9.2
+============
+
+pytest is a mature Python testing tool with more than a 1100 tests
+against itself, passing on many different interpreters and platforms.
+
+See below for the changes and see docs at:
+
+ http://pytest.org
+
+As usual, you can upgrade from pypi via::
+
+ pip install -U pytest
+
+Thanks to all who contributed to this release, among them:
+
+ Adam Chainz
+ Benjamin Dopplinger
+ Bruno Oliveira
+ Florian Bruhin
+ John Towler
+ Martin Prusse
+ Meng Jue
+ MengJueM
+ Omar Kohl
+ Quentin Pradet
+ Ronny Pfannschmidt
+ Thomas Güttler
+ TomV
+ Tyler Goodlet
+
+
+Happy testing,
+The py.test Development Team
+
+
+2.9.2 (compared to 2.9.1)
+---------------------------
+
+**Bug Fixes**
+
+* fix `#510`_: skip tests where one parameterize dimension was empty
+ thanks Alex Stapleton for the Report and `@RonnyPfannschmidt`_ for the PR
+
+* Fix Xfail does not work with condition keyword argument.
+ Thanks `@astraw38`_ for reporting the issue (`#1496`_) and `@tomviner`_
+ for PR the (`#1524`_).
+
+* Fix win32 path issue when putting custom config file with absolute path
+ in ``pytest.main("-c your_absolute_path")``.
+
+* Fix maximum recursion depth detection when raised error class is not aware
+ of unicode/encoded bytes.
+ Thanks `@prusse-martin`_ for the PR (`#1506`_).
+
+* Fix ``pytest.mark.skip`` mark when used in strict mode.
+ Thanks `@pquentin`_ for the PR and `@RonnyPfannschmidt`_ for
+ showing how to fix the bug.
+
+* Minor improvements and fixes to the documentation.
+ Thanks `@omarkohl`_ for the PR.
+
+* Fix ``--fixtures`` to show all fixture definitions as opposed to just
+ one per fixture name.
+ Thanks to `@hackebrot`_ for the PR.
+
+.. _#510: https://github.com/pytest-dev/pytest/issues/510
+.. _#1506: https://github.com/pytest-dev/pytest/pull/1506
+.. _#1496: https://github.com/pytest-dev/pytest/issue/1496
+.. _#1524: https://github.com/pytest-dev/pytest/issue/1524
+
+.. _@astraw38: https://github.com/astraw38
+.. _@hackebrot: https://github.com/hackebrot
+.. _@omarkohl: https://github.com/omarkohl
+.. _@pquentin: https://github.com/pquentin
+.. _@prusse-martin: https://github.com/prusse-martin
+.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
+.. _@tomviner: https://github.com/tomviner
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.0.rst
new file mode 100644
index 00000000000..4bf1e8534ec
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.0.rst
@@ -0,0 +1,82 @@
+pytest-3.0.0
+============
+
+The pytest team is proud to announce the 3.0.0 release!
+
+pytest is a mature Python testing tool with more than a 1600 tests
+against itself, passing on many different interpreters and platforms.
+
+This release contains a lot of bugs fixes and improvements, and much of
+the work done on it was possible because of the 2016 Sprint[1], which
+was funded by an indiegogo campaign which raised over US$12,000 with
+nearly 100 backers.
+
+There's a "What's new in pytest 3.0" [2] blog post highlighting the
+major features in this release.
+
+To see the complete changelog and documentation, please visit:
+
+ http://docs.pytest.org
+
+As usual, you can upgrade from pypi via:
+
+ pip install -U pytest
+
+Thanks to all who contributed to this release, among them:
+
+ AbdealiJK
+ Ana Ribeiro
+ Antony Lee
+ Brandon W Maister
+ Brianna Laugher
+ Bruno Oliveira
+ Ceridwen
+ Christian Boelsen
+ Daniel Hahler
+ Danielle Jenkins
+ Dave Hunt
+ Diego Russo
+ Dmitry Dygalo
+ Edoardo Batini
+ Eli Boyarski
+ Florian Bruhin
+ Floris Bruynooghe
+ Greg Price
+ Guyzmo
+ HEAD KANGAROO
+ JJ
+ Javi Romero
+ Javier Domingo Cansino
+ Kale Kundert
+ Kalle Bronsen
+ Marius Gedminas
+ Matt Williams
+ Mike Lundy
+ Oliver Bestwalter
+ Omar Kohl
+ Raphael Pierzina
+ RedBeardCode
+ Roberto Polli
+ Romain Dorgueil
+ Roman Bolshakov
+ Ronny Pfannschmidt
+ Stefan Zimmermann
+ Steffen Allner
+ Tareq Alayan
+ Ted Xiao
+ Thomas Grainger
+ Tom Viner
+ TomV
+ Vasily Kuznetsov
+ aostr
+ marscher
+ palaviv
+ satoru
+ taschini
+
+
+Happy testing,
+The Pytest Development Team
+
+[1] http://blog.pytest.org/2016/pytest-development-sprint/
+[2] http://blog.pytest.org/2016/whats-new-in-pytest-30/
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.1.rst
new file mode 100644
index 00000000000..9fb38047b9c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.1.rst
@@ -0,0 +1,26 @@
+pytest-3.0.1
+============
+
+pytest 3.0.1 has just been released to PyPI.
+
+This release fixes some regressions reported in version 3.0.0, being a
+drop-in replacement. To upgrade:
+
+ pip install --upgrade pytest
+
+The changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+ Adam Chainz
+ Andrew Svetlov
+ Bruno Oliveira
+ Daniel Hahler
+ Dmitry Dygalo
+ Florian Bruhin
+ Marcin Bachry
+ Ronny Pfannschmidt
+ matthiasha
+
+Happy testing,
+The py.test Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.2.rst
new file mode 100644
index 00000000000..9d1c05f2d45
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.2.rst
@@ -0,0 +1,24 @@
+pytest-3.0.2
+============
+
+pytest 3.0.2 has just been released to PyPI.
+
+This release fixes some regressions and bugs reported in version 3.0.1, being a
+drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Ahn Ki-Wook
+* Bruno Oliveira
+* Florian Bruhin
+* Jordan Guymon
+* Raphael Pierzina
+* Ronny Pfannschmidt
+* mbyt
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.3.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.3.rst
new file mode 100644
index 00000000000..f00172195db
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.3.rst
@@ -0,0 +1,27 @@
+pytest-3.0.3
+============
+
+pytest 3.0.3 has just been released to PyPI.
+
+This release fixes some regressions and bugs reported in the last version,
+being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Bruno Oliveira
+* Florian Bruhin
+* Floris Bruynooghe
+* Huayi Zhang
+* Lev Maximov
+* Raquel Alegre
+* Ronny Pfannschmidt
+* Roy Williams
+* Tyler Goodlet
+* mbyt
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.4.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.4.rst
new file mode 100644
index 00000000000..852057037dd
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.4.rst
@@ -0,0 +1,29 @@
+pytest-3.0.4
+============
+
+pytest 3.0.4 has just been released to PyPI.
+
+This release fixes some regressions and bugs reported in the last version,
+being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Bruno Oliveira
+* Dan Wandschneider
+* Florian Bruhin
+* Georgy Dyuldin
+* Grigorii Eremeev
+* Jason R. Coombs
+* Manuel Jacob
+* Mathieu Clabaut
+* Michael Seifert
+* Nikolaus Rath
+* Ronny Pfannschmidt
+* Tom V
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.5.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.5.rst
new file mode 100644
index 00000000000..3e2419d7e5d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.5.rst
@@ -0,0 +1,27 @@
+pytest-3.0.5
+============
+
+pytest 3.0.5 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Ana Vojnovic
+* Bruno Oliveira
+* Daniel Hahler
+* Duncan Betts
+* Igor Starikov
+* Ismail
+* Luke Murphy
+* Ned Batchelder
+* Ronny Pfannschmidt
+* Sebastian Ramacher
+* nmundar
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.6.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.6.rst
new file mode 100644
index 00000000000..2988b9cb3b8
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.6.rst
@@ -0,0 +1,33 @@
+pytest-3.0.6
+============
+
+pytest 3.0.6 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+
+Thanks to all who contributed to this release, among them:
+
+* Andreas Pelme
+* Bruno Oliveira
+* Dmitry Malinovsky
+* Eli Boyarski
+* Jakub Wilk
+* Jeff Widman
+* Loïc Estève
+* Luke Murphy
+* Miro Hrončok
+* Oscar Hellström
+* Peter Heatwole
+* Philippe Ombredanne
+* Ronny Pfannschmidt
+* Rutger Prins
+* Stefan Scherfke
+
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.7.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.7.rst
new file mode 100644
index 00000000000..591557aa787
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.0.7.rst
@@ -0,0 +1,33 @@
+pytest-3.0.7
+============
+
+pytest 3.0.7 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Anthony Sottile
+* Barney Gale
+* Bruno Oliveira
+* Florian Bruhin
+* Floris Bruynooghe
+* Ionel Cristian Mărieș
+* Katerina Koukiou
+* NODA, Kai
+* Omer Hadari
+* Patrick Hayes
+* Ran Benita
+* Ronny Pfannschmidt
+* Victor Uriarte
+* Vidar Tonaas Fauske
+* Ville Skyttä
+* fbjorn
+* mbyt
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.0.rst
new file mode 100644
index 00000000000..99cc6bdbe20
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.0.rst
@@ -0,0 +1,61 @@
+pytest-3.1.0
+=======================================
+
+The pytest team is proud to announce the 3.1.0 release!
+
+pytest is a mature Python testing tool with more than a 1600 tests
+against itself, passing on many different interpreters and platforms.
+
+This release contains a bugs fixes and improvements, so users are encouraged
+to take a look at the CHANGELOG:
+
+http://doc.pytest.org/en/latest/changelog.html
+
+For complete documentation, please visit:
+
+ http://docs.pytest.org
+
+As usual, you can upgrade from pypi via:
+
+ pip install -U pytest
+
+Thanks to all who contributed to this release, among them:
+
+* Anthony Sottile
+* Ben Lloyd
+* Bruno Oliveira
+* David Giese
+* David Szotten
+* Dmitri Pribysh
+* Florian Bruhin
+* Florian Schulze
+* Floris Bruynooghe
+* John Towler
+* Jonas Obrist
+* Katerina Koukiou
+* Kodi Arfer
+* Krzysztof Szularz
+* Lev Maximov
+* Loïc Estève
+* Luke Murphy
+* Manuel Krebber
+* Matthew Duck
+* Matthias Bussonnier
+* Michael Howitz
+* Michal Wajszczuk
+* Paweł Adamczak
+* Rafael Bertoldi
+* Ravi Chandra
+* Ronny Pfannschmidt
+* Skylar Downes
+* Thomas Kriechbaumer
+* Vitaly Lashmanov
+* Vlad Dragos
+* Wheerd
+* Xander Johnson
+* mandeep
+* reut
+
+
+Happy testing,
+The Pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.1.rst
new file mode 100644
index 00000000000..370b8fd7355
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.1.rst
@@ -0,0 +1,23 @@
+pytest-3.1.1
+=======================================
+
+pytest 3.1.1 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Bruno Oliveira
+* Florian Bruhin
+* Floris Bruynooghe
+* Jason R. Coombs
+* Ronny Pfannschmidt
+* wanghui
+
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.2.rst
new file mode 100644
index 00000000000..60168a857ba
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.2.rst
@@ -0,0 +1,23 @@
+pytest-3.1.2
+=======================================
+
+pytest 3.1.2 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Andreas Pelme
+* ApaDoctor
+* Bruno Oliveira
+* Florian Bruhin
+* Ronny Pfannschmidt
+* Segev Finer
+
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.3.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.3.rst
new file mode 100644
index 00000000000..a55280626ba
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.1.3.rst
@@ -0,0 +1,23 @@
+pytest-3.1.3
+=======================================
+
+pytest 3.1.3 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Antoine Legrand
+* Bruno Oliveira
+* Max Moroz
+* Raphael Pierzina
+* Ronny Pfannschmidt
+* Ryan Fitzpatrick
+
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.0.rst
new file mode 100644
index 00000000000..4d2830edd2d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.0.rst
@@ -0,0 +1,48 @@
+pytest-3.2.0
+=======================================
+
+The pytest team is proud to announce the 3.2.0 release!
+
+pytest is a mature Python testing tool with more than a 1600 tests
+against itself, passing on many different interpreters and platforms.
+
+This release contains a number of bugs fixes and improvements, so users are encouraged
+to take a look at the CHANGELOG:
+
+ http://doc.pytest.org/en/latest/changelog.html
+
+For complete documentation, please visit:
+
+ http://docs.pytest.org
+
+As usual, you can upgrade from pypi via:
+
+ pip install -U pytest
+
+Thanks to all who contributed to this release, among them:
+
+* Alex Hartoto
+* Andras Tim
+* Bruno Oliveira
+* Daniel Hahler
+* Florian Bruhin
+* Floris Bruynooghe
+* John Still
+* Jordan Moldow
+* Kale Kundert
+* Lawrence Mitchell
+* Llandy Riveron Del Risco
+* Maik Figura
+* Martin Altmayer
+* Mihai Capotă
+* Nathaniel Waisbrot
+* Nguyễn Hồng Quân
+* Pauli Virtanen
+* Raphael Pierzina
+* Ronny Pfannschmidt
+* Segev Finer
+* V.Kuznetsov
+
+
+Happy testing,
+The Pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.1.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.1.rst
new file mode 100644
index 00000000000..899ffcd4b4a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.1.rst
@@ -0,0 +1,22 @@
+pytest-3.2.1
+=======================================
+
+pytest 3.2.1 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Alex Gaynor
+* Bruno Oliveira
+* Florian Bruhin
+* Ronny Pfannschmidt
+* Srinivas Reddy Thatiparthy
+
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.2.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.2.rst
new file mode 100644
index 00000000000..599bf872775
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.2.rst
@@ -0,0 +1,28 @@
+pytest-3.2.2
+=======================================
+
+pytest 3.2.2 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Andreas Pelme
+* Antonio Hidalgo
+* Bruno Oliveira
+* Felipe Dau
+* Fernando Macedo
+* Jesús Espino
+* Joan Massich
+* Joe Talbott
+* Kirill Pinchuk
+* Ronny Pfannschmidt
+* Xuan Luong
+
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.3.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.3.rst
new file mode 100644
index 00000000000..589374974d9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.3.rst
@@ -0,0 +1,23 @@
+pytest-3.2.3
+=======================================
+
+pytest 3.2.3 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Bruno Oliveira
+* Evan
+* Joe Hamman
+* Oliver Bestwalter
+* Ronny Pfannschmidt
+* Xuan Luong
+
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.4.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.4.rst
new file mode 100644
index 00000000000..44bfcc27e29
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.4.rst
@@ -0,0 +1,36 @@
+pytest-3.2.4
+=======================================
+
+pytest 3.2.4 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Bruno Oliveira
+* Christian Boelsen
+* Christoph Buchner
+* Daw-Ran Liou
+* Florian Bruhin
+* Franck Michea
+* Leonard Lausen
+* Matty G
+* Owen Tuz
+* Pavel Karateev
+* Pierre GIRAUD
+* Ronny Pfannschmidt
+* Stephen Finucane
+* Sviatoslav Abakumov
+* Thomas Hisch
+* Tom Dalton
+* Xuan Luong
+* Yorgos Pagles
+* Семён Марьясин
+
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.5.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.5.rst
new file mode 100644
index 00000000000..a520ce2b333
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.2.5.rst
@@ -0,0 +1,18 @@
+pytest-3.2.5
+=======================================
+
+pytest 3.2.5 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+* Bruno Oliveira
+
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.3.0.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.3.0.rst
new file mode 100644
index 00000000000..e0740e7d592
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/release-3.3.0.rst
@@ -0,0 +1,50 @@
+pytest-3.3.0
+=======================================
+
+The pytest team is proud to announce the 3.3.0 release!
+
+pytest is a mature Python testing tool with more than a 1600 tests
+against itself, passing on many different interpreters and platforms.
+
+This release contains a number of bugs fixes and improvements, so users are encouraged
+to take a look at the CHANGELOG:
+
+ http://doc.pytest.org/en/latest/changelog.html
+
+For complete documentation, please visit:
+
+ http://docs.pytest.org
+
+As usual, you can upgrade from pypi via:
+
+ pip install -U pytest
+
+Thanks to all who contributed to this release, among them:
+
+* Anthony Sottile
+* Bruno Oliveira
+* Ceridwen
+* Daniel Hahler
+* Dirk Thomas
+* Dmitry Malinovsky
+* Florian Bruhin
+* George Y. Kussumoto
+* Hugo
+* Jesús Espino
+* Joan Massich
+* Ofir
+* OfirOshir
+* Ronny Pfannschmidt
+* Samuel Dion-Girardeau
+* Srinivas Reddy Thatiparthy
+* Sviatoslav Abakumov
+* Tarcisio Fischer
+* Thomas Hisch
+* Tyler Goodlet
+* hugovk
+* je
+* prokaktus
+
+
+Happy testing,
+The Pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/sprint2016.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/sprint2016.rst
new file mode 100644
index 00000000000..8e706589876
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/announce/sprint2016.rst
@@ -0,0 +1,64 @@
+python testing sprint June 20th-26th 2016
+======================================================
+
+.. image:: ../img/freiburg2.jpg
+ :width: 400
+
+The pytest core group held the biggest sprint
+in its history in June 2016, taking place in the black forest town Freiburg
+in Germany. In February 2016 we started a `funding
+campaign on Indiegogo to cover expenses
+<http://igg.me/at/pytest-sprint/x/4034848>`_ The page also mentions
+some preliminary topics:
+
+- improving pytest-xdist test scheduling to take into account
+ fixture setups and explicit user hints.
+
+- provide info on fixture dependencies during --collect-only
+
+- tying pytest-xdist to tox so that you can do "py.test -e py34"
+ to run tests in a particular tox-managed virtualenv. Also
+ look into making pytest-xdist use tox environments on
+ remote ssh-sides so that remote dependency management becomes
+ easier.
+
+- refactoring the fixture system so more people understand it :)
+
+- integrating PyUnit setup methods as autouse fixtures.
+ possibly adding ways to influence ordering of same-scoped
+ fixtures (so you can make a choice of which fixtures come
+ before others)
+
+- fixing bugs and issues from the tracker, really an endless source :)
+
+
+Participants
+--------------
+
+Over 20 participants took part from 4 continents, including employees
+from Splunk, Personalkollen, Cobe.io, FanDuel and Dolby. Some newcomers
+mixed with developers who have worked on pytest since its beginning, and
+of course everyone in between.
+
+
+Sprint organisation, schedule
+-------------------------------
+
+People arrived in Freiburg on the 19th, with sprint development taking
+place on 20th, 21st, 22nd, 24th and 25th. On the 23rd we took a break
+day for some hot hiking in the Black Forest.
+
+Sprint activity was organised heavily around pairing, with plenty of group
+discusssions to take advantage of the high bandwidth, and lightning talks
+as well.
+
+
+Money / funding
+---------------
+
+
+The Indiegogo campaign aimed for 11000 USD and in the end raised over
+12000, to reimburse travel costs, pay for a sprint venue and catering.
+
+Excess money is reserved for further sprint/travel funding for pytest/tox
+contributors.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/assert.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/assert.rst
new file mode 100644
index 00000000000..4a852978ed2
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/assert.rst
@@ -0,0 +1,299 @@
+
+The writing and reporting of assertions in tests
+==================================================
+
+.. _`assertfeedback`:
+.. _`assert with the assert statement`:
+.. _`assert`:
+
+
+Asserting with the ``assert`` statement
+---------------------------------------------------------
+
+``pytest`` allows you to use the standard python ``assert`` for verifying
+expectations and values in Python tests. For example, you can write the
+following::
+
+ # content of test_assert1.py
+ def f():
+ return 3
+
+ def test_function():
+ assert f() == 4
+
+to assert that your function returns a certain value. If this assertion fails
+you will see the return value of the function call::
+
+ $ pytest test_assert1.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 1 item
+
+ test_assert1.py F [100%]
+
+ ================================= FAILURES =================================
+ ______________________________ test_function _______________________________
+
+ def test_function():
+ > assert f() == 4
+ E assert 3 == 4
+ E + where 3 = f()
+
+ test_assert1.py:5: AssertionError
+ ========================= 1 failed in 0.12 seconds =========================
+
+``pytest`` has support for showing the values of the most common subexpressions
+including calls, attributes, comparisons, and binary and unary
+operators. (See :ref:`tbreportdemo`). This allows you to use the
+idiomatic python constructs without boilerplate code while not losing
+introspection information.
+
+However, if you specify a message with the assertion like this::
+
+ assert a % 2 == 0, "value was odd, should be even"
+
+then no assertion introspection takes places at all and the message
+will be simply shown in the traceback.
+
+See :ref:`assert-details` for more information on assertion introspection.
+
+.. _`assertraises`:
+
+Assertions about expected exceptions
+------------------------------------------
+
+In order to write assertions about raised exceptions, you can use
+``pytest.raises`` as a context manager like this::
+
+ import pytest
+
+ def test_zero_division():
+ with pytest.raises(ZeroDivisionError):
+ 1 / 0
+
+and if you need to have access to the actual exception info you may use::
+
+ def test_recursion_depth():
+ with pytest.raises(RuntimeError) as excinfo:
+ def f():
+ f()
+ f()
+ assert 'maximum recursion' in str(excinfo.value)
+
+``excinfo`` is a ``ExceptionInfo`` instance, which is a wrapper around
+the actual exception raised. The main attributes of interest are
+``.type``, ``.value`` and ``.traceback``.
+
+.. versionchanged:: 3.0
+
+In the context manager form you may use the keyword argument
+``message`` to specify a custom failure message::
+
+ >>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):
+ ... pass
+ ... Failed: Expecting ZeroDivisionError
+
+If you want to write test code that works on Python 2.4 as well,
+you may also use two other ways to test for an expected exception::
+
+ pytest.raises(ExpectedException, func, *args, **kwargs)
+ pytest.raises(ExpectedException, "func(*args, **kwargs)")
+
+both of which execute the specified function with args and kwargs and
+asserts that the given ``ExpectedException`` is raised. The reporter will
+provide you with helpful output in case of failures such as *no
+exception* or *wrong exception*.
+
+Note that it is also possible to specify a "raises" argument to
+``pytest.mark.xfail``, which checks that the test is failing in a more
+specific way than just having any exception raised::
+
+ @pytest.mark.xfail(raises=IndexError)
+ def test_f():
+ f()
+
+Using ``pytest.raises`` is likely to be better for cases where you are testing
+exceptions your own code is deliberately raising, whereas using
+``@pytest.mark.xfail`` with a check function is probably better for something
+like documenting unfixed bugs (where the test describes what "should" happen)
+or bugs in dependencies.
+
+Also, the context manager form accepts a ``match`` keyword parameter to test
+that a regular expression matches on the string representation of an exception
+(like the ``TestCase.assertRaisesRegexp`` method from ``unittest``)::
+
+ import pytest
+
+ def myfunc():
+ raise ValueError("Exception 123 raised")
+
+ def test_match():
+ with pytest.raises(ValueError, match=r'.* 123 .*'):
+ myfunc()
+
+The regexp parameter of the ``match`` method is matched with the ``re.search``
+function. So in the above example ``match='123'`` would have worked as
+well.
+
+
+.. _`assertwarns`:
+
+Assertions about expected warnings
+-----------------------------------------
+
+.. versionadded:: 2.8
+
+You can check that code raises a particular warning using
+:ref:`pytest.warns <warns>`.
+
+
+.. _newreport:
+
+Making use of context-sensitive comparisons
+-------------------------------------------------
+
+.. versionadded:: 2.0
+
+``pytest`` has rich support for providing context-sensitive information
+when it encounters comparisons. For example::
+
+ # content of test_assert2.py
+
+ def test_set_comparison():
+ set1 = set("1308")
+ set2 = set("8035")
+ assert set1 == set2
+
+if you run this module::
+
+ $ pytest test_assert2.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 1 item
+
+ test_assert2.py F [100%]
+
+ ================================= FAILURES =================================
+ ___________________________ test_set_comparison ____________________________
+
+ def test_set_comparison():
+ set1 = set("1308")
+ set2 = set("8035")
+ > assert set1 == set2
+ E AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
+ E Extra items in the left set:
+ E '1'
+ E Extra items in the right set:
+ E '5'
+ E Use -v to get the full diff
+
+ test_assert2.py:5: AssertionError
+ ========================= 1 failed in 0.12 seconds =========================
+
+Special comparisons are done for a number of cases:
+
+* comparing long strings: a context diff is shown
+* comparing long sequences: first failing indices
+* comparing dicts: different entries
+
+See the :ref:`reporting demo <tbreportdemo>` for many more examples.
+
+Defining your own assertion comparison
+----------------------------------------------
+
+It is possible to add your own detailed explanations by implementing
+the ``pytest_assertrepr_compare`` hook.
+
+.. autofunction:: _pytest.hookspec.pytest_assertrepr_compare
+ :noindex:
+
+As an example consider adding the following hook in a :ref:`conftest.py <conftest.py>`
+file which provides an alternative explanation for ``Foo`` objects::
+
+ # content of conftest.py
+ from test_foocompare import Foo
+ def pytest_assertrepr_compare(op, left, right):
+ if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
+ return ['Comparing Foo instances:',
+ ' vals: %s != %s' % (left.val, right.val)]
+
+now, given this test module::
+
+ # content of test_foocompare.py
+ class Foo(object):
+ def __init__(self, val):
+ self.val = val
+
+ def __eq__(self, other):
+ return self.val == other.val
+
+ def test_compare():
+ f1 = Foo(1)
+ f2 = Foo(2)
+ assert f1 == f2
+
+you can run the test module and get the custom output defined in
+the conftest file::
+
+ $ pytest -q test_foocompare.py
+ F [100%]
+ ================================= FAILURES =================================
+ _______________________________ test_compare _______________________________
+
+ def test_compare():
+ f1 = Foo(1)
+ f2 = Foo(2)
+ > assert f1 == f2
+ E assert Comparing Foo instances:
+ E vals: 1 != 2
+
+ test_foocompare.py:11: AssertionError
+ 1 failed in 0.12 seconds
+
+.. _assert-details:
+.. _`assert introspection`:
+
+Advanced assertion introspection
+----------------------------------
+
+.. versionadded:: 2.1
+
+
+Reporting details about a failing assertion is achieved by rewriting assert
+statements before they are run. Rewritten assert statements put introspection
+information into the assertion failure message. ``pytest`` only rewrites test
+modules directly discovered by its test collection process, so asserts in
+supporting modules which are not themselves test modules will not be rewritten.
+
+.. note::
+
+ ``pytest`` rewrites test modules on import by using an import
+ hook to write new ``pyc`` files. Most of the time this works transparently.
+ However, if you are messing with import yourself, the import hook may
+ interfere.
+
+ If this is the case you have two options:
+
+ * Disable rewriting for a specific module by adding the string
+ ``PYTEST_DONT_REWRITE`` to its docstring.
+
+ * Disable rewriting for all modules by using ``--assert=plain``.
+
+ Additionally, rewriting will fail silently if it cannot write new ``.pyc`` files,
+ i.e. in a read-only filesystem or a zipfile.
+
+
+For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_.
+
+.. versionadded:: 2.1
+ Add assert rewriting as an alternate introspection technique.
+
+.. versionchanged:: 2.1
+ Introduce the ``--assert`` option. Deprecate ``--no-assert`` and
+ ``--nomagic``.
+
+.. versionchanged:: 3.0
+ Removes the ``--no-assert`` and ``--nomagic`` options.
+ Removes the ``--assert=reinterp`` option.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/backwards-compatibility.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/backwards-compatibility.rst
new file mode 100644
index 00000000000..84f2c43edaa
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/backwards-compatibility.rst
@@ -0,0 +1,105 @@
+.. _backwards-compatibility:
+
+Backwards Compatibility Policy
+==============================
+
+Keeping backwards compatibility has a very high priority in the pytest project. Although we have deprecated functionality over the years, most of it is still supported. All deprecations in pytest were done because simpler or more efficient ways of accomplishing the same tasks have emerged, making the old way of doing things unnecessary.
+
+With the pytest 3.0 release we introduced a clear communication scheme for when we will actually remove the old busted joint and politely ask you to use the new hotness instead, while giving you enough time to adjust your tests or raise concerns if there are valid reasons to keep deprecated functionality around.
+
+To communicate changes we are already issuing deprecation warnings, but they are not displayed by default. In pytest 3.0 we changed the default setting so that pytest deprecation warnings are displayed if not explicitly silenced (with ``--disable-pytest-warnings``).
+
+We will only remove deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we will not remove it in 4.0 but in 5.0).
+
+
+Deprecation Roadmap
+-------------------
+
+This page lists deprecated features and when we plan to remove them. It is important to list the feature, the version where it got deprecated and the version we plan to remove it.
+
+Following our deprecation policy, we should aim to keep features for *at least* two minor versions after it was considered deprecated.
+
+
+Future Releases
+~~~~~~~~~~~~~~~
+
+3.4
+^^^
+
+**Old style classes**
+
+Issue: `#2147 <https://github.com/pytest-dev/pytest/issues/2147>`_.
+
+Deprecated in ``3.2``.
+
+4.0
+^^^
+
+**Yield tests**
+
+Deprecated in ``3.0``.
+
+**pytest-namespace hook**
+
+deprecated in ``3.2``.
+
+**Marks in parameter sets**
+
+Deprecated in ``3.2``.
+
+**--result-log**
+
+Deprecated in ``3.0``.
+
+See `#830 <https://github.com/pytest-dev/pytest/issues/830>`_ for more information. Suggested alternative: `pytest-tap <https://pypi.python.org/pypi/pytest-tap>`_.
+
+**metafunc.addcall**
+
+Issue: `#2876 <https://github.com/pytest-dev/pytest/issues/2876>`_.
+
+Deprecated in ``3.3``.
+
+**pytest_plugins in non-toplevel conftests**
+
+There is a deep conceptual confusion as ``conftest.py`` files themselves are activated/deactivated based on path, but the plugins they depend on aren't.
+
+Issue: `#2639 <https://github.com/pytest-dev/pytest/issues/2639>`_.
+
+Not yet officially deprecated.
+
+**passing a single string to pytest.main()**
+
+Pass a list of strings to ``pytest.main()`` instead.
+
+Deprecated in ``3.1``.
+
+**[pytest] section in setup.cfg**
+
+Use ``[tool:pytest]`` instead for compatibility with other tools.
+
+Deprecated in ``3.0``.
+
+Past Releases
+~~~~~~~~~~~~~
+
+3.0
+^^^
+
+* The following deprecated commandline options were removed:
+
+ * ``--genscript``: no longer supported;
+ * ``--no-assert``: use ``--assert=plain`` instead;
+ * ``--nomagic``: use ``--assert=plain`` instead;
+ * ``--report``: use ``-r`` instead;
+
+* Removed all ``py.test-X*`` entry points. The versioned, suffixed entry points
+ were never documented and a leftover from a pre-virtualenv era. These entry
+ points also created broken entry points in wheels, so removing them also
+ removes a source of confusion for users.
+
+
+
+3.3
+^^^
+
+* Dropped support for EOL Python 2.6 and 3.3. \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/bash-completion.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/bash-completion.rst
new file mode 100644
index 00000000000..81fe62183fb
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/bash-completion.rst
@@ -0,0 +1,28 @@
+
+.. _bash_completion:
+
+Setting up bash completion
+==========================
+
+When using bash as your shell, ``pytest`` can use argcomplete
+(https://argcomplete.readthedocs.io/) for auto-completion.
+For this ``argcomplete`` needs to be installed **and** enabled.
+
+Install argcomplete using::
+
+ sudo pip install 'argcomplete>=0.5.7'
+
+For global activation of all argcomplete enabled python applications run::
+
+ sudo activate-global-python-argcomplete
+
+For permanent (but not global) ``pytest`` activation, use::
+
+ register-python-argcomplete pytest >> ~/.bashrc
+
+For one-time activation of argcomplete for ``pytest`` only, use::
+
+ eval "$(register-python-argcomplete pytest)"
+
+
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/builtin.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/builtin.rst
new file mode 100644
index 00000000000..d11eb5606e0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/builtin.rst
@@ -0,0 +1,161 @@
+
+.. _`pytest helpers`:
+
+Pytest API and builtin fixtures
+================================================
+
+This is a list of ``pytest.*`` API functions and fixtures.
+
+For information on plugin hooks and objects, see :ref:`plugins`.
+
+For information on the ``pytest.mark`` mechanism, see :ref:`mark`.
+
+For the below objects, you can also interactively ask for help, e.g. by
+typing on the Python interactive prompt something like::
+
+ import pytest
+ help(pytest)
+
+.. currentmodule:: pytest
+
+Invoking pytest interactively
+---------------------------------------------------
+
+.. autofunction:: main
+
+More examples at :ref:`pytest.main-usage`
+
+
+Helpers for assertions about Exceptions/Warnings
+--------------------------------------------------------
+
+.. autofunction:: raises
+
+Examples at :ref:`assertraises`.
+
+.. autofunction:: deprecated_call
+
+Comparing floating point numbers
+--------------------------------
+
+.. autofunction:: approx
+
+Raising a specific test outcome
+--------------------------------------
+
+You can use the following functions in your test, fixture or setup
+functions to force a certain test outcome. Note that most often
+you can rather use declarative marks, see :ref:`skipping`.
+
+.. autofunction:: _pytest.outcomes.fail
+.. autofunction:: _pytest.outcomes.skip
+.. autofunction:: _pytest.outcomes.importorskip
+.. autofunction:: _pytest.outcomes.xfail
+.. autofunction:: _pytest.outcomes.exit
+
+Fixtures and requests
+-----------------------------------------------------
+
+To mark a fixture function:
+
+.. autofunction:: _pytest.fixtures.fixture
+
+Tutorial at :ref:`fixtures`.
+
+The ``request`` object that can be used from fixture functions.
+
+.. autoclass:: _pytest.fixtures.FixtureRequest()
+ :members:
+
+
+.. _builtinfixtures:
+.. _builtinfuncargs:
+
+Builtin fixtures/function arguments
+-----------------------------------------
+
+You can ask for available builtin or project-custom
+:ref:`fixtures <fixtures>` by typing::
+
+ $ pytest -q --fixtures
+ cache
+ Return a cache object that can persist state between testing sessions.
+
+ cache.get(key, default)
+ cache.set(key, value)
+
+ Keys must be a ``/`` separated value, where the first part is usually the
+ name of your plugin or application to avoid clashes with other cache users.
+
+ Values can be any object handled by the json stdlib module.
+ capsys
+ Enable capturing of writes to sys.stdout/sys.stderr and make
+ captured output available via ``capsys.readouterr()`` method calls
+ which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text``
+ objects.
+ capsysbinary
+ Enable capturing of writes to sys.stdout/sys.stderr and make
+ captured output available via ``capsys.readouterr()`` method calls
+ which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``bytes``
+ objects.
+ capfd
+ Enable capturing of writes to file descriptors 1 and 2 and make
+ captured output available via ``capfd.readouterr()`` method calls
+ which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text``
+ objects.
+ capfdbinary
+ Enable capturing of write to file descriptors 1 and 2 and make
+ captured output available via ``capfdbinary.readouterr`` method calls
+ which return a ``(out, err)`` tuple. ``out`` and ``err`` will be
+ ``bytes`` objects.
+ doctest_namespace
+ Inject names into the doctest namespace.
+ pytestconfig
+ the pytest config object with access to command line opts.
+ record_xml_property
+ Add extra xml properties to the tag for the calling test.
+ The fixture is callable with ``(name, value)``, with value being automatically
+ xml-encoded.
+ caplog
+ Access and control log capturing.
+
+ Captured logs are available through the following methods::
+
+ * caplog.text() -> string containing formatted log output
+ * caplog.records() -> list of logging.LogRecord instances
+ * caplog.record_tuples() -> list of (logger_name, level, message) tuples
+ monkeypatch
+ The returned ``monkeypatch`` fixture provides these
+ helper methods to modify objects, dictionaries or os.environ::
+
+ monkeypatch.setattr(obj, name, value, raising=True)
+ monkeypatch.delattr(obj, name, raising=True)
+ monkeypatch.setitem(mapping, name, value)
+ monkeypatch.delitem(obj, name, raising=True)
+ monkeypatch.setenv(name, value, prepend=False)
+ monkeypatch.delenv(name, value, raising=True)
+ monkeypatch.syspath_prepend(path)
+ monkeypatch.chdir(path)
+
+ All modifications will be undone after the requesting
+ test function or fixture has finished. The ``raising``
+ parameter determines if a KeyError or AttributeError
+ will be raised if the set/deletion operation has no target.
+ recwarn
+ Return a WarningsRecorder instance that provides these methods:
+
+ * ``pop(category=None)``: return last warning matching the category.
+ * ``clear()``: clear list of warnings
+
+ See http://docs.python.org/library/warnings.html for information
+ on warning categories.
+ tmpdir_factory
+ Return a TempdirFactory instance for the test session.
+ tmpdir
+ Return a temporary directory path object
+ which is unique to each test function invocation,
+ created as a sub directory of the base temporary
+ directory. The returned object is a `py.path.local`_
+ path object.
+
+ no tests ran in 0.12 seconds
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/cache.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/cache.rst
new file mode 100644
index 00000000000..c88721b11b0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/cache.rst
@@ -0,0 +1,268 @@
+.. _`cache_provider`:
+.. _cache:
+
+
+Cache: working with cross-testrun state
+=======================================
+
+.. versionadded:: 2.8
+
+Usage
+---------
+
+The plugin provides two command line options to rerun failures from the
+last ``pytest`` invocation:
+
+* ``--lf``, ``--last-failed`` - to only re-run the failures.
+* ``--ff``, ``--failed-first`` - to run the failures first and then the rest of
+ the tests.
+
+For cleanup (usually not needed), a ``--cache-clear`` option allows to remove
+all cross-session cache contents ahead of a test run.
+
+Other plugins may access the `config.cache`_ object to set/get
+**json encodable** values between ``pytest`` invocations.
+
+.. note::
+
+ This plugin is enabled by default, but can be disabled if needed: see
+ :ref:`cmdunregister` (the internal name for this plugin is
+ ``cacheprovider``).
+
+
+Rerunning only failures or failures first
+-----------------------------------------------
+
+First, let's create 50 test invocation of which only 2 fail::
+
+ # content of test_50.py
+ import pytest
+
+ @pytest.mark.parametrize("i", range(50))
+ def test_num(i):
+ if i in (17, 25):
+ pytest.fail("bad luck")
+
+If you run this for the first time you will see two failures::
+
+ $ pytest -q
+ .................F.......F........................ [100%]
+ ================================= FAILURES =================================
+ _______________________________ test_num[17] _______________________________
+
+ i = 17
+
+ @pytest.mark.parametrize("i", range(50))
+ def test_num(i):
+ if i in (17, 25):
+ > pytest.fail("bad luck")
+ E Failed: bad luck
+
+ test_50.py:6: Failed
+ _______________________________ test_num[25] _______________________________
+
+ i = 25
+
+ @pytest.mark.parametrize("i", range(50))
+ def test_num(i):
+ if i in (17, 25):
+ > pytest.fail("bad luck")
+ E Failed: bad luck
+
+ test_50.py:6: Failed
+ 2 failed, 48 passed in 0.12 seconds
+
+If you then run it with ``--lf``::
+
+ $ pytest --lf
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 50 items
+ run-last-failure: rerun previous 2 failures
+
+ test_50.py FF [100%]
+
+ ================================= FAILURES =================================
+ _______________________________ test_num[17] _______________________________
+
+ i = 17
+
+ @pytest.mark.parametrize("i", range(50))
+ def test_num(i):
+ if i in (17, 25):
+ > pytest.fail("bad luck")
+ E Failed: bad luck
+
+ test_50.py:6: Failed
+ _______________________________ test_num[25] _______________________________
+
+ i = 25
+
+ @pytest.mark.parametrize("i", range(50))
+ def test_num(i):
+ if i in (17, 25):
+ > pytest.fail("bad luck")
+ E Failed: bad luck
+
+ test_50.py:6: Failed
+ =========================== 48 tests deselected ============================
+ ================= 2 failed, 48 deselected in 0.12 seconds ==================
+
+You have run only the two failing test from the last run, while 48 tests have
+not been run ("deselected").
+
+Now, if you run with the ``--ff`` option, all tests will be run but the first
+previous failures will be executed first (as can be seen from the series
+of ``FF`` and dots)::
+
+ $ pytest --ff
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 50 items
+ run-last-failure: rerun previous 2 failures first
+
+ test_50.py FF................................................ [100%]
+
+ ================================= FAILURES =================================
+ _______________________________ test_num[17] _______________________________
+
+ i = 17
+
+ @pytest.mark.parametrize("i", range(50))
+ def test_num(i):
+ if i in (17, 25):
+ > pytest.fail("bad luck")
+ E Failed: bad luck
+
+ test_50.py:6: Failed
+ _______________________________ test_num[25] _______________________________
+
+ i = 25
+
+ @pytest.mark.parametrize("i", range(50))
+ def test_num(i):
+ if i in (17, 25):
+ > pytest.fail("bad luck")
+ E Failed: bad luck
+
+ test_50.py:6: Failed
+ =================== 2 failed, 48 passed in 0.12 seconds ====================
+
+.. _`config.cache`:
+
+The new config.cache object
+--------------------------------
+
+.. regendoc:wipe
+
+Plugins or conftest.py support code can get a cached value using the
+pytest ``config`` object. Here is a basic example plugin which
+implements a :ref:`fixture` which re-uses previously created state
+across pytest invocations::
+
+ # content of test_caching.py
+ import pytest
+ import time
+
+ @pytest.fixture
+ def mydata(request):
+ val = request.config.cache.get("example/value", None)
+ if val is None:
+ time.sleep(9*0.6) # expensive computation :)
+ val = 42
+ request.config.cache.set("example/value", val)
+ return val
+
+ def test_function(mydata):
+ assert mydata == 23
+
+If you run this command once, it will take a while because
+of the sleep::
+
+ $ pytest -q
+ F [100%]
+ ================================= FAILURES =================================
+ ______________________________ test_function _______________________________
+
+ mydata = 42
+
+ def test_function(mydata):
+ > assert mydata == 23
+ E assert 42 == 23
+
+ test_caching.py:14: AssertionError
+ 1 failed in 0.12 seconds
+
+If you run it a second time the value will be retrieved from
+the cache and this will be quick::
+
+ $ pytest -q
+ F [100%]
+ ================================= FAILURES =================================
+ ______________________________ test_function _______________________________
+
+ mydata = 42
+
+ def test_function(mydata):
+ > assert mydata == 23
+ E assert 42 == 23
+
+ test_caching.py:14: AssertionError
+ 1 failed in 0.12 seconds
+
+See the `cache-api`_ for more details.
+
+
+Inspecting Cache content
+-------------------------------
+
+You can always peek at the content of the cache using the
+``--cache-show`` command line option::
+
+ $ py.test --cache-show
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ cachedir: $REGENDOC_TMPDIR/.cache
+ ------------------------------- cache values -------------------------------
+ cache/lastfailed contains:
+ {'test_caching.py::test_function': True}
+ example/value contains:
+ 42
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+Clearing Cache content
+-------------------------------
+
+You can instruct pytest to clear all cache files and values
+by adding the ``--cache-clear`` option like this::
+
+ pytest --cache-clear
+
+This is recommended for invocations from Continuous Integration
+servers where isolation and correctness is more important
+than speed.
+
+
+.. _`cache-api`:
+
+config.cache API
+------------------
+
+The ``config.cache`` object allows other plugins,
+including ``conftest.py`` files,
+to safely and flexibly store and retrieve values across
+test runs because the ``config`` object is available
+in many places.
+
+Under the hood, the cache plugin uses the simple
+dumps/loads API of the json stdlib module
+
+.. currentmodule:: _pytest.cacheprovider
+
+.. automethod:: Cache.get
+.. automethod:: Cache.set
+.. automethod:: Cache.makedir
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/capture.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/capture.rst
new file mode 100644
index 00000000000..a87b57f8fc0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/capture.rst
@@ -0,0 +1,148 @@
+
+.. _`captures`:
+
+Capturing of the stdout/stderr output
+=========================================================
+
+Default stdout/stderr/stdin capturing behaviour
+---------------------------------------------------------
+
+During test execution any output sent to ``stdout`` and ``stderr`` is
+captured. If a test or a setup method fails its according captured
+output will usually be shown along with the failure traceback.
+
+In addition, ``stdin`` is set to a "null" object which will
+fail on attempts to read from it because it is rarely desired
+to wait for interactive input when running automated tests.
+
+By default capturing is done by intercepting writes to low level
+file descriptors. This allows to capture output from simple
+print statements as well as output from a subprocess started by
+a test.
+
+Setting capturing methods or disabling capturing
+-------------------------------------------------
+
+There are two ways in which ``pytest`` can perform capturing:
+
+* file descriptor (FD) level capturing (default): All writes going to the
+ operating system file descriptors 1 and 2 will be captured.
+
+* ``sys`` level capturing: Only writes to Python files ``sys.stdout``
+ and ``sys.stderr`` will be captured. No capturing of writes to
+ filedescriptors is performed.
+
+.. _`disable capturing`:
+
+You can influence output capturing mechanisms from the command line::
+
+ pytest -s # disable all capturing
+ pytest --capture=sys # replace sys.stdout/stderr with in-mem files
+ pytest --capture=fd # also point filedescriptors 1 and 2 to temp file
+
+.. _printdebugging:
+
+Using print statements for debugging
+---------------------------------------------------
+
+One primary benefit of the default capturing of stdout/stderr output
+is that you can use print statements for debugging::
+
+ # content of test_module.py
+
+ def setup_function(function):
+ print ("setting up %s" % function)
+
+ def test_func1():
+ assert True
+
+ def test_func2():
+ assert False
+
+and running this module will show you precisely the output
+of the failing function and hide the other one::
+
+ $ pytest
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 2 items
+
+ test_module.py .F [100%]
+
+ ================================= FAILURES =================================
+ ________________________________ test_func2 ________________________________
+
+ def test_func2():
+ > assert False
+ E assert False
+
+ test_module.py:9: AssertionError
+ -------------------------- Captured stdout setup ---------------------------
+ setting up <function test_func2 at 0xdeadbeef>
+ ==================== 1 failed, 1 passed in 0.12 seconds ====================
+
+Accessing captured output from a test function
+---------------------------------------------------
+
+The ``capsys``, ``capsysbinary``, ``capfd``, and ``capfdbinary`` fixtures
+allow access to stdout/stderr output created during test execution. Here is
+an example test function that performs some output related checks:
+
+.. code-block:: python
+
+ def test_myoutput(capsys): # or use "capfd" for fd-level
+ print ("hello")
+ sys.stderr.write("world\n")
+ out, err = capsys.readouterr()
+ assert out == "hello\n"
+ assert err == "world\n"
+ print ("next")
+ out, err = capsys.readouterr()
+ assert out == "next\n"
+
+The ``readouterr()`` call snapshots the output so far -
+and capturing will be continued. After the test
+function finishes the original streams will
+be restored. Using ``capsys`` this way frees your
+test from having to care about setting/resetting
+output streams and also interacts well with pytest's
+own per-test capturing.
+
+If you want to capture on filedescriptor level you can use
+the ``capfd`` fixture which offers the exact
+same interface but allows to also capture output from
+libraries or subprocesses that directly write to operating
+system level output streams (FD1 and FD2).
+
+.. versionadded:: 3.3
+
+If the code under test writes non-textual data, you can capture this using
+the ``capsysbinary`` fixture which instead returns ``bytes`` from
+the ``readouterr`` method. The ``capfsysbinary`` fixture is currently only
+available in python 3.
+
+
+.. versionadded:: 3.3
+
+If the code under test writes non-textual data, you can capture this using
+the ``capfdbinary`` fixture which instead returns ``bytes`` from
+the ``readouterr`` method. The ``capfdbinary`` fixture operates on the
+filedescriptor level.
+
+
+.. versionadded:: 3.0
+
+To temporarily disable capture within a test, both ``capsys``
+and ``capfd`` have a ``disabled()`` method that can be used
+as a context manager, disabling capture inside the ``with`` block:
+
+.. code-block:: python
+
+ def test_disabling_capturing(capsys):
+ print('this output is captured')
+ with capsys.disabled():
+ print('output not captured, going directly to sys.stdout')
+ print('this output is also captured')
+
+.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/changelog.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/changelog.rst
index a59b3c7e250..a59b3c7e250 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/changelog.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/changelog.rst
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/check_sphinx.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/check_sphinx.py
index 0f536ffa6ac..0f536ffa6ac 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/check_sphinx.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/check_sphinx.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/conf.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/conf.py
new file mode 100644
index 00000000000..40f1e4165e4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/conf.py
@@ -0,0 +1,323 @@
+# -*- coding: utf-8 -*-
+#
+# pytest documentation build configuration file, created by
+# sphinx-quickstart on Fri Oct 8 17:54:28 2010.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The full version, including alpha/beta/rc tags.
+# The short X.Y version.
+
+import os, sys
+from _pytest import __version__ as version
+release = ".".join(version.split(".")[:2])
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+autodoc_member_order = "bysource"
+todo_include_todos = 1
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.autosummary',
+ 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'contents'
+
+# General information about the project.
+project = u'pytest'
+copyright = u'2015, holger krekel and pytest-dev team'
+
+
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['links.inc', '_build', 'naming20.rst', 'test/*',
+ "old_*",
+ '*attic*',
+ '*/attic*',
+ 'funcargs.rst',
+ 'setup.rst',
+ 'example/remoteinterp.rst',
+ ]
+
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+add_module_names = False
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+sys.path.append(os.path.abspath('_themes'))
+html_theme_path = ['_themes']
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'flask'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+html_theme_options = {
+ 'index_logo': None
+}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+html_title = 'pytest documentation'
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+html_short_title = "pytest-%s" % release
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+html_logo = "img/pytest1.png"
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+html_favicon = "img/pytest1favi.ico"
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+# html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+#html_sidebars = {'index': 'indexsidebar.html'}
+
+html_sidebars = {
+ 'index': [
+ 'sidebarintro.html',
+ 'globaltoc.html',
+ 'links.html',
+ 'sourcelink.html',
+ 'searchbox.html'
+ ],
+ '**': [
+ 'globaltoc.html',
+ 'relations.html',
+ 'links.html',
+ 'sourcelink.html',
+ 'searchbox.html'
+ ]
+}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+#html_additional_pages = {'index': 'index.html'}
+
+
+# If false, no module index is generated.
+html_domain_indices = True
+
+# If false, no index is generated.
+html_use_index = False
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+html_show_sourcelink = False
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'pytestdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('contents', 'pytest.tex', u'pytest Documentation',
+ u'holger krekel, trainer and consultant, http://merlinux.eu', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+latex_logo = 'img/pytest1.png'
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+latex_domain_indices = False
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('usage', 'pytest', u'pytest usage',
+ [u'holger krekel at merlinux eu'], 1)
+]
+
+
+# -- Options for Epub output ---------------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = u'pytest'
+epub_author = u'holger krekel at merlinux eu'
+epub_publisher = u'holger krekel at merlinux eu'
+epub_copyright = u'2013, holger krekel et alii'
+
+# The language of the text. It defaults to the language option
+# or en if the language is not set.
+#epub_language = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+#epub_scheme = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#epub_identifier = ''
+
+# A unique identification for the text.
+#epub_uid = ''
+
+# HTML files that should be inserted before the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_pre_files = []
+
+# HTML files shat should be inserted after the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_post_files = []
+
+# A list of files that should not be packed into the epub file.
+#epub_exclude_files = []
+
+# The depth of the table of contents in toc.ncx.
+#epub_tocdepth = 3
+
+# Allow duplicate toc entries.
+#epub_tocdup = True
+
+
+# -- Options for texinfo output ------------------------------------------------
+
+texinfo_documents = [
+ (master_doc, 'pytest', 'pytest Documentation',
+ ('Holger Krekel@*Benjamin Peterson@*Ronny Pfannschmidt@*'
+ 'Floris Bruynooghe@*others'),
+ 'pytest',
+ 'simple powerful testing with Python',
+ 'Programming',
+ 1),
+]
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'python': ('http://docs.python.org/', None),
+# 'lib': ("http://docs.python.org/2.7library/", None),
+ }
+
+
+def setup(app):
+ #from sphinx.ext.autodoc import cut_lines
+ #app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
+ app.add_description_unit('confval', 'confval',
+ objname='configuration value',
+ indextemplate='pair: %s; configuration value')
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/conftest.py
index 1a62e1b5df5..1a62e1b5df5 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/conftest.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/conftest.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/contact.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/contact.rst
new file mode 100644
index 00000000000..83d496640d5
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/contact.rst
@@ -0,0 +1,50 @@
+
+.. _`contact channels`:
+.. _`contact`:
+
+Contact channels
+===================================
+
+- `pytest issue tracker`_ to report bugs or suggest features (for version
+ 2.0 and above).
+
+- `pytest on stackoverflow.com <http://stackoverflow.com/search?q=pytest>`_
+ to post questions with the tag ``pytest``. New Questions will usually
+ be seen by pytest users or developers and answered quickly.
+
+- `Testing In Python`_: a mailing list for Python testing tools and discussion.
+
+- `pytest-dev at python.org (mailing list)`_ pytest specific announcements and discussions.
+
+- `pytest-commit at python.org (mailing list)`_: for commits and new issues
+
+- :doc:`contribution guide <contributing>` for help on submitting pull
+ requests to GitHub.
+
+- ``#pylib`` on irc.freenode.net IRC channel for random questions.
+
+- private mail to Holger.Krekel at gmail com if you want to communicate sensitive issues
+
+
+- `merlinux.eu`_ offers pytest and tox-related professional teaching and
+ consulting.
+
+.. _`pytest issue tracker`: https://github.com/pytest-dev/pytest/issues
+.. _`old issue tracker`: http://bitbucket.org/hpk42/py-trunk/issues/
+
+.. _`merlinux.eu`: http://merlinux.eu
+
+.. _`get an account`:
+
+.. _tetamap: http://tetamap.wordpress.com
+
+.. _`@pylibcommit`: http://twitter.com/pylibcommit
+
+
+.. _`Testing in Python`: http://lists.idyll.org/listinfo/testing-in-python
+.. _FOAF: http://en.wikipedia.org/wiki/FOAF
+.. _`py-dev`:
+.. _`development mailing list`:
+.. _`pytest-dev at python.org (mailing list)`: http://mail.python.org/mailman/listinfo/pytest-dev
+.. _`pytest-commit at python.org (mailing list)`: http://mail.python.org/mailman/listinfo/pytest-commit
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/contents.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/contents.rst
new file mode 100644
index 00000000000..7a6570e0bc5
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/contents.rst
@@ -0,0 +1,65 @@
+.. _toc:
+
+Full pytest documentation
+===========================
+
+`Download latest version as PDF <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
+
+.. `Download latest version as EPUB <http://media.readthedocs.org/epub/pytest/latest/pytest.epub>`_
+
+.. toctree::
+ :maxdepth: 2
+
+ getting-started
+ usage
+ existingtestsuite
+ assert
+ builtin
+ fixture
+ monkeypatch
+ tmpdir
+ capture
+ warnings
+ doctest
+ mark
+ skipping
+ parametrize
+ cache
+ unittest
+ nose
+ xunit_setup
+ plugins
+ writing_plugins
+ logging
+
+ goodpractices
+ pythonpath
+ customize
+ example/index
+ bash-completion
+
+ backwards-compatibility
+ historical-notes
+ license
+ contributing
+ development_guide
+ talks
+ projects
+ faq
+ contact
+
+.. only:: html
+
+ .. toctree::
+ :maxdepth: 1
+
+ announce/index
+
+.. only:: html
+
+ .. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ changelog
+
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/contributing.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/contributing.rst
index 2b6578f6b97..2b6578f6b97 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/contributing.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/contributing.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/customize.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/customize.rst
new file mode 100644
index 00000000000..8133704a52c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/customize.rst
@@ -0,0 +1,333 @@
+Configuration
+=============
+
+Command line options and configuration file settings
+-----------------------------------------------------------------
+
+You can get help on command line options and values in INI-style
+configurations files by using the general help option::
+
+ pytest -h # prints options _and_ config file settings
+
+This will display command line and configuration file settings
+which were registered by installed plugins.
+
+.. _rootdir:
+.. _inifiles:
+
+Initialization: determining rootdir and inifile
+-----------------------------------------------
+
+.. versionadded:: 2.7
+
+pytest determines a ``rootdir`` for each test run which depends on
+the command line arguments (specified test files, paths) and on
+the existence of *ini-files*. The determined ``rootdir`` and *ini-file* are
+printed as part of the pytest header during startup.
+
+Here's a summary what ``pytest`` uses ``rootdir`` for:
+
+* Construct *nodeids* during collection; each test is assigned
+ a unique *nodeid* which is rooted at the ``rootdir`` and takes in account full path,
+ class name, function name and parametrization (if any).
+
+* Is used by plugins as a stable location to store project/test run specific information;
+ for example, the internal :ref:`cache <cache>` plugin creates a ``.cache`` subdirectory
+ in ``rootdir`` to store its cross-test run state.
+
+Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
+influence how modules are imported. See :ref:`pythonpath` for more details.
+
+Finding the ``rootdir``
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Here is the algorithm which finds the rootdir from ``args``:
+
+- determine the common ancestor directory for the specified ``args`` that are
+ recognised as paths that exist in the file system. If no such paths are
+ found, the common ancestor directory is set to the current working directory.
+
+- look for ``pytest.ini``, ``tox.ini`` and ``setup.cfg`` files in the ancestor
+ directory and upwards. If one is matched, it becomes the ini-file and its
+ directory becomes the rootdir.
+
+- if no ini-file was found, look for ``setup.py`` upwards from the common
+ ancestor directory to determine the ``rootdir``.
+
+- if no ``setup.py`` was found, look for ``pytest.ini``, ``tox.ini`` and
+ ``setup.cfg`` in each of the specified ``args`` and upwards. If one is
+ matched, it becomes the ini-file and its directory becomes the rootdir.
+
+- if no ini-file was found, use the already determined common ancestor as root
+ directory. This allows the use of pytest in structures that are not part of
+ a package and don't have any particular ini-file configuration.
+
+If no ``args`` are given, pytest collects test below the current working
+directory and also starts determining the rootdir from there.
+
+:warning: custom pytest plugin commandline arguments may include a path, as in
+ ``pytest --log-output ../../test.log args``. Then ``args`` is mandatory,
+ otherwise pytest uses the folder of test.log for rootdir determination
+ (see also `issue 1435 <https://github.com/pytest-dev/pytest/issues/1435>`_).
+ A dot ``.`` for referencing to the current working directory is also
+ possible.
+
+Note that an existing ``pytest.ini`` file will always be considered a match,
+whereas ``tox.ini`` and ``setup.cfg`` will only match if they contain a
+``[pytest]`` or ``[tool:pytest]`` section, respectively. Options from multiple ini-files candidates are never
+merged - the first one wins (``pytest.ini`` always wins, even if it does not
+contain a ``[pytest]`` section).
+
+The ``config`` object will subsequently carry these attributes:
+
+- ``config.rootdir``: the determined root directory, guaranteed to exist.
+
+- ``config.inifile``: the determined ini-file, may be ``None``.
+
+The rootdir is used a reference directory for constructing test
+addresses ("nodeids") and can be used also by plugins for storing
+per-testrun information.
+
+Example::
+
+ pytest path/to/testdir path/other/
+
+will determine the common ancestor as ``path`` and then
+check for ini-files as follows::
+
+ # first look for pytest.ini files
+ path/pytest.ini
+ path/setup.cfg # must also contain [tool:pytest] section to match
+ path/tox.ini # must also contain [pytest] section to match
+ pytest.ini
+ ... # all the way down to the root
+
+ # now look for setup.py
+ path/setup.py
+ setup.py
+ ... # all the way down to the root
+
+
+.. _`how to change command line options defaults`:
+.. _`adding default options`:
+
+
+
+How to change command line options defaults
+------------------------------------------------
+
+It can be tedious to type the same series of command line options
+every time you use ``pytest``. For example, if you always want to see
+detailed info on skipped and xfailed tests, as well as have terser "dot"
+progress output, you can write it into a configuration file:
+
+.. code-block:: ini
+
+ # content of pytest.ini
+ # (or tox.ini or setup.cfg)
+ [pytest]
+ addopts = -ra -q
+
+Alternatively, you can set a ``PYTEST_ADDOPTS`` environment variable to add command
+line options while the environment is in use::
+
+ export PYTEST_ADDOPTS="-v"
+
+Here's how the command-line is built in the presence of ``addopts`` or the environment variable::
+
+ <pytest.ini:addopts> $PYTEST_ADDOTPS <extra command-line arguments>
+
+So if the user executes in the command-line::
+
+ pytest -m slow
+
+The actual command line executed is::
+
+ pytest -ra -q -v -m slow
+
+Note that as usual for other command-line applications, in case of conflicting options the last one wins, so the example
+above will show verbose output because ``-v`` overwrites ``-q``.
+
+
+Builtin configuration file options
+----------------------------------------------
+
+.. confval:: minversion
+
+ Specifies a minimal pytest version required for running tests.
+
+ minversion = 2.1 # will fail if we run with pytest-2.0
+
+.. confval:: addopts
+
+ Add the specified ``OPTS`` to the set of command line arguments as if they
+ had been specified by the user. Example: if you have this ini file content:
+
+ .. code-block:: ini
+
+ [pytest]
+ addopts = --maxfail=2 -rf # exit after 2 failures, report fail info
+
+ issuing ``pytest test_hello.py`` actually means::
+
+ pytest --maxfail=2 -rf test_hello.py
+
+ Default is to add no options.
+
+.. confval:: norecursedirs
+
+ Set the directory basename patterns to avoid when recursing
+ for test discovery. The individual (fnmatch-style) patterns are
+ applied to the basename of a directory to decide if to recurse into it.
+ Pattern matching characters::
+
+ * matches everything
+ ? matches any single character
+ [seq] matches any character in seq
+ [!seq] matches any char not in seq
+
+ Default patterns are ``'.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg', 'venv'``.
+ Setting a ``norecursedirs`` replaces the default. Here is an example of
+ how to avoid certain directories:
+
+ .. code-block:: ini
+
+ # content of pytest.ini
+ [pytest]
+ norecursedirs = .svn _build tmp*
+
+ This would tell ``pytest`` to not look into typical subversion or
+ sphinx-build directories or into any ``tmp`` prefixed directory.
+
+ Additionally, ``pytest`` will attempt to intelligently identify and ignore a
+ virtualenv by the presence of an activation script. Any directory deemed to
+ be the root of a virtual environment will not be considered during test
+ collection unless ``‑‑collect‑in‑virtualenv`` is given. Note also that
+ ``norecursedirs`` takes precedence over ``‑‑collect‑in‑virtualenv``; e.g. if
+ you intend to run tests in a virtualenv with a base directory that matches
+ ``'.*'`` you *must* override ``norecursedirs`` in addition to using the
+ ``‑‑collect‑in‑virtualenv`` flag.
+
+.. confval:: testpaths
+
+ .. versionadded:: 2.8
+
+ Sets list of directories that should be searched for tests when
+ no specific directories, files or test ids are given in the command line when
+ executing pytest from the :ref:`rootdir <rootdir>` directory.
+ Useful when all project tests are in a known location to speed up
+ test collection and to avoid picking up undesired tests by accident.
+
+ .. code-block:: ini
+
+ # content of pytest.ini
+ [pytest]
+ testpaths = testing doc
+
+ This tells pytest to only look for tests in ``testing`` and ``doc``
+ directories when executing from the root directory.
+
+.. confval:: python_files
+
+ One or more Glob-style file patterns determining which python files
+ are considered as test modules. By default, pytest will consider
+ any file matching with ``test_*.py`` and ``*_test.py`` globs as a test
+ module.
+
+.. confval:: python_classes
+
+ One or more name prefixes or glob-style patterns determining which classes
+ are considered for test collection. By default, pytest will consider any
+ class prefixed with ``Test`` as a test collection. Here is an example of how
+ to collect tests from classes that end in ``Suite``:
+
+ .. code-block:: ini
+
+ # content of pytest.ini
+ [pytest]
+ python_classes = *Suite
+
+ Note that ``unittest.TestCase`` derived classes are always collected
+ regardless of this option, as ``unittest``'s own collection framework is used
+ to collect those tests.
+
+.. confval:: python_functions
+
+ One or more name prefixes or glob-patterns determining which test functions
+ and methods are considered tests. By default, pytest will consider any
+ function prefixed with ``test`` as a test. Here is an example of how
+ to collect test functions and methods that end in ``_test``:
+
+ .. code-block:: ini
+
+ # content of pytest.ini
+ [pytest]
+ python_functions = *_test
+
+ Note that this has no effect on methods that live on a ``unittest
+ .TestCase`` derived class, as ``unittest``'s own collection framework is used
+ to collect those tests.
+
+ See :ref:`change naming conventions` for more detailed examples.
+
+.. confval:: doctest_optionflags
+
+ One or more doctest flag names from the standard ``doctest`` module.
+ :doc:`See how pytest handles doctests <doctest>`.
+
+.. confval:: confcutdir
+
+ Sets a directory where search upwards for ``conftest.py`` files stops.
+ By default, pytest will stop searching for ``conftest.py`` files upwards
+ from ``pytest.ini``/``tox.ini``/``setup.cfg`` of the project if any,
+ or up to the file-system root.
+
+
+.. confval:: filterwarnings
+
+ .. versionadded:: 3.1
+
+ Sets a list of filters and actions that should be taken for matched
+ warnings. By default all warnings emitted during the test session
+ will be displayed in a summary at the end of the test session.
+
+ .. code-block:: ini
+
+ # content of pytest.ini
+ [pytest]
+ filterwarnings =
+ error
+ ignore::DeprecationWarning
+
+ This tells pytest to ignore deprecation warnings and turn all other warnings
+ into errors. For more information please refer to :ref:`warnings`.
+
+.. confval:: cache_dir
+
+ .. versionadded:: 3.2
+
+ Sets a directory where stores content of cache plugin. Default directory is
+ ``.cache`` which is created in :ref:`rootdir <rootdir>`. Directory may be
+ relative or absolute path. If setting relative path, then directory is created
+ relative to :ref:`rootdir <rootdir>`. Additionally path may contain environment
+ variables, that will be expanded. For more information about cache plugin
+ please refer to :ref:`cache_provider`.
+
+
+.. confval:: console_output_style
+
+ .. versionadded:: 3.3
+
+ Sets the console output style while running tests:
+
+ * ``classic``: classic pytest output.
+ * ``progress``: like classic pytest output, but with a progress indicator.
+
+ The default is ``progress``, but you can fallback to ``classic`` if you prefer or
+ the new mode is causing unexpected problems:
+
+ .. code-block:: ini
+
+ # content of pytest.ini
+ [pytest]
+ console_output_style = classic
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/development_guide.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/development_guide.rst
new file mode 100644
index 00000000000..465e97de0f9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/development_guide.rst
@@ -0,0 +1,108 @@
+=================
+Development Guide
+=================
+
+Some general guidelines regarding development in pytest for core maintainers and general contributors. Nothing here
+is set in stone and can't be changed, feel free to suggest improvements or changes in the workflow.
+
+
+Code Style
+----------
+
+* `PEP-8 <https://www.python.org/dev/peps/pep-0008>`_
+* `flake8 <https://pypi.python.org/pypi/flake8>`_ for quality checks
+* `invoke <http://www.pyinvoke.org/>`_ to automate development tasks
+
+
+Branches
+--------
+
+We have two long term branches:
+
+* ``master``: contains the code for the next bugfix release.
+* ``features``: contains the code with new features for the next minor release.
+
+The official repository usually does not contain topic branches, developers and contributors should create topic
+branches in their own forks.
+
+Exceptions can be made for cases where more than one contributor is working on the same
+topic or where it makes sense to use some automatic capability of the main repository, such as automatic docs from
+`readthedocs <readthedocs.org>`_ for a branch dealing with documentation refactoring.
+
+Issues
+------
+
+Any question, feature, bug or proposal is welcome as an issue. Users are encouraged to use them whenever they need.
+
+GitHub issues should use labels to categorize them. Labels should be created sporadically, to fill a niche; we should
+avoid creating labels just for the sake of creating them.
+
+Here is a list of labels and a brief description mentioning their intent.
+
+
+**Type**
+
+* ``type: backward compatibility``: issue that will cause problems with old pytest versions.
+* ``type: bug``: problem that needs to be addressed.
+* ``type: deprecation``: feature that will be deprecated in the future.
+* ``type: docs``: documentation missing or needing clarification.
+* ``type: enhancement``: new feature or API change, should be merged into ``features``.
+* ``type: feature-branch``: new feature or API change, should be merged into ``features``.
+* ``type: infrastructure``: improvement to development/releases/CI structure.
+* ``type: performance``: performance or memory problem/improvement.
+* ``type: proposal``: proposal for a new feature, often to gather opinions or design the API around the new feature.
+* ``type: question``: question regarding usage, installation, internals or how to test something.
+* ``type: refactoring``: internal improvements to the code.
+* ``type: regression``: indicates a problem that was introduced in a release which was working previously.
+
+**Status**
+
+* ``status: critical``: grave problem or usability issue that affects lots of users.
+* ``status: easy``: easy issue that is friendly to new contributors.
+* ``status: help wanted``: core developers need help from experts on this topic.
+* ``status: needs information``: reporter needs to provide more information; can be closed after 2 or more weeks of inactivity.
+
+**Topic**
+
+* ``topic: collection``
+* ``topic: fixtures``
+* ``topic: parametrize``
+* ``topic: reporting``
+* ``topic: selection``
+* ``topic: tracebacks``
+
+**Plugin (internal or external)**
+
+* ``plugin: cache``
+* ``plugin: capture``
+* ``plugin: doctests``
+* ``plugin: junitxml``
+* ``plugin: monkeypatch``
+* ``plugin: nose``
+* ``plugin: pastebin``
+* ``plugin: pytester``
+* ``plugin: tmpdir``
+* ``plugin: unittest``
+* ``plugin: warnings``
+* ``plugin: xdist``
+
+
+**OS**
+
+Issues specific to a single operating system. Do not use as a means to indicate where an issue originated from, only
+for problems that happen **only** in that system.
+
+* ``os: linux``
+* ``os: mac``
+* ``os: windows``
+
+**Temporary**
+
+Used to classify issues for limited time, to help find issues related in events for example.
+They should be removed after they are no longer relevant.
+
+* ``temporary: EP2017 sprint``:
+* ``temporary: sprint-candidate``:
+
+
+.. include:: ../../HOWTORELEASE.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/doctest.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/doctest.rst
new file mode 100644
index 00000000000..4c5a878dd61
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/doctest.rst
@@ -0,0 +1,165 @@
+
+Doctest integration for modules and test files
+=========================================================
+
+By default all files matching the ``test*.txt`` pattern will
+be run through the python standard ``doctest`` module. You
+can change the pattern by issuing::
+
+ pytest --doctest-glob='*.rst'
+
+on the command line. Since version ``2.9``, ``--doctest-glob``
+can be given multiple times in the command-line.
+
+.. versionadded:: 3.1
+
+ You can specify the encoding that will be used for those doctest files
+ using the ``doctest_encoding`` ini option:
+
+ .. code-block:: ini
+
+ # content of pytest.ini
+ [pytest]
+ doctest_encoding = latin1
+
+ The default encoding is UTF-8.
+
+You can also trigger running of doctests
+from docstrings in all python modules (including regular
+python test modules)::
+
+ pytest --doctest-modules
+
+You can make these changes permanent in your project by
+putting them into a pytest.ini file like this:
+
+.. code-block:: ini
+
+ # content of pytest.ini
+ [pytest]
+ addopts = --doctest-modules
+
+If you then have a text file like this::
+
+ # content of example.rst
+
+ hello this is a doctest
+ >>> x = 3
+ >>> x
+ 3
+
+and another like this::
+
+ # content of mymodule.py
+ def something():
+ """ a doctest in a docstring
+ >>> something()
+ 42
+ """
+ return 42
+
+then you can just invoke ``pytest`` without command line options::
+
+ $ pytest
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
+ collected 1 item
+
+ mymodule.py . [100%]
+
+ ========================= 1 passed in 0.12 seconds =========================
+
+It is possible to use fixtures using the ``getfixture`` helper::
+
+ # content of example.rst
+ >>> tmp = getfixture('tmpdir')
+ >>> ...
+ >>>
+
+Also, :ref:`usefixtures` and :ref:`autouse` fixtures are supported
+when executing text doctest files.
+
+The standard ``doctest`` module provides some setting flags to configure the
+strictness of doctest tests. In pytest You can enable those flags those flags
+using the configuration file. To make pytest ignore trailing whitespaces and
+ignore lengthy exception stack traces you can just write:
+
+.. code-block:: ini
+
+ [pytest]
+ doctest_optionflags= NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL
+
+pytest also introduces new options to allow doctests to run in Python 2 and
+Python 3 unchanged:
+
+* ``ALLOW_UNICODE``: when enabled, the ``u`` prefix is stripped from unicode
+ strings in expected doctest output.
+
+* ``ALLOW_BYTES``: when enabled, the ``b`` prefix is stripped from byte strings
+ in expected doctest output.
+
+As with any other option flag, these flags can be enabled in ``pytest.ini`` using
+the ``doctest_optionflags`` ini option:
+
+.. code-block:: ini
+
+ [pytest]
+ doctest_optionflags = ALLOW_UNICODE ALLOW_BYTES
+
+
+Alternatively, it can be enabled by an inline comment in the doc test
+itself::
+
+ # content of example.rst
+ >>> get_unicode_greeting() # doctest: +ALLOW_UNICODE
+ 'Hello'
+
+
+The 'doctest_namespace' fixture
+-------------------------------
+
+.. versionadded:: 3.0
+
+The ``doctest_namespace`` fixture can be used to inject items into the
+namespace in which your doctests run. It is intended to be used within
+your own fixtures to provide the tests that use them with context.
+
+``doctest_namespace`` is a standard ``dict`` object into which you
+place the objects you want to appear in the doctest namespace::
+
+ # content of conftest.py
+ import numpy
+ @pytest.fixture(autouse=True)
+ def add_np(doctest_namespace):
+ doctest_namespace['np'] = numpy
+
+which can then be used in your doctests directly::
+
+ # content of numpy.py
+ def arange():
+ """
+ >>> a = np.arange(10)
+ >>> len(a)
+ 10
+ """
+ pass
+
+
+Output format
+-------------
+
+.. versionadded:: 3.0
+
+You can change the diff output format on failure for your doctests
+by using one of standard doctest modules format in options
+(see :data:`python:doctest.REPORT_UDIFF`, :data:`python:doctest.REPORT_CDIFF`,
+:data:`python:doctest.REPORT_NDIFF`, :data:`python:doctest.REPORT_ONLY_FIRST_FAILURE`)::
+
+ pytest --doctest-modules --doctest-report none
+ pytest --doctest-modules --doctest-report udiff
+ pytest --doctest-modules --doctest-report cdiff
+ pytest --doctest-modules --doctest-report ndiff
+ pytest --doctest-modules --doctest-report only_first_failure
+
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/failure_demo.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/failure_demo.py
new file mode 100644
index 00000000000..d31fba2adaa
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/failure_demo.py
@@ -0,0 +1,238 @@
+from pytest import raises
+import _pytest._code
+import py
+
+def otherfunc(a,b):
+ assert a==b
+
+def somefunc(x,y):
+ otherfunc(x,y)
+
+def otherfunc_multi(a,b):
+ assert (a ==
+ b)
+
+def test_generative(param1, param2):
+ assert param1 * 2 < param2
+
+def pytest_generate_tests(metafunc):
+ if 'param1' in metafunc.fixturenames:
+ metafunc.addcall(funcargs=dict(param1=3, param2=6))
+
+class TestFailing(object):
+ def test_simple(self):
+ def f():
+ return 42
+ def g():
+ return 43
+
+ assert f() == g()
+
+ def test_simple_multiline(self):
+ otherfunc_multi(
+ 42,
+ 6*9)
+
+ def test_not(self):
+ def f():
+ return 42
+ assert not f()
+
+class TestSpecialisedExplanations(object):
+ def test_eq_text(self):
+ assert 'spam' == 'eggs'
+
+ def test_eq_similar_text(self):
+ assert 'foo 1 bar' == 'foo 2 bar'
+
+ def test_eq_multiline_text(self):
+ assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
+
+ def test_eq_long_text(self):
+ a = '1'*100 + 'a' + '2'*100
+ b = '1'*100 + 'b' + '2'*100
+ assert a == b
+
+ def test_eq_long_text_multiline(self):
+ a = '1\n'*100 + 'a' + '2\n'*100
+ b = '1\n'*100 + 'b' + '2\n'*100
+ assert a == b
+
+ def test_eq_list(self):
+ assert [0, 1, 2] == [0, 1, 3]
+
+ def test_eq_list_long(self):
+ a = [0]*100 + [1] + [3]*100
+ b = [0]*100 + [2] + [3]*100
+ assert a == b
+
+ def test_eq_dict(self):
+ assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
+
+ def test_eq_set(self):
+ assert set([0, 10, 11, 12]) == set([0, 20, 21])
+
+ def test_eq_longer_list(self):
+ assert [1,2] == [1,2,3]
+
+ def test_in_list(self):
+ assert 1 in [0, 2, 3, 4, 5]
+
+ def test_not_in_text_multiline(self):
+ text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail'
+ assert 'foo' not in text
+
+ def test_not_in_text_single(self):
+ text = 'single foo line'
+ assert 'foo' not in text
+
+ def test_not_in_text_single_long(self):
+ text = 'head ' * 50 + 'foo ' + 'tail ' * 20
+ assert 'foo' not in text
+
+ def test_not_in_text_single_long_term(self):
+ text = 'head ' * 50 + 'f'*70 + 'tail ' * 20
+ assert 'f'*70 not in text
+
+
+def test_attribute():
+ class Foo(object):
+ b = 1
+ i = Foo()
+ assert i.b == 2
+
+
+def test_attribute_instance():
+ class Foo(object):
+ b = 1
+ assert Foo().b == 2
+
+
+def test_attribute_failure():
+ class Foo(object):
+ def _get_b(self):
+ raise Exception('Failed to get attrib')
+ b = property(_get_b)
+ i = Foo()
+ assert i.b == 2
+
+
+def test_attribute_multiple():
+ class Foo(object):
+ b = 1
+ class Bar(object):
+ b = 2
+ assert Foo().b == Bar().b
+
+
+def globf(x):
+ return x+1
+
+class TestRaises(object):
+ def test_raises(self):
+ s = 'qwe'
+ raises(TypeError, "int(s)")
+
+ def test_raises_doesnt(self):
+ raises(IOError, "int('3')")
+
+ def test_raise(self):
+ raise ValueError("demo error")
+
+ def test_tupleerror(self):
+ a,b = [1]
+
+ def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
+ l = [1,2,3]
+ print ("l is %r" % l)
+ a,b = l.pop()
+
+ def test_some_error(self):
+ if namenotexi:
+ pass
+
+ def func1(self):
+ assert 41 == 42
+
+
+# thanks to Matthew Scott for this test
+def test_dynamic_compile_shows_nicely():
+ src = 'def foo():\n assert 1 == 0\n'
+ name = 'abc-123'
+ module = py.std.imp.new_module(name)
+ code = _pytest._code.compile(src, name, 'exec')
+ py.builtin.exec_(code, module.__dict__)
+ py.std.sys.modules[name] = module
+ module.foo()
+
+
+
+class TestMoreErrors(object):
+ def test_complex_error(self):
+ def f():
+ return 44
+ def g():
+ return 43
+ somefunc(f(), g())
+
+ def test_z1_unpack_error(self):
+ l = []
+ a,b = l
+
+ def test_z2_type_error(self):
+ l = 3
+ a,b = l
+
+ def test_startswith(self):
+ s = "123"
+ g = "456"
+ assert s.startswith(g)
+
+ def test_startswith_nested(self):
+ def f():
+ return "123"
+ def g():
+ return "456"
+ assert f().startswith(g())
+
+ def test_global_func(self):
+ assert isinstance(globf(42), float)
+
+ def test_instance(self):
+ self.x = 6*7
+ assert self.x != 42
+
+ def test_compare(self):
+ assert globf(10) < 5
+
+ def test_try_finally(self):
+ x = 1
+ try:
+ assert x == 0
+ finally:
+ x = 0
+
+
+class TestCustomAssertMsg(object):
+
+ def test_single_line(self):
+ class A(object):
+ a = 1
+ b = 2
+ assert A.a == b, "A.a appears not to be b"
+
+ def test_multiline(self):
+ class A(object):
+ a = 1
+ b = 2
+ assert A.a == b, "A.a appears not to be b\n" \
+ "or does not appear to be b\none of those"
+
+ def test_custom_repr(self):
+ class JSON(object):
+ a = 1
+ def __repr__(self):
+ return "This is JSON\n{\n 'foo': 'bar'\n}"
+ a = JSON()
+ b = 2
+ assert a.a == b, a
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/global_testmodule_config/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/global_testmodule_config/conftest.py
index 71e8c54be53..71e8c54be53 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/global_testmodule_config/conftest.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/global_testmodule_config/conftest.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/global_testmodule_config/test_hello.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/global_testmodule_config/test_hello.py
index 828e6b9fd7d..828e6b9fd7d 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/global_testmodule_config/test_hello.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/global_testmodule_config/test_hello.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/test_failures.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/test_failures.py
index 2e5cd20b194..2e5cd20b194 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/assertion/test_failures.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/test_failures.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/test_setup_flow_example.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/test_setup_flow_example.py
new file mode 100644
index 00000000000..100effa499f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/assertion/test_setup_flow_example.py
@@ -0,0 +1,42 @@
+def setup_module(module):
+ module.TestStateFullThing.classcount = 0
+
+class TestStateFullThing(object):
+ def setup_class(cls):
+ cls.classcount += 1
+
+ def teardown_class(cls):
+ cls.classcount -= 1
+
+ def setup_method(self, method):
+ self.id = eval(method.__name__[5:])
+
+ def test_42(self):
+ assert self.classcount == 1
+ assert self.id == 42
+
+ def test_23(self):
+ assert self.classcount == 1
+ assert self.id == 23
+
+def teardown_module(module):
+ assert module.TestStateFullThing.classcount == 0
+
+""" For this example the control flow happens as follows::
+ import test_setup_flow_example
+ setup_module(test_setup_flow_example)
+ setup_class(TestStateFullThing)
+ instance = TestStateFullThing()
+ setup_method(instance, instance.test_42)
+ instance.test_42()
+ setup_method(instance, instance.test_23)
+ instance.test_23()
+ teardown_class(TestStateFullThing)
+ teardown_module(test_setup_flow_example)
+
+Note that ``setup_class(TestStateFullThing)`` is called and not
+``TestStateFullThing.setup_class()`` which would require you
+to insert ``setup_class = classmethod(setup_class)`` to make
+your setup function callable.
+"""
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/attic.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/attic.rst
new file mode 100644
index 00000000000..9e124a5d09d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/attic.rst
@@ -0,0 +1,79 @@
+
+.. _`accept example`:
+
+example: specifying and selecting acceptance tests
+--------------------------------------------------------------
+
+.. sourcecode:: python
+
+ # ./conftest.py
+ def pytest_option(parser):
+ group = parser.getgroup("myproject")
+ group.addoption("-A", dest="acceptance", action="store_true",
+ help="run (slow) acceptance tests")
+
+ def pytest_funcarg__accept(request):
+ return AcceptFixture(request)
+
+ class AcceptFixture(object):
+ def __init__(self, request):
+ if not request.config.getoption('acceptance'):
+ pytest.skip("specify -A to run acceptance tests")
+ self.tmpdir = request.config.mktemp(request.function.__name__, numbered=True)
+
+ def run(self, cmd):
+ """ called by test code to execute an acceptance test. """
+ self.tmpdir.chdir()
+ return py.process.cmdexec(cmd)
+
+
+and the actual test function example:
+
+.. sourcecode:: python
+
+ def test_some_acceptance_aspect(accept):
+ accept.tmpdir.mkdir("somesub")
+ result = accept.run("ls -la")
+ assert "somesub" in result
+
+If you run this test without specifying a command line option
+the test will get skipped with an appropriate message. Otherwise
+you can start to add convenience and test support methods
+to your AcceptFixture and drive running of tools or
+applications and provide ways to do assertions about
+the output.
+
+.. _`decorate a funcarg`:
+
+example: decorating a funcarg in a test module
+--------------------------------------------------------------
+
+For larger scale setups it's sometimes useful to decorate
+a funcarg just for a particular test module. We can
+extend the `accept example`_ by putting this in our test module:
+
+.. sourcecode:: python
+
+ def pytest_funcarg__accept(request):
+ # call the next factory (living in our conftest.py)
+ arg = request.getfuncargvalue("accept")
+ # create a special layout in our tempdir
+ arg.tmpdir.mkdir("special")
+ return arg
+
+ class TestSpecialAcceptance(object):
+ def test_sometest(self, accept):
+ assert accept.tmpdir.join("special").check()
+
+Our module level factory will be invoked first and it can
+ask its request object to call the next factory and then
+decorate its result. This mechanism allows us to stay
+ignorant of how/where the function argument is provided -
+in our example from a `conftest plugin`_.
+
+sidenote: the temporary directory used here are instances of
+the `py.path.local`_ class which provides many of the os.path
+methods in a convenient way.
+
+.. _`py.path.local`: ../path.html#local
+.. _`conftest plugin`: customize.html#conftestplugin
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/conftest.py
index f905738c4f6..f905738c4f6 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/conftest.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/conftest.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/conftest.py
new file mode 100644
index 00000000000..ea3c1cffb72
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/conftest.py
@@ -0,0 +1,18 @@
+
+import pytest
+
+@pytest.fixture("session")
+def setup(request):
+ setup = CostlySetup()
+ yield setup
+ setup.finalize()
+
+class CostlySetup(object):
+ def __init__(self):
+ import time
+ print ("performing costly setup")
+ time.sleep(5)
+ self.timecostly = 1
+
+ def finalize(self):
+ del self.timecostly
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub1/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub1/__init__.py
index 792d6005489..792d6005489 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub1/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub1/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub1/test_quick.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub1/test_quick.py
index d97657867e0..d97657867e0 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub1/test_quick.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub1/test_quick.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub2/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub2/__init__.py
index 792d6005489..792d6005489 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub2/__init__.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub2/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub2/test_two.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub2/test_two.py
index 6ed6ee4d898..6ed6ee4d898 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/costlysetup/sub2/test_two.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/costlysetup/sub2/test_two.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/index.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/index.rst
new file mode 100644
index 00000000000..f63cb822a41
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/index.rst
@@ -0,0 +1,34 @@
+
+.. _examples:
+
+Examples and customization tricks
+=================================
+
+Here is a (growing) list of examples. :ref:`Contact <contact>` us if you
+need more examples or have questions. Also take a look at the
+:ref:`comprehensive documentation <toc>` which contains many example
+snippets as well. Also, `pytest on stackoverflow.com
+<http://stackoverflow.com/search?q=pytest>`_ often comes with example
+answers.
+
+For basic examples, see
+
+- :doc:`../getting-started` for basic introductory examples
+- :ref:`assert` for basic assertion examples
+- :ref:`fixtures` for basic fixture/setup examples
+- :ref:`parametrize` for basic test function parametrization
+- :doc:`../unittest` for basic unittest integration
+- :doc:`../nose` for basic nosetests integration
+
+The following examples aim at various use cases you might encounter.
+
+.. toctree::
+ :maxdepth: 2
+
+ reportingdemo
+ simple
+ parametrize
+ markers
+ special
+ pythoncollection
+ nonpython
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/markers.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/markers.rst
new file mode 100644
index 00000000000..43c20d5b7dc
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/markers.rst
@@ -0,0 +1,642 @@
+
+.. _`mark examples`:
+
+Working with custom markers
+=================================================
+
+Here are some example using the :ref:`mark` mechanism.
+
+Marking test functions and selecting them for a run
+----------------------------------------------------
+
+You can "mark" a test function with custom metadata like this::
+
+ # content of test_server.py
+
+ import pytest
+ @pytest.mark.webtest
+ def test_send_http():
+ pass # perform some webtest test for your app
+ def test_something_quick():
+ pass
+ def test_another():
+ pass
+ class TestClass(object):
+ def test_method(self):
+ pass
+
+.. versionadded:: 2.2
+
+You can then restrict a test run to only run tests marked with ``webtest``::
+
+ $ pytest -v -m webtest
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collecting ... collected 4 items
+
+ test_server.py::test_send_http PASSED [100%]
+
+ ============================ 3 tests deselected ============================
+ ================== 1 passed, 3 deselected in 0.12 seconds ==================
+
+Or the inverse, running all tests except the webtest ones::
+
+ $ pytest -v -m "not webtest"
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collecting ... collected 4 items
+
+ test_server.py::test_something_quick PASSED [ 33%]
+ test_server.py::test_another PASSED [ 66%]
+ test_server.py::TestClass::test_method PASSED [100%]
+
+ ============================ 1 tests deselected ============================
+ ================== 3 passed, 1 deselected in 0.12 seconds ==================
+
+Selecting tests based on their node ID
+--------------------------------------
+
+You can provide one or more :ref:`node IDs <node-id>` as positional
+arguments to select only specified tests. This makes it easy to select
+tests based on their module, class, method, or function name::
+
+ $ pytest -v test_server.py::TestClass::test_method
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collecting ... collected 1 item
+
+ test_server.py::TestClass::test_method PASSED [100%]
+
+ ========================= 1 passed in 0.12 seconds =========================
+
+You can also select on the class::
+
+ $ pytest -v test_server.py::TestClass
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collecting ... collected 1 item
+
+ test_server.py::TestClass::test_method PASSED [100%]
+
+ ========================= 1 passed in 0.12 seconds =========================
+
+Or select multiple nodes::
+
+ $ pytest -v test_server.py::TestClass test_server.py::test_send_http
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collecting ... collected 2 items
+
+ test_server.py::TestClass::test_method PASSED [ 50%]
+ test_server.py::test_send_http PASSED [100%]
+
+ ========================= 2 passed in 0.12 seconds =========================
+
+.. _node-id:
+
+.. note::
+
+ Node IDs are of the form ``module.py::class::method`` or
+ ``module.py::function``. Node IDs control which tests are
+ collected, so ``module.py::class`` will select all test methods
+ on the class. Nodes are also created for each parameter of a
+ parametrized fixture or test, so selecting a parametrized test
+ must include the parameter value, e.g.
+ ``module.py::function[param]``.
+
+ Node IDs for failing tests are displayed in the test summary info
+ when running pytest with the ``-rf`` option. You can also
+ construct Node IDs from the output of ``pytest --collectonly``.
+
+Using ``-k expr`` to select tests based on their name
+-------------------------------------------------------
+
+.. versionadded: 2.0/2.3.4
+
+You can use the ``-k`` command line option to specify an expression
+which implements a substring match on the test names instead of the
+exact match on markers that ``-m`` provides. This makes it easy to
+select tests based on their names::
+
+ $ pytest -v -k http # running with the above defined example module
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collecting ... collected 4 items
+
+ test_server.py::test_send_http PASSED [100%]
+
+ ============================ 3 tests deselected ============================
+ ================== 1 passed, 3 deselected in 0.12 seconds ==================
+
+And you can also run all tests except the ones that match the keyword::
+
+ $ pytest -k "not send_http" -v
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collecting ... collected 4 items
+
+ test_server.py::test_something_quick PASSED [ 33%]
+ test_server.py::test_another PASSED [ 66%]
+ test_server.py::TestClass::test_method PASSED [100%]
+
+ ============================ 1 tests deselected ============================
+ ================== 3 passed, 1 deselected in 0.12 seconds ==================
+
+Or to select "http" and "quick" tests::
+
+ $ pytest -k "http or quick" -v
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collecting ... collected 4 items
+
+ test_server.py::test_send_http PASSED [ 50%]
+ test_server.py::test_something_quick PASSED [100%]
+
+ ============================ 2 tests deselected ============================
+ ================== 2 passed, 2 deselected in 0.12 seconds ==================
+
+.. note::
+
+ If you are using expressions such as ``"X and Y"`` then both ``X`` and ``Y``
+ need to be simple non-keyword names. For example, ``"pass"`` or ``"from"``
+ will result in SyntaxErrors because ``"-k"`` evaluates the expression using
+ Python's `eval`_ function.
+
+.. _`eval`: https://docs.python.org/3.6/library/functions.html#eval
+
+
+ However, if the ``"-k"`` argument is a simple string, no such restrictions
+ apply. Also ``"-k 'not STRING'"`` has no restrictions. You can also
+ specify numbers like ``"-k 1.3"`` to match tests which are parametrized
+ with the float ``"1.3"``.
+
+Registering markers
+-------------------------------------
+
+.. versionadded:: 2.2
+
+.. ini-syntax for custom markers:
+
+Registering markers for your test suite is simple::
+
+ # content of pytest.ini
+ [pytest]
+ markers =
+ webtest: mark a test as a webtest.
+
+You can ask which markers exist for your test suite - the list includes our just defined ``webtest`` markers::
+
+ $ pytest --markers
+ @pytest.mark.webtest: mark a test as a webtest.
+
+ @pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.
+
+ @pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
+
+ @pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
+
+ @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
+
+ @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
+
+ @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
+
+ @pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
+
+
+For an example on how to add and work with markers from a plugin, see
+:ref:`adding a custom marker from a plugin`.
+
+.. note::
+
+ It is recommended to explicitly register markers so that:
+
+ * There is one place in your test suite defining your markers
+
+ * Asking for existing markers via ``pytest --markers`` gives good output
+
+ * Typos in function markers are treated as an error if you use
+ the ``--strict`` option.
+
+.. _`scoped-marking`:
+
+Marking whole classes or modules
+----------------------------------------------------
+
+You may use ``pytest.mark`` decorators with classes to apply markers to all of
+its test methods::
+
+ # content of test_mark_classlevel.py
+ import pytest
+ @pytest.mark.webtest
+ class TestClass(object):
+ def test_startup(self):
+ pass
+ def test_startup_and_more(self):
+ pass
+
+This is equivalent to directly applying the decorator to the
+two test functions.
+
+To remain backward-compatible with Python 2.4 you can also set a
+``pytestmark`` attribute on a TestClass like this::
+
+ import pytest
+
+ class TestClass(object):
+ pytestmark = pytest.mark.webtest
+
+or if you need to use multiple markers you can use a list::
+
+ import pytest
+
+ class TestClass(object):
+ pytestmark = [pytest.mark.webtest, pytest.mark.slowtest]
+
+You can also set a module level marker::
+
+ import pytest
+ pytestmark = pytest.mark.webtest
+
+in which case it will be applied to all functions and
+methods defined in the module.
+
+.. _`marking individual tests when using parametrize`:
+
+Marking individual tests when using parametrize
+-----------------------------------------------
+
+When using parametrize, applying a mark will make it apply
+to each individual test. However it is also possible to
+apply a marker to an individual test instance::
+
+ import pytest
+
+ @pytest.mark.foo
+ @pytest.mark.parametrize(("n", "expected"), [
+ (1, 2),
+ pytest.mark.bar((1, 3)),
+ (2, 3),
+ ])
+ def test_increment(n, expected):
+ assert n + 1 == expected
+
+In this example the mark "foo" will apply to each of the three
+tests, whereas the "bar" mark is only applied to the second test.
+Skip and xfail marks can also be applied in this way, see :ref:`skip/xfail with parametrize`.
+
+.. note::
+
+ If the data you are parametrizing happen to be single callables, you need to be careful
+ when marking these items. `pytest.mark.xfail(my_func)` won't work because it's also the
+ signature of a function being decorated. To resolve this ambiguity, you need to pass a
+ reason argument:
+ `pytest.mark.xfail(func_bar, reason="Issue#7")`.
+
+
+.. _`adding a custom marker from a plugin`:
+
+Custom marker and command line option to control test runs
+----------------------------------------------------------
+
+.. regendoc:wipe
+
+Plugins can provide custom markers and implement specific behaviour
+based on it. This is a self-contained example which adds a command
+line option and a parametrized test function marker to run tests
+specifies via named environments::
+
+ # content of conftest.py
+
+ import pytest
+ def pytest_addoption(parser):
+ parser.addoption("-E", action="store", metavar="NAME",
+ help="only run tests matching the environment NAME.")
+
+ def pytest_configure(config):
+ # register an additional marker
+ config.addinivalue_line("markers",
+ "env(name): mark test to run only on named environment")
+
+ def pytest_runtest_setup(item):
+ envmarker = item.get_marker("env")
+ if envmarker is not None:
+ envname = envmarker.args[0]
+ if envname != item.config.getoption("-E"):
+ pytest.skip("test requires env %r" % envname)
+
+A test file using this local plugin::
+
+ # content of test_someenv.py
+
+ import pytest
+ @pytest.mark.env("stage1")
+ def test_basic_db_operation():
+ pass
+
+and an example invocations specifying a different environment than what
+the test needs::
+
+ $ pytest -E stage2
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 1 item
+
+ test_someenv.py s [100%]
+
+ ======================== 1 skipped in 0.12 seconds =========================
+
+and here is one that specifies exactly the environment needed::
+
+ $ pytest -E stage1
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 1 item
+
+ test_someenv.py . [100%]
+
+ ========================= 1 passed in 0.12 seconds =========================
+
+The ``--markers`` option always gives you a list of available markers::
+
+ $ pytest --markers
+ @pytest.mark.env(name): mark test to run only on named environment
+
+ @pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.
+
+ @pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
+
+ @pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
+
+ @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
+
+ @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
+
+ @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
+
+ @pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
+
+
+.. _`passing callables to custom markers`:
+
+Passing a callable to custom markers
+--------------------------------------------
+
+.. regendoc:wipe
+
+Below is the config file that will be used in the next examples::
+
+ # content of conftest.py
+ import sys
+
+ def pytest_runtest_setup(item):
+ marker = item.get_marker('my_marker')
+ if marker is not None:
+ for info in marker:
+ print('Marker info name={} args={} kwars={}'.format(info.name, info.args, info.kwargs))
+ sys.stdout.flush()
+
+A custom marker can have its argument set, i.e. ``args`` and ``kwargs`` properties, defined by either invoking it as a callable or using ``pytest.mark.MARKER_NAME.with_args``. These two methods achieve the same effect most of the time.
+
+However, if there is a callable as the single positional argument with no keyword arguments, using the ``pytest.mark.MARKER_NAME(c)`` will not pass ``c`` as a positional argument but decorate ``c`` with the custom marker (see :ref:`MarkDecorator <mark>`). Fortunately, ``pytest.mark.MARKER_NAME.with_args`` comes to the rescue::
+
+ # content of test_custom_marker.py
+ import pytest
+
+ def hello_world(*args, **kwargs):
+ return 'Hello World'
+
+ @pytest.mark.my_marker.with_args(hello_world)
+ def test_with_args():
+ pass
+
+The output is as follows::
+
+ $ pytest -q -s
+ Marker info name=my_marker args=(<function hello_world at 0xdeadbeef>,) kwars={}
+ . [100%]
+ 1 passed in 0.12 seconds
+
+We can see that the custom marker has its argument set extended with the function ``hello_world``. This is the key difference between creating a custom marker as a callable, which invokes ``__call__`` behind the scenes, and using ``with_args``.
+
+
+Reading markers which were set from multiple places
+----------------------------------------------------
+
+.. versionadded: 2.2.2
+
+.. regendoc:wipe
+
+If you are heavily using markers in your test suite you may encounter the case where a marker is applied several times to a test function. From plugin
+code you can read over all such settings. Example::
+
+ # content of test_mark_three_times.py
+ import pytest
+ pytestmark = pytest.mark.glob("module", x=1)
+
+ @pytest.mark.glob("class", x=2)
+ class TestClass(object):
+ @pytest.mark.glob("function", x=3)
+ def test_something(self):
+ pass
+
+Here we have the marker "glob" applied three times to the same
+test function. From a conftest file we can read it like this::
+
+ # content of conftest.py
+ import sys
+
+ def pytest_runtest_setup(item):
+ g = item.get_marker("glob")
+ if g is not None:
+ for info in g:
+ print ("glob args=%s kwargs=%s" %(info.args, info.kwargs))
+ sys.stdout.flush()
+
+Let's run this without capturing output and see what we get::
+
+ $ pytest -q -s
+ glob args=('function',) kwargs={'x': 3}
+ glob args=('class',) kwargs={'x': 2}
+ glob args=('module',) kwargs={'x': 1}
+ . [100%]
+ 1 passed in 0.12 seconds
+
+marking platform specific tests with pytest
+--------------------------------------------------------------
+
+.. regendoc:wipe
+
+Consider you have a test suite which marks tests for particular platforms,
+namely ``pytest.mark.darwin``, ``pytest.mark.win32`` etc. and you
+also have tests that run on all platforms and have no specific
+marker. If you now want to have a way to only run the tests
+for your particular platform, you could use the following plugin::
+
+ # content of conftest.py
+ #
+ import sys
+ import pytest
+
+ ALL = set("darwin linux win32".split())
+
+ def pytest_runtest_setup(item):
+ if isinstance(item, item.Function):
+ plat = sys.platform
+ if not item.get_marker(plat):
+ if ALL.intersection(item.keywords):
+ pytest.skip("cannot run on platform %s" %(plat))
+
+then tests will be skipped if they were specified for a different platform.
+Let's do a little test file to show how this looks like::
+
+ # content of test_plat.py
+
+ import pytest
+
+ @pytest.mark.darwin
+ def test_if_apple_is_evil():
+ pass
+
+ @pytest.mark.linux
+ def test_if_linux_works():
+ pass
+
+ @pytest.mark.win32
+ def test_if_win32_crashes():
+ pass
+
+ def test_runs_everywhere():
+ pass
+
+then you will see two tests skipped and two executed tests as expected::
+
+ $ pytest -rs # this option reports skip reasons
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 4 items
+
+ test_plat.py s.s. [100%]
+ ========================= short test summary info ==========================
+ SKIP [2] $REGENDOC_TMPDIR/conftest.py:13: cannot run on platform linux
+
+ =================== 2 passed, 2 skipped in 0.12 seconds ====================
+
+Note that if you specify a platform via the marker-command line option like this::
+
+ $ pytest -m linux
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 4 items
+
+ test_plat.py . [100%]
+
+ ============================ 3 tests deselected ============================
+ ================== 1 passed, 3 deselected in 0.12 seconds ==================
+
+then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests.
+
+Automatically adding markers based on test names
+--------------------------------------------------------
+
+.. regendoc:wipe
+
+If you a test suite where test function names indicate a certain
+type of test, you can implement a hook that automatically defines
+markers so that you can use the ``-m`` option with it. Let's look
+at this test module::
+
+ # content of test_module.py
+
+ def test_interface_simple():
+ assert 0
+
+ def test_interface_complex():
+ assert 0
+
+ def test_event_simple():
+ assert 0
+
+ def test_something_else():
+ assert 0
+
+We want to dynamically define two markers and can do it in a
+``conftest.py`` plugin::
+
+ # content of conftest.py
+
+ import pytest
+ def pytest_collection_modifyitems(items):
+ for item in items:
+ if "interface" in item.nodeid:
+ item.add_marker(pytest.mark.interface)
+ elif "event" in item.nodeid:
+ item.add_marker(pytest.mark.event)
+
+We can now use the ``-m option`` to select one set::
+
+ $ pytest -m interface --tb=short
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 4 items
+
+ test_module.py FF [100%]
+
+ ================================= FAILURES =================================
+ __________________________ test_interface_simple ___________________________
+ test_module.py:3: in test_interface_simple
+ assert 0
+ E assert 0
+ __________________________ test_interface_complex __________________________
+ test_module.py:6: in test_interface_complex
+ assert 0
+ E assert 0
+ ============================ 2 tests deselected ============================
+ ================== 2 failed, 2 deselected in 0.12 seconds ==================
+
+or to select both "event" and "interface" tests::
+
+ $ pytest -m "interface or event" --tb=short
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 4 items
+
+ test_module.py FFF [100%]
+
+ ================================= FAILURES =================================
+ __________________________ test_interface_simple ___________________________
+ test_module.py:3: in test_interface_simple
+ assert 0
+ E assert 0
+ __________________________ test_interface_complex __________________________
+ test_module.py:6: in test_interface_complex
+ assert 0
+ E assert 0
+ ____________________________ test_event_simple _____________________________
+ test_module.py:9: in test_event_simple
+ assert 0
+ E assert 0
+ ============================ 1 tests deselected ============================
+ ================== 3 failed, 1 deselected in 0.12 seconds ==================
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/multipython.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/multipython.py
new file mode 100644
index 00000000000..66079be7e37
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/multipython.py
@@ -0,0 +1,52 @@
+"""
+module containing a parametrized tests testing cross-python
+serialization via the pickle module.
+"""
+import py
+import pytest
+import _pytest._code
+
+pythonlist = ['python2.7', 'python3.4', 'python3.5']
+@pytest.fixture(params=pythonlist)
+def python1(request, tmpdir):
+ picklefile = tmpdir.join("data.pickle")
+ return Python(request.param, picklefile)
+
+@pytest.fixture(params=pythonlist)
+def python2(request, python1):
+ return Python(request.param, python1.picklefile)
+
+class Python(object):
+ def __init__(self, version, picklefile):
+ self.pythonpath = py.path.local.sysfind(version)
+ if not self.pythonpath:
+ pytest.skip("%r not found" %(version,))
+ self.picklefile = picklefile
+ def dumps(self, obj):
+ dumpfile = self.picklefile.dirpath("dump.py")
+ dumpfile.write(_pytest._code.Source("""
+ import pickle
+ f = open(%r, 'wb')
+ s = pickle.dump(%r, f, protocol=2)
+ f.close()
+ """ % (str(self.picklefile), obj)))
+ py.process.cmdexec("%s %s" %(self.pythonpath, dumpfile))
+
+ def load_and_is_true(self, expression):
+ loadfile = self.picklefile.dirpath("load.py")
+ loadfile.write(_pytest._code.Source("""
+ import pickle
+ f = open(%r, 'rb')
+ obj = pickle.load(f)
+ f.close()
+ res = eval(%r)
+ if not res:
+ raise SystemExit(1)
+ """ % (str(self.picklefile), expression)))
+ print (loadfile)
+ py.process.cmdexec("%s %s" %(self.pythonpath, loadfile))
+
+@pytest.mark.parametrize("obj", [42, {}, {1:3},])
+def test_basic_objects(python1, python2, obj):
+ python1.dumps(obj)
+ python2.load_and_is_true("obj == %s" % obj)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython.rst
new file mode 100644
index 00000000000..cf72c7219e1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython.rst
@@ -0,0 +1,91 @@
+
+.. _`non-python tests`:
+
+Working with non-python tests
+====================================================
+
+.. _`yaml plugin`:
+
+A basic example for specifying tests in Yaml files
+--------------------------------------------------------------
+
+.. _`pytest-yamlwsgi`: http://bitbucket.org/aafshar/pytest-yamlwsgi/src/tip/pytest_yamlwsgi.py
+.. _`PyYAML`: http://pypi.python.org/pypi/PyYAML/
+
+Here is an example ``conftest.py`` (extracted from Ali Afshnars special purpose `pytest-yamlwsgi`_ plugin). This ``conftest.py`` will collect ``test*.yml`` files and will execute the yaml-formatted content as custom tests:
+
+.. include:: nonpython/conftest.py
+ :literal:
+
+You can create a simple example file:
+
+.. include:: nonpython/test_simple.yml
+ :literal:
+
+and if you installed `PyYAML`_ or a compatible YAML-parser you can
+now execute the test specification::
+
+ nonpython $ pytest test_simple.yml
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
+ collected 2 items
+
+ test_simple.yml F. [100%]
+
+ ================================= FAILURES =================================
+ ______________________________ usecase: hello ______________________________
+ usecase execution failed
+ spec failed: 'some': 'other'
+ no further details known at this point.
+ ==================== 1 failed, 1 passed in 0.12 seconds ====================
+
+.. regendoc:wipe
+
+You get one dot for the passing ``sub1: sub1`` check and one failure.
+Obviously in the above ``conftest.py`` you'll want to implement a more
+interesting interpretation of the yaml-values. You can easily write
+your own domain specific testing language this way.
+
+.. note::
+
+ ``repr_failure(excinfo)`` is called for representing test failures.
+ If you create custom collection nodes you can return an error
+ representation string of your choice. It
+ will be reported as a (red) string.
+
+``reportinfo()`` is used for representing the test location and is also
+consulted when reporting in ``verbose`` mode::
+
+ nonpython $ pytest -v
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
+ collecting ... collected 2 items
+
+ test_simple.yml::hello FAILED [ 50%]
+ test_simple.yml::ok PASSED [100%]
+
+ ================================= FAILURES =================================
+ ______________________________ usecase: hello ______________________________
+ usecase execution failed
+ spec failed: 'some': 'other'
+ no further details known at this point.
+ ==================== 1 failed, 1 passed in 0.12 seconds ====================
+
+.. regendoc:wipe
+
+While developing your custom test collection and execution it's also
+interesting to just look at the collection tree::
+
+ nonpython $ pytest --collect-only
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
+ collected 2 items
+ <YamlFile 'test_simple.yml'>
+ <YamlItem 'hello'>
+ <YamlItem 'ok'>
+
+ ======================= no tests ran in 0.12 seconds =======================
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython/__init__.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython/conftest.py
new file mode 100644
index 00000000000..baff3001550
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython/conftest.py
@@ -0,0 +1,40 @@
+# content of conftest.py
+
+import pytest
+
+def pytest_collect_file(parent, path):
+ if path.ext == ".yml" and path.basename.startswith("test"):
+ return YamlFile(path, parent)
+
+class YamlFile(pytest.File):
+ def collect(self):
+ import yaml # we need a yaml parser, e.g. PyYAML
+ raw = yaml.safe_load(self.fspath.open())
+ for name, spec in sorted(raw.items()):
+ yield YamlItem(name, self, spec)
+
+class YamlItem(pytest.Item):
+ def __init__(self, name, parent, spec):
+ super(YamlItem, self).__init__(name, parent)
+ self.spec = spec
+
+ def runtest(self):
+ for name, value in sorted(self.spec.items()):
+ # some custom test execution (dumb example follows)
+ if name != value:
+ raise YamlException(self, name, value)
+
+ def repr_failure(self, excinfo):
+ """ called when self.runtest() raises an exception. """
+ if isinstance(excinfo.value, YamlException):
+ return "\n".join([
+ "usecase execution failed",
+ " spec failed: %r: %r" % excinfo.value.args[1:3],
+ " no further details known at this point."
+ ])
+
+ def reportinfo(self):
+ return self.fspath, 0, "usecase: %s" % self.name
+
+class YamlException(Exception):
+ """ custom exception for error reporting. """
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython/test_simple.yml b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython/test_simple.yml
index f0d8d11fc3e..f0d8d11fc3e 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/nonpython/test_simple.yml
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/nonpython/test_simple.yml
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/parametrize.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/parametrize.rst
new file mode 100644
index 00000000000..dd01b25277a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/parametrize.rst
@@ -0,0 +1,536 @@
+
+.. _paramexamples:
+
+Parametrizing tests
+=================================================
+
+.. currentmodule:: _pytest.python
+
+``pytest`` allows to easily parametrize test functions.
+For basic docs, see :ref:`parametrize-basics`.
+
+In the following we provide some examples using
+the builtin mechanisms.
+
+Generating parameters combinations, depending on command line
+----------------------------------------------------------------------------
+
+.. regendoc:wipe
+
+Let's say we want to execute a test with different computation
+parameters and the parameter range shall be determined by a command
+line argument. Let's first write a simple (do-nothing) computation test::
+
+ # content of test_compute.py
+
+ def test_compute(param1):
+ assert param1 < 4
+
+Now we add a test configuration like this::
+
+ # content of conftest.py
+
+ def pytest_addoption(parser):
+ parser.addoption("--all", action="store_true",
+ help="run all combinations")
+
+ def pytest_generate_tests(metafunc):
+ if 'param1' in metafunc.fixturenames:
+ if metafunc.config.getoption('all'):
+ end = 5
+ else:
+ end = 2
+ metafunc.parametrize("param1", range(end))
+
+This means that we only run 2 tests if we do not pass ``--all``::
+
+ $ pytest -q test_compute.py
+ .. [100%]
+ 2 passed in 0.12 seconds
+
+We run only two computations, so we see two dots.
+let's run the full monty::
+
+ $ pytest -q --all
+ ....F [100%]
+ ================================= FAILURES =================================
+ _____________________________ test_compute[4] ______________________________
+
+ param1 = 4
+
+ def test_compute(param1):
+ > assert param1 < 4
+ E assert 4 < 4
+
+ test_compute.py:3: AssertionError
+ 1 failed, 4 passed in 0.12 seconds
+
+As expected when running the full range of ``param1`` values
+we'll get an error on the last one.
+
+
+Different options for test IDs
+------------------------------------
+
+pytest will build a string that is the test ID for each set of values in a
+parametrized test. These IDs can be used with ``-k`` to select specific cases
+to run, and they will also identify the specific case when one is failing.
+Running pytest with ``--collect-only`` will show the generated IDs.
+
+Numbers, strings, booleans and None will have their usual string representation
+used in the test ID. For other objects, pytest will make a string based on
+the argument name::
+
+ # content of test_time.py
+
+ import pytest
+
+ from datetime import datetime, timedelta
+
+ testdata = [
+ (datetime(2001, 12, 12), datetime(2001, 12, 11), timedelta(1)),
+ (datetime(2001, 12, 11), datetime(2001, 12, 12), timedelta(-1)),
+ ]
+
+
+ @pytest.mark.parametrize("a,b,expected", testdata)
+ def test_timedistance_v0(a, b, expected):
+ diff = a - b
+ assert diff == expected
+
+
+ @pytest.mark.parametrize("a,b,expected", testdata, ids=["forward", "backward"])
+ def test_timedistance_v1(a, b, expected):
+ diff = a - b
+ assert diff == expected
+
+
+ def idfn(val):
+ if isinstance(val, (datetime,)):
+ # note this wouldn't show any hours/minutes/seconds
+ return val.strftime('%Y%m%d')
+
+
+ @pytest.mark.parametrize("a,b,expected", testdata, ids=idfn)
+ def test_timedistance_v2(a, b, expected):
+ diff = a - b
+ assert diff == expected
+
+ @pytest.mark.parametrize("a,b,expected", [
+ pytest.param(datetime(2001, 12, 12), datetime(2001, 12, 11),
+ timedelta(1), id='forward'),
+ pytest.param(datetime(2001, 12, 11), datetime(2001, 12, 12),
+ timedelta(-1), id='backward'),
+ ])
+ def test_timedistance_v3(a, b, expected):
+ diff = a - b
+ assert diff == expected
+
+In ``test_timedistance_v0``, we let pytest generate the test IDs.
+
+In ``test_timedistance_v1``, we specified ``ids`` as a list of strings which were
+used as the test IDs. These are succinct, but can be a pain to maintain.
+
+In ``test_timedistance_v2``, we specified ``ids`` as a function that can generate a
+string representation to make part of the test ID. So our ``datetime`` values use the
+label generated by ``idfn``, but because we didn't generate a label for ``timedelta``
+objects, they are still using the default pytest representation::
+
+
+ $ pytest test_time.py --collect-only
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 8 items
+ <Module 'test_time.py'>
+ <Function 'test_timedistance_v0[a0-b0-expected0]'>
+ <Function 'test_timedistance_v0[a1-b1-expected1]'>
+ <Function 'test_timedistance_v1[forward]'>
+ <Function 'test_timedistance_v1[backward]'>
+ <Function 'test_timedistance_v2[20011212-20011211-expected0]'>
+ <Function 'test_timedistance_v2[20011211-20011212-expected1]'>
+ <Function 'test_timedistance_v3[forward]'>
+ <Function 'test_timedistance_v3[backward]'>
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+In ``test_timedistance_v3``, we used ``pytest.param`` to specify the test IDs
+together with the actual data, instead of listing them separately.
+
+A quick port of "testscenarios"
+------------------------------------
+
+.. _`test scenarios`: http://pypi.python.org/pypi/testscenarios/
+
+Here is a quick port to run tests configured with `test scenarios`_,
+an add-on from Robert Collins for the standard unittest framework. We
+only have to work a bit to construct the correct arguments for pytest's
+:py:func:`Metafunc.parametrize`::
+
+ # content of test_scenarios.py
+
+ def pytest_generate_tests(metafunc):
+ idlist = []
+ argvalues = []
+ for scenario in metafunc.cls.scenarios:
+ idlist.append(scenario[0])
+ items = scenario[1].items()
+ argnames = [x[0] for x in items]
+ argvalues.append(([x[1] for x in items]))
+ metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class")
+
+ scenario1 = ('basic', {'attribute': 'value'})
+ scenario2 = ('advanced', {'attribute': 'value2'})
+
+ class TestSampleWithScenarios(object):
+ scenarios = [scenario1, scenario2]
+
+ def test_demo1(self, attribute):
+ assert isinstance(attribute, str)
+
+ def test_demo2(self, attribute):
+ assert isinstance(attribute, str)
+
+this is a fully self-contained example which you can run with::
+
+ $ pytest test_scenarios.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 4 items
+
+ test_scenarios.py .... [100%]
+
+ ========================= 4 passed in 0.12 seconds =========================
+
+If you just collect tests you'll also nicely see 'advanced' and 'basic' as variants for the test function::
+
+
+ $ pytest --collect-only test_scenarios.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 4 items
+ <Module 'test_scenarios.py'>
+ <Class 'TestSampleWithScenarios'>
+ <Instance '()'>
+ <Function 'test_demo1[basic]'>
+ <Function 'test_demo2[basic]'>
+ <Function 'test_demo1[advanced]'>
+ <Function 'test_demo2[advanced]'>
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+Note that we told ``metafunc.parametrize()`` that your scenario values
+should be considered class-scoped. With pytest-2.3 this leads to a
+resource-based ordering.
+
+Deferring the setup of parametrized resources
+---------------------------------------------------
+
+.. regendoc:wipe
+
+The parametrization of test functions happens at collection
+time. It is a good idea to setup expensive resources like DB
+connections or subprocess only when the actual test is run.
+Here is a simple example how you can achieve that, first
+the actual test requiring a ``db`` object::
+
+ # content of test_backends.py
+
+ import pytest
+ def test_db_initialized(db):
+ # a dummy test
+ if db.__class__.__name__ == "DB2":
+ pytest.fail("deliberately failing for demo purposes")
+
+We can now add a test configuration that generates two invocations of
+the ``test_db_initialized`` function and also implements a factory that
+creates a database object for the actual test invocations::
+
+ # content of conftest.py
+ import pytest
+
+ def pytest_generate_tests(metafunc):
+ if 'db' in metafunc.fixturenames:
+ metafunc.parametrize("db", ['d1', 'd2'], indirect=True)
+
+ class DB1(object):
+ "one database object"
+ class DB2(object):
+ "alternative database object"
+
+ @pytest.fixture
+ def db(request):
+ if request.param == "d1":
+ return DB1()
+ elif request.param == "d2":
+ return DB2()
+ else:
+ raise ValueError("invalid internal test config")
+
+Let's first see how it looks like at collection time::
+
+ $ pytest test_backends.py --collect-only
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 2 items
+ <Module 'test_backends.py'>
+ <Function 'test_db_initialized[d1]'>
+ <Function 'test_db_initialized[d2]'>
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+And then when we run the test::
+
+ $ pytest -q test_backends.py
+ .F [100%]
+ ================================= FAILURES =================================
+ _________________________ test_db_initialized[d2] __________________________
+
+ db = <conftest.DB2 object at 0xdeadbeef>
+
+ def test_db_initialized(db):
+ # a dummy test
+ if db.__class__.__name__ == "DB2":
+ > pytest.fail("deliberately failing for demo purposes")
+ E Failed: deliberately failing for demo purposes
+
+ test_backends.py:6: Failed
+ 1 failed, 1 passed in 0.12 seconds
+
+The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``db`` fixture function has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase.
+
+.. regendoc:wipe
+
+Apply indirect on particular arguments
+---------------------------------------------------
+
+Very often parametrization uses more than one argument name. There is opportunity to apply ``indirect``
+parameter on particular arguments. It can be done by passing list or tuple of
+arguments' names to ``indirect``. In the example below there is a function ``test_indirect`` which uses
+two fixtures: ``x`` and ``y``. Here we give to indirect the list, which contains the name of the
+fixture ``x``. The indirect parameter will be applied to this argument only, and the value ``a``
+will be passed to respective fixture function::
+
+ # content of test_indirect_list.py
+
+ import pytest
+ @pytest.fixture(scope='function')
+ def x(request):
+ return request.param * 3
+
+ @pytest.fixture(scope='function')
+ def y(request):
+ return request.param * 2
+
+ @pytest.mark.parametrize('x, y', [('a', 'b')], indirect=['x'])
+ def test_indirect(x,y):
+ assert x == 'aaa'
+ assert y == 'b'
+
+The result of this test will be successful::
+
+ $ pytest test_indirect_list.py --collect-only
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 1 item
+ <Module 'test_indirect_list.py'>
+ <Function 'test_indirect[a-b]'>
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+.. regendoc:wipe
+
+Parametrizing test methods through per-class configuration
+--------------------------------------------------------------
+
+.. _`unittest parametrizer`: https://github.com/testing-cabal/unittest-ext/blob/master/params.py
+
+
+Here is an example ``pytest_generate_tests`` function implementing a
+parametrization scheme similar to Michael Foord's `unittest
+parametrizer`_ but in a lot less code::
+
+ # content of ./test_parametrize.py
+ import pytest
+
+ def pytest_generate_tests(metafunc):
+ # called once per each test function
+ funcarglist = metafunc.cls.params[metafunc.function.__name__]
+ argnames = sorted(funcarglist[0])
+ metafunc.parametrize(argnames, [[funcargs[name] for name in argnames]
+ for funcargs in funcarglist])
+
+ class TestClass(object):
+ # a map specifying multiple argument sets for a test method
+ params = {
+ 'test_equals': [dict(a=1, b=2), dict(a=3, b=3), ],
+ 'test_zerodivision': [dict(a=1, b=0), ],
+ }
+
+ def test_equals(self, a, b):
+ assert a == b
+
+ def test_zerodivision(self, a, b):
+ pytest.raises(ZeroDivisionError, "a/b")
+
+Our test generator looks up a class-level definition which specifies which
+argument sets to use for each test function. Let's run it::
+
+ $ pytest -q
+ F.. [100%]
+ ================================= FAILURES =================================
+ ________________________ TestClass.test_equals[1-2] ________________________
+
+ self = <test_parametrize.TestClass object at 0xdeadbeef>, a = 1, b = 2
+
+ def test_equals(self, a, b):
+ > assert a == b
+ E assert 1 == 2
+
+ test_parametrize.py:18: AssertionError
+ 1 failed, 2 passed in 0.12 seconds
+
+Indirect parametrization with multiple fixtures
+--------------------------------------------------------------
+
+Here is a stripped down real-life example of using parametrized
+testing for testing serialization of objects between different python
+interpreters. We define a ``test_basic_objects`` function which
+is to be run with different sets of arguments for its three arguments:
+
+* ``python1``: first python interpreter, run to pickle-dump an object to a file
+* ``python2``: second interpreter, run to pickle-load an object from a file
+* ``obj``: object to be dumped/loaded
+
+.. literalinclude:: multipython.py
+
+Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
+
+ . $ pytest -rs -q multipython.py
+ ........................... [100%]
+ 27 passed in 0.12 seconds
+
+Indirect parametrization of optional implementations/imports
+--------------------------------------------------------------------
+
+If you want to compare the outcomes of several implementations of a given
+API, you can write test functions that receive the already imported implementations
+and get skipped in case the implementation is not importable/available. Let's
+say we have a "base" implementation and the other (possibly optimized ones)
+need to provide similar results::
+
+ # content of conftest.py
+
+ import pytest
+
+ @pytest.fixture(scope="session")
+ def basemod(request):
+ return pytest.importorskip("base")
+
+ @pytest.fixture(scope="session", params=["opt1", "opt2"])
+ def optmod(request):
+ return pytest.importorskip(request.param)
+
+And then a base implementation of a simple function::
+
+ # content of base.py
+ def func1():
+ return 1
+
+And an optimized version::
+
+ # content of opt1.py
+ def func1():
+ return 1.0001
+
+And finally a little test module::
+
+ # content of test_module.py
+
+ def test_func1(basemod, optmod):
+ assert round(basemod.func1(), 3) == round(optmod.func1(), 3)
+
+
+If you run this with reporting for skips enabled::
+
+ $ pytest -rs test_module.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 2 items
+
+ test_module.py .s [100%]
+ ========================= short test summary info ==========================
+ SKIP [1] $REGENDOC_TMPDIR/conftest.py:11: could not import 'opt2'
+
+ =================== 1 passed, 1 skipped in 0.12 seconds ====================
+
+You'll see that we don't have a ``opt2`` module and thus the second test run
+of our ``test_func1`` was skipped. A few notes:
+
+- the fixture functions in the ``conftest.py`` file are "session-scoped" because we
+ don't need to import more than once
+
+- if you have multiple test functions and a skipped import, you will see
+ the ``[1]`` count increasing in the report
+
+- you can put :ref:`@pytest.mark.parametrize <@pytest.mark.parametrize>` style
+ parametrization on the test functions to parametrize input/output
+ values as well.
+
+
+Set marks or test ID for individual parametrized test
+--------------------------------------------------------------------
+
+Use ``pytest.param`` to apply marks or set test ID to individual parametrized test.
+For example::
+
+ # content of test_pytest_param_example.py
+ import pytest
+ @pytest.mark.parametrize('test_input,expected', [
+ ('3+5', 8),
+ pytest.param('1+7', 8,
+ marks=pytest.mark.basic),
+ pytest.param('2+4', 6,
+ marks=pytest.mark.basic,
+ id='basic_2+4'),
+ pytest.param('6*9', 42,
+ marks=[pytest.mark.basic, pytest.mark.xfail],
+ id='basic_6*9'),
+ ])
+ def test_eval(test_input, expected):
+ assert eval(test_input) == expected
+
+In this example, we have 4 parametrized tests. Except for the first test,
+we mark the rest three parametrized tests with the custom marker ``basic``,
+and for the fourth test we also use the built-in mark ``xfail`` to indicate this
+test is expected to fail. For explicitness, we set test ids for some tests.
+
+Then run ``pytest`` with verbose mode and with only the ``basic`` marker::
+
+ pytest -v -m basic
+ ============================================ test session starts =============================================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 4 items
+
+ test_pytest_param_example.py::test_eval[1+7-8] PASSED
+ test_pytest_param_example.py::test_eval[basic_2+4] PASSED
+ test_pytest_param_example.py::test_eval[basic_6*9] xfail
+ ========================================== short test summary info ===========================================
+ XFAIL test_pytest_param_example.py::test_eval[basic_6*9]
+
+ ============================================= 1 tests deselected =============================================
+
+As the result:
+
+- Four tests were collected
+- One test was deselected because it doesn't have the ``basic`` mark.
+- Three tests with the ``basic`` mark was selected.
+- The test ``test_eval[1+7-8]`` passed, but the name is autogenerated and confusing.
+- The test ``test_eval[basic_2+4]`` passed.
+- The test ``test_eval[basic_6*9]`` was expected to fail and did fail.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/py2py3/conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/py2py3/conftest.py
index 81cd1fb11ea..81cd1fb11ea 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/py2py3/conftest.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/py2py3/conftest.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/py2py3/test_py2.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/py2py3/test_py2.py
index e09ed946627..e09ed946627 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/py2py3/test_py2.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/py2py3/test_py2.py
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/py2py3/test_py3.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/py2py3/test_py3.py
index a811f2bbc55..a811f2bbc55 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/py2py3/test_py3.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/py2py3/test_py3.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/pythoncollection.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/pythoncollection.py
new file mode 100644
index 00000000000..9c4bd31cea0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/pythoncollection.py
@@ -0,0 +1,11 @@
+
+# run this with $ pytest --collect-only test_collectonly.py
+#
+def test_function():
+ pass
+
+class TestClass(object):
+ def test_method(self):
+ pass
+ def test_anothermethod(self):
+ pass
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/pythoncollection.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/pythoncollection.rst
new file mode 100644
index 00000000000..c9d31d7c420
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/pythoncollection.rst
@@ -0,0 +1,239 @@
+Changing standard (Python) test discovery
+===============================================
+
+Ignore paths during test collection
+-----------------------------------
+
+You can easily ignore certain test directories and modules during collection
+by passing the ``--ignore=path`` option on the cli. ``pytest`` allows multiple
+``--ignore`` options. Example::
+
+ tests/
+ |-- example
+ | |-- test_example_01.py
+ | |-- test_example_02.py
+ | '-- test_example_03.py
+ |-- foobar
+ | |-- test_foobar_01.py
+ | |-- test_foobar_02.py
+ | '-- test_foobar_03.py
+ '-- hello
+ '-- world
+ |-- test_world_01.py
+ |-- test_world_02.py
+ '-- test_world_03.py
+
+Now if you invoke ``pytest`` with ``--ignore=tests/foobar/test_foobar_03.py --ignore=tests/hello/``,
+you will see that ``pytest`` only collects test-modules, which do not match the patterns specified::
+
+ ========= test session starts ==========
+ platform darwin -- Python 2.7.10, pytest-2.8.2, py-1.4.30, pluggy-0.3.1
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 5 items
+
+ tests/example/test_example_01.py .
+ tests/example/test_example_02.py .
+ tests/example/test_example_03.py .
+ tests/foobar/test_foobar_01.py .
+ tests/foobar/test_foobar_02.py .
+
+ ======= 5 passed in 0.02 seconds =======
+
+
+Keeping duplicate paths specified from command line
+----------------------------------------------------
+
+Default behavior of ``pytest`` is to ignore duplicate paths specified from the command line.
+Example::
+
+ py.test path_a path_a
+
+ ...
+ collected 1 item
+ ...
+
+Just collect tests once.
+
+To collect duplicate tests, use the ``--keep-duplicates`` option on the cli.
+Example::
+
+ py.test --keep-duplicates path_a path_a
+
+ ...
+ collected 2 items
+ ...
+
+As the collector just works on directories, if you specify twice a single test file, ``pytest`` will
+still collect it twice, no matter if the ``--keep-duplicates`` is not specified.
+Example::
+
+ py.test test_a.py test_a.py
+
+ ...
+ collected 2 items
+ ...
+
+
+Changing directory recursion
+-----------------------------------------------------
+
+You can set the :confval:`norecursedirs` option in an ini-file, for example your ``pytest.ini`` in the project root directory::
+
+ # content of pytest.ini
+ [pytest]
+ norecursedirs = .svn _build tmp*
+
+This would tell ``pytest`` to not recurse into typical subversion or sphinx-build directories or into any ``tmp`` prefixed directory.
+
+.. _`change naming conventions`:
+
+Changing naming conventions
+-----------------------------------------------------
+
+You can configure different naming conventions by setting
+the :confval:`python_files`, :confval:`python_classes` and
+:confval:`python_functions` configuration options. Example::
+
+ # content of pytest.ini
+ # can also be defined in tox.ini or setup.cfg file, although the section
+ # name in setup.cfg files should be "tool:pytest"
+ [pytest]
+ python_files=check_*.py
+ python_classes=Check
+ python_functions=*_check
+
+This would make ``pytest`` look for tests in files that match the ``check_*
+.py`` glob-pattern, ``Check`` prefixes in classes, and functions and methods
+that match ``*_check``. For example, if we have::
+
+ # content of check_myapp.py
+ class CheckMyApp(object):
+ def simple_check(self):
+ pass
+ def complex_check(self):
+ pass
+
+then the test collection looks like this::
+
+ $ pytest --collect-only
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
+ collected 2 items
+ <Module 'check_myapp.py'>
+ <Class 'CheckMyApp'>
+ <Instance '()'>
+ <Function 'simple_check'>
+ <Function 'complex_check'>
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+.. note::
+
+ the ``python_functions`` and ``python_classes`` options has no effect
+ for ``unittest.TestCase`` test discovery because pytest delegates
+ detection of test case methods to unittest code.
+
+Interpreting cmdline arguments as Python packages
+-----------------------------------------------------
+
+You can use the ``--pyargs`` option to make ``pytest`` try
+interpreting arguments as python package names, deriving
+their file system path and then running the test. For
+example if you have unittest2 installed you can type::
+
+ pytest --pyargs unittest2.test.test_skipping -q
+
+which would run the respective test module. Like with
+other options, through an ini-file and the :confval:`addopts` option you
+can make this change more permanently::
+
+ # content of pytest.ini
+ [pytest]
+ addopts = --pyargs
+
+Now a simple invocation of ``pytest NAME`` will check
+if NAME exists as an importable package/module and otherwise
+treat it as a filesystem path.
+
+Finding out what is collected
+-----------------------------------------------
+
+You can always peek at the collection tree without running tests like this::
+
+ . $ pytest --collect-only pythoncollection.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
+ collected 3 items
+ <Module 'CWD/pythoncollection.py'>
+ <Function 'test_function'>
+ <Class 'TestClass'>
+ <Instance '()'>
+ <Function 'test_method'>
+ <Function 'test_anothermethod'>
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+.. _customizing-test-collection:
+
+Customizing test collection
+---------------------------
+
+.. regendoc:wipe
+
+You can easily instruct ``pytest`` to discover tests from every Python file::
+
+ # content of pytest.ini
+ [pytest]
+ python_files = *.py
+
+However, many projects will have a ``setup.py`` which they don't want to be
+imported. Moreover, there may files only importable by a specific python
+version. For such cases you can dynamically define files to be ignored by
+listing them in a ``conftest.py`` file::
+
+ # content of conftest.py
+ import sys
+
+ collect_ignore = ["setup.py"]
+ if sys.version_info[0] > 2:
+ collect_ignore.append("pkg/module_py2.py")
+
+and then if you have a module file like this::
+
+ # content of pkg/module_py2.py
+ def test_only_on_python2():
+ try:
+ assert 0
+ except Exception, e:
+ pass
+
+and a ``setup.py`` dummy file like this::
+
+ # content of setup.py
+ 0/0 # will raise exception if imported
+
+If you run with a Python 2 interpreter then you will find the one test and will
+leave out the ``setup.py`` file::
+
+ #$ pytest --collect-only
+ ====== test session starts ======
+ platform linux2 -- Python 2.7.10, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
+ rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
+ collected 1 items
+ <Module 'pkg/module_py2.py'>
+ <Function 'test_only_on_python2'>
+
+ ====== no tests ran in 0.04 seconds ======
+
+If you run with a Python 3 interpreter both the one test and the ``setup.py``
+file will be left out::
+
+ $ pytest --collect-only
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
+ collected 0 items
+
+ ======================= no tests ran in 0.12 seconds =======================
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/reportingdemo.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/reportingdemo.rst
new file mode 100644
index 00000000000..9edc02b3cd4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/reportingdemo.rst
@@ -0,0 +1,604 @@
+
+.. _`tbreportdemo`:
+
+Demo of Python failure reports with pytest
+==================================================
+
+Here is a nice run of several tens of failures
+and how ``pytest`` presents things (unfortunately
+not showing the nice colors here in the HTML that you
+get on the terminal - we are working on that)::
+
+ assertion $ pytest failure_demo.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR/assertion, inifile:
+ collected 42 items
+
+ failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF [100%]
+
+ ================================= FAILURES =================================
+ ____________________________ test_generative[0] ____________________________
+
+ param1 = 3, param2 = 6
+
+ def test_generative(param1, param2):
+ > assert param1 * 2 < param2
+ E assert (3 * 2) < 6
+
+ failure_demo.py:16: AssertionError
+ _________________________ TestFailing.test_simple __________________________
+
+ self = <failure_demo.TestFailing object at 0xdeadbeef>
+
+ def test_simple(self):
+ def f():
+ return 42
+ def g():
+ return 43
+
+ > assert f() == g()
+ E assert 42 == 43
+ E + where 42 = <function TestFailing.test_simple.<locals>.f at 0xdeadbeef>()
+ E + and 43 = <function TestFailing.test_simple.<locals>.g at 0xdeadbeef>()
+
+ failure_demo.py:29: AssertionError
+ ____________________ TestFailing.test_simple_multiline _____________________
+
+ self = <failure_demo.TestFailing object at 0xdeadbeef>
+
+ def test_simple_multiline(self):
+ otherfunc_multi(
+ 42,
+ > 6*9)
+
+ failure_demo.py:34:
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+ a = 42, b = 54
+
+ def otherfunc_multi(a,b):
+ > assert (a ==
+ b)
+ E assert 42 == 54
+
+ failure_demo.py:12: AssertionError
+ ___________________________ TestFailing.test_not ___________________________
+
+ self = <failure_demo.TestFailing object at 0xdeadbeef>
+
+ def test_not(self):
+ def f():
+ return 42
+ > assert not f()
+ E assert not 42
+ E + where 42 = <function TestFailing.test_not.<locals>.f at 0xdeadbeef>()
+
+ failure_demo.py:39: AssertionError
+ _________________ TestSpecialisedExplanations.test_eq_text _________________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_eq_text(self):
+ > assert 'spam' == 'eggs'
+ E AssertionError: assert 'spam' == 'eggs'
+ E - spam
+ E + eggs
+
+ failure_demo.py:43: AssertionError
+ _____________ TestSpecialisedExplanations.test_eq_similar_text _____________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_eq_similar_text(self):
+ > assert 'foo 1 bar' == 'foo 2 bar'
+ E AssertionError: assert 'foo 1 bar' == 'foo 2 bar'
+ E - foo 1 bar
+ E ? ^
+ E + foo 2 bar
+ E ? ^
+
+ failure_demo.py:46: AssertionError
+ ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_eq_multiline_text(self):
+ > assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
+ E AssertionError: assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
+ E foo
+ E - spam
+ E + eggs
+ E bar
+
+ failure_demo.py:49: AssertionError
+ ______________ TestSpecialisedExplanations.test_eq_long_text _______________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_eq_long_text(self):
+ a = '1'*100 + 'a' + '2'*100
+ b = '1'*100 + 'b' + '2'*100
+ > assert a == b
+ E AssertionError: assert '111111111111...2222222222222' == '1111111111111...2222222222222'
+ E Skipping 90 identical leading characters in diff, use -v to show
+ E Skipping 91 identical trailing characters in diff, use -v to show
+ E - 1111111111a222222222
+ E ? ^
+ E + 1111111111b222222222
+ E ? ^
+
+ failure_demo.py:54: AssertionError
+ _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_eq_long_text_multiline(self):
+ a = '1\n'*100 + 'a' + '2\n'*100
+ b = '1\n'*100 + 'b' + '2\n'*100
+ > assert a == b
+ E AssertionError: assert '1\n1\n1\n1\n...n2\n2\n2\n2\n' == '1\n1\n1\n1\n1...n2\n2\n2\n2\n'
+ E Skipping 190 identical leading characters in diff, use -v to show
+ E Skipping 191 identical trailing characters in diff, use -v to show
+ E 1
+ E 1
+ E 1
+ E 1
+ E 1...
+ E
+ E ...Full output truncated (7 lines hidden), use '-vv' to show
+
+ failure_demo.py:59: AssertionError
+ _________________ TestSpecialisedExplanations.test_eq_list _________________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_eq_list(self):
+ > assert [0, 1, 2] == [0, 1, 3]
+ E assert [0, 1, 2] == [0, 1, 3]
+ E At index 2 diff: 2 != 3
+ E Use -v to get the full diff
+
+ failure_demo.py:62: AssertionError
+ ______________ TestSpecialisedExplanations.test_eq_list_long _______________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_eq_list_long(self):
+ a = [0]*100 + [1] + [3]*100
+ b = [0]*100 + [2] + [3]*100
+ > assert a == b
+ E assert [0, 0, 0, 0, 0, 0, ...] == [0, 0, 0, 0, 0, 0, ...]
+ E At index 100 diff: 1 != 2
+ E Use -v to get the full diff
+
+ failure_demo.py:67: AssertionError
+ _________________ TestSpecialisedExplanations.test_eq_dict _________________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_eq_dict(self):
+ > assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
+ E AssertionError: assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
+ E Omitting 1 identical items, use -vv to show
+ E Differing items:
+ E {'b': 1} != {'b': 2}
+ E Left contains more items:
+ E {'c': 0}
+ E Right contains more items:
+ E {'d': 0}...
+ E
+ E ...Full output truncated (2 lines hidden), use '-vv' to show
+
+ failure_demo.py:70: AssertionError
+ _________________ TestSpecialisedExplanations.test_eq_set __________________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_eq_set(self):
+ > assert set([0, 10, 11, 12]) == set([0, 20, 21])
+ E AssertionError: assert {0, 10, 11, 12} == {0, 20, 21}
+ E Extra items in the left set:
+ E 10
+ E 11
+ E 12
+ E Extra items in the right set:
+ E 20
+ E 21...
+ E
+ E ...Full output truncated (2 lines hidden), use '-vv' to show
+
+ failure_demo.py:73: AssertionError
+ _____________ TestSpecialisedExplanations.test_eq_longer_list ______________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_eq_longer_list(self):
+ > assert [1,2] == [1,2,3]
+ E assert [1, 2] == [1, 2, 3]
+ E Right contains more items, first extra item: 3
+ E Use -v to get the full diff
+
+ failure_demo.py:76: AssertionError
+ _________________ TestSpecialisedExplanations.test_in_list _________________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_in_list(self):
+ > assert 1 in [0, 2, 3, 4, 5]
+ E assert 1 in [0, 2, 3, 4, 5]
+
+ failure_demo.py:79: AssertionError
+ __________ TestSpecialisedExplanations.test_not_in_text_multiline __________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_not_in_text_multiline(self):
+ text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail'
+ > assert 'foo' not in text
+ E AssertionError: assert 'foo' not in 'some multiline\ntext\nw...ncludes foo\nand a\ntail'
+ E 'foo' is contained here:
+ E some multiline
+ E text
+ E which
+ E includes foo
+ E ? +++
+ E and a...
+ E
+ E ...Full output truncated (2 lines hidden), use '-vv' to show
+
+ failure_demo.py:83: AssertionError
+ ___________ TestSpecialisedExplanations.test_not_in_text_single ____________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_not_in_text_single(self):
+ text = 'single foo line'
+ > assert 'foo' not in text
+ E AssertionError: assert 'foo' not in 'single foo line'
+ E 'foo' is contained here:
+ E single foo line
+ E ? +++
+
+ failure_demo.py:87: AssertionError
+ _________ TestSpecialisedExplanations.test_not_in_text_single_long _________
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_not_in_text_single_long(self):
+ text = 'head ' * 50 + 'foo ' + 'tail ' * 20
+ > assert 'foo' not in text
+ E AssertionError: assert 'foo' not in 'head head head head hea...ail tail tail tail tail '
+ E 'foo' is contained here:
+ E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
+ E ? +++
+
+ failure_demo.py:91: AssertionError
+ ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
+
+ self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
+
+ def test_not_in_text_single_long_term(self):
+ text = 'head ' * 50 + 'f'*70 + 'tail ' * 20
+ > assert 'f'*70 not in text
+ E AssertionError: assert 'fffffffffff...ffffffffffff' not in 'head head he...l tail tail '
+ E 'ffffffffffffffffff...fffffffffffffffffff' is contained here:
+ E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
+ E ? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ failure_demo.py:95: AssertionError
+ ______________________________ test_attribute ______________________________
+
+ def test_attribute():
+ class Foo(object):
+ b = 1
+ i = Foo()
+ > assert i.b == 2
+ E assert 1 == 2
+ E + where 1 = <failure_demo.test_attribute.<locals>.Foo object at 0xdeadbeef>.b
+
+ failure_demo.py:102: AssertionError
+ _________________________ test_attribute_instance __________________________
+
+ def test_attribute_instance():
+ class Foo(object):
+ b = 1
+ > assert Foo().b == 2
+ E AssertionError: assert 1 == 2
+ E + where 1 = <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef>.b
+ E + where <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef> = <class 'failure_demo.test_attribute_instance.<locals>.Foo'>()
+
+ failure_demo.py:108: AssertionError
+ __________________________ test_attribute_failure __________________________
+
+ def test_attribute_failure():
+ class Foo(object):
+ def _get_b(self):
+ raise Exception('Failed to get attrib')
+ b = property(_get_b)
+ i = Foo()
+ > assert i.b == 2
+
+ failure_demo.py:117:
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+ self = <failure_demo.test_attribute_failure.<locals>.Foo object at 0xdeadbeef>
+
+ def _get_b(self):
+ > raise Exception('Failed to get attrib')
+ E Exception: Failed to get attrib
+
+ failure_demo.py:114: Exception
+ _________________________ test_attribute_multiple __________________________
+
+ def test_attribute_multiple():
+ class Foo(object):
+ b = 1
+ class Bar(object):
+ b = 2
+ > assert Foo().b == Bar().b
+ E AssertionError: assert 1 == 2
+ E + where 1 = <failure_demo.test_attribute_multiple.<locals>.Foo object at 0xdeadbeef>.b
+ E + where <failure_demo.test_attribute_multiple.<locals>.Foo object at 0xdeadbeef> = <class 'failure_demo.test_attribute_multiple.<locals>.Foo'>()
+ E + and 2 = <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef>.b
+ E + where <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef> = <class 'failure_demo.test_attribute_multiple.<locals>.Bar'>()
+
+ failure_demo.py:125: AssertionError
+ __________________________ TestRaises.test_raises __________________________
+
+ self = <failure_demo.TestRaises object at 0xdeadbeef>
+
+ def test_raises(self):
+ s = 'qwe'
+ > raises(TypeError, "int(s)")
+
+ failure_demo.py:134:
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+ > int(s)
+ E ValueError: invalid literal for int() with base 10: 'qwe'
+
+ <0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python_api.py:580>:1: ValueError
+ ______________________ TestRaises.test_raises_doesnt _______________________
+
+ self = <failure_demo.TestRaises object at 0xdeadbeef>
+
+ def test_raises_doesnt(self):
+ > raises(IOError, "int('3')")
+ E Failed: DID NOT RAISE <class 'OSError'>
+
+ failure_demo.py:137: Failed
+ __________________________ TestRaises.test_raise ___________________________
+
+ self = <failure_demo.TestRaises object at 0xdeadbeef>
+
+ def test_raise(self):
+ > raise ValueError("demo error")
+ E ValueError: demo error
+
+ failure_demo.py:140: ValueError
+ ________________________ TestRaises.test_tupleerror ________________________
+
+ self = <failure_demo.TestRaises object at 0xdeadbeef>
+
+ def test_tupleerror(self):
+ > a,b = [1]
+ E ValueError: not enough values to unpack (expected 2, got 1)
+
+ failure_demo.py:143: ValueError
+ ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______
+
+ self = <failure_demo.TestRaises object at 0xdeadbeef>
+
+ def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
+ l = [1,2,3]
+ print ("l is %r" % l)
+ > a,b = l.pop()
+ E TypeError: 'int' object is not iterable
+
+ failure_demo.py:148: TypeError
+ --------------------------- Captured stdout call ---------------------------
+ l is [1, 2, 3]
+ ________________________ TestRaises.test_some_error ________________________
+
+ self = <failure_demo.TestRaises object at 0xdeadbeef>
+
+ def test_some_error(self):
+ > if namenotexi:
+ E NameError: name 'namenotexi' is not defined
+
+ failure_demo.py:151: NameError
+ ____________________ test_dynamic_compile_shows_nicely _____________________
+
+ def test_dynamic_compile_shows_nicely():
+ src = 'def foo():\n assert 1 == 0\n'
+ name = 'abc-123'
+ module = py.std.imp.new_module(name)
+ code = _pytest._code.compile(src, name, 'exec')
+ py.builtin.exec_(code, module.__dict__)
+ py.std.sys.modules[name] = module
+ > module.foo()
+
+ failure_demo.py:166:
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+ def foo():
+ > assert 1 == 0
+ E AssertionError
+
+ <2-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:163>:2: AssertionError
+ ____________________ TestMoreErrors.test_complex_error _____________________
+
+ self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
+
+ def test_complex_error(self):
+ def f():
+ return 44
+ def g():
+ return 43
+ > somefunc(f(), g())
+
+ failure_demo.py:176:
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ failure_demo.py:9: in somefunc
+ otherfunc(x,y)
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+ a = 44, b = 43
+
+ def otherfunc(a,b):
+ > assert a==b
+ E assert 44 == 43
+
+ failure_demo.py:6: AssertionError
+ ___________________ TestMoreErrors.test_z1_unpack_error ____________________
+
+ self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
+
+ def test_z1_unpack_error(self):
+ l = []
+ > a,b = l
+ E ValueError: not enough values to unpack (expected 2, got 0)
+
+ failure_demo.py:180: ValueError
+ ____________________ TestMoreErrors.test_z2_type_error _____________________
+
+ self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
+
+ def test_z2_type_error(self):
+ l = 3
+ > a,b = l
+ E TypeError: 'int' object is not iterable
+
+ failure_demo.py:184: TypeError
+ ______________________ TestMoreErrors.test_startswith ______________________
+
+ self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
+
+ def test_startswith(self):
+ s = "123"
+ g = "456"
+ > assert s.startswith(g)
+ E AssertionError: assert False
+ E + where False = <built-in method startswith of str object at 0xdeadbeef>('456')
+ E + where <built-in method startswith of str object at 0xdeadbeef> = '123'.startswith
+
+ failure_demo.py:189: AssertionError
+ __________________ TestMoreErrors.test_startswith_nested ___________________
+
+ self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
+
+ def test_startswith_nested(self):
+ def f():
+ return "123"
+ def g():
+ return "456"
+ > assert f().startswith(g())
+ E AssertionError: assert False
+ E + where False = <built-in method startswith of str object at 0xdeadbeef>('456')
+ E + where <built-in method startswith of str object at 0xdeadbeef> = '123'.startswith
+ E + where '123' = <function TestMoreErrors.test_startswith_nested.<locals>.f at 0xdeadbeef>()
+ E + and '456' = <function TestMoreErrors.test_startswith_nested.<locals>.g at 0xdeadbeef>()
+
+ failure_demo.py:196: AssertionError
+ _____________________ TestMoreErrors.test_global_func ______________________
+
+ self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
+
+ def test_global_func(self):
+ > assert isinstance(globf(42), float)
+ E assert False
+ E + where False = isinstance(43, float)
+ E + where 43 = globf(42)
+
+ failure_demo.py:199: AssertionError
+ _______________________ TestMoreErrors.test_instance _______________________
+
+ self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
+
+ def test_instance(self):
+ self.x = 6*7
+ > assert self.x != 42
+ E assert 42 != 42
+ E + where 42 = <failure_demo.TestMoreErrors object at 0xdeadbeef>.x
+
+ failure_demo.py:203: AssertionError
+ _______________________ TestMoreErrors.test_compare ________________________
+
+ self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
+
+ def test_compare(self):
+ > assert globf(10) < 5
+ E assert 11 < 5
+ E + where 11 = globf(10)
+
+ failure_demo.py:206: AssertionError
+ _____________________ TestMoreErrors.test_try_finally ______________________
+
+ self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
+
+ def test_try_finally(self):
+ x = 1
+ try:
+ > assert x == 0
+ E assert 1 == 0
+
+ failure_demo.py:211: AssertionError
+ ___________________ TestCustomAssertMsg.test_single_line ___________________
+
+ self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
+
+ def test_single_line(self):
+ class A(object):
+ a = 1
+ b = 2
+ > assert A.a == b, "A.a appears not to be b"
+ E AssertionError: A.a appears not to be b
+ E assert 1 == 2
+ E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_single_line.<locals>.A'>.a
+
+ failure_demo.py:222: AssertionError
+ ____________________ TestCustomAssertMsg.test_multiline ____________________
+
+ self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
+
+ def test_multiline(self):
+ class A(object):
+ a = 1
+ b = 2
+ > assert A.a == b, "A.a appears not to be b\n" \
+ "or does not appear to be b\none of those"
+ E AssertionError: A.a appears not to be b
+ E or does not appear to be b
+ E one of those
+ E assert 1 == 2
+ E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_multiline.<locals>.A'>.a
+
+ failure_demo.py:228: AssertionError
+ ___________________ TestCustomAssertMsg.test_custom_repr ___________________
+
+ self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
+
+ def test_custom_repr(self):
+ class JSON(object):
+ a = 1
+ def __repr__(self):
+ return "This is JSON\n{\n 'foo': 'bar'\n}"
+ a = JSON()
+ b = 2
+ > assert a.a == b, a
+ E AssertionError: This is JSON
+ E {
+ E 'foo': 'bar'
+ E }
+ E assert 1 == 2
+ E + where 1 = This is JSON\n{\n 'foo': 'bar'\n}.a
+
+ failure_demo.py:238: AssertionError
+ ============================= warnings summary =============================
+ None
+ Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0.
+ Please use Metafunc.parametrize instead.
+
+ -- Docs: http://doc.pytest.org/en/latest/warnings.html
+ ================== 42 failed, 1 warnings in 0.12 seconds ===================
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/simple.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/simple.rst
new file mode 100644
index 00000000000..678a0db0094
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/simple.rst
@@ -0,0 +1,847 @@
+
+
+Basic patterns and examples
+==========================================================
+
+Pass different values to a test function, depending on command line options
+----------------------------------------------------------------------------
+
+.. regendoc:wipe
+
+Suppose we want to write a test that depends on a command line option.
+Here is a basic pattern to achieve this:
+
+.. code-block:: python
+
+ # content of test_sample.py
+ def test_answer(cmdopt):
+ if cmdopt == "type1":
+ print ("first")
+ elif cmdopt == "type2":
+ print ("second")
+ assert 0 # to see what was printed
+
+
+For this to work we need to add a command line option and
+provide the ``cmdopt`` through a :ref:`fixture function <fixture function>`:
+
+.. code-block:: python
+
+ # content of conftest.py
+ import pytest
+
+ def pytest_addoption(parser):
+ parser.addoption("--cmdopt", action="store", default="type1",
+ help="my option: type1 or type2")
+
+ @pytest.fixture
+ def cmdopt(request):
+ return request.config.getoption("--cmdopt")
+
+Let's run this without supplying our new option::
+
+ $ pytest -q test_sample.py
+ F [100%]
+ ================================= FAILURES =================================
+ _______________________________ test_answer ________________________________
+
+ cmdopt = 'type1'
+
+ def test_answer(cmdopt):
+ if cmdopt == "type1":
+ print ("first")
+ elif cmdopt == "type2":
+ print ("second")
+ > assert 0 # to see what was printed
+ E assert 0
+
+ test_sample.py:6: AssertionError
+ --------------------------- Captured stdout call ---------------------------
+ first
+ 1 failed in 0.12 seconds
+
+And now with supplying a command line option::
+
+ $ pytest -q --cmdopt=type2
+ F [100%]
+ ================================= FAILURES =================================
+ _______________________________ test_answer ________________________________
+
+ cmdopt = 'type2'
+
+ def test_answer(cmdopt):
+ if cmdopt == "type1":
+ print ("first")
+ elif cmdopt == "type2":
+ print ("second")
+ > assert 0 # to see what was printed
+ E assert 0
+
+ test_sample.py:6: AssertionError
+ --------------------------- Captured stdout call ---------------------------
+ second
+ 1 failed in 0.12 seconds
+
+You can see that the command line option arrived in our test. This
+completes the basic pattern. However, one often rather wants to process
+command line options outside of the test and rather pass in different or
+more complex objects.
+
+Dynamically adding command line options
+--------------------------------------------------------------
+
+.. regendoc:wipe
+
+Through :confval:`addopts` you can statically add command line
+options for your project. You can also dynamically modify
+the command line arguments before they get processed:
+
+.. code-block:: python
+
+ # content of conftest.py
+ import sys
+ def pytest_cmdline_preparse(args):
+ if 'xdist' in sys.modules: # pytest-xdist plugin
+ import multiprocessing
+ num = max(multiprocessing.cpu_count() / 2, 1)
+ args[:] = ["-n", str(num)] + args
+
+If you have the `xdist plugin <https://pypi.python.org/pypi/pytest-xdist>`_ installed
+you will now always perform test runs using a number
+of subprocesses close to your CPU. Running in an empty
+directory with the above conftest.py::
+
+ $ pytest
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 0 items
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+.. _`excontrolskip`:
+
+Control skipping of tests according to command line option
+--------------------------------------------------------------
+
+.. regendoc:wipe
+
+Here is a ``conftest.py`` file adding a ``--runslow`` command
+line option to control skipping of ``pytest.mark.slow`` marked tests:
+
+.. code-block:: python
+
+ # content of conftest.py
+
+ import pytest
+ def pytest_addoption(parser):
+ parser.addoption("--runslow", action="store_true",
+ default=False, help="run slow tests")
+
+ def pytest_collection_modifyitems(config, items):
+ if config.getoption("--runslow"):
+ # --runslow given in cli: do not skip slow tests
+ return
+ skip_slow = pytest.mark.skip(reason="need --runslow option to run")
+ for item in items:
+ if "slow" in item.keywords:
+ item.add_marker(skip_slow)
+
+We can now write a test module like this:
+
+.. code-block:: python
+
+ # content of test_module.py
+ import pytest
+
+
+ def test_func_fast():
+ pass
+
+
+ @pytest.mark.slow
+ def test_func_slow():
+ pass
+
+and when running it will see a skipped "slow" test::
+
+ $ pytest -rs # "-rs" means report details on the little 's'
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 2 items
+
+ test_module.py .s [100%]
+ ========================= short test summary info ==========================
+ SKIP [1] test_module.py:8: need --runslow option to run
+
+ =================== 1 passed, 1 skipped in 0.12 seconds ====================
+
+Or run it including the ``slow`` marked test::
+
+ $ pytest --runslow
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 2 items
+
+ test_module.py .. [100%]
+
+ ========================= 2 passed in 0.12 seconds =========================
+
+Writing well integrated assertion helpers
+--------------------------------------------------
+
+.. regendoc:wipe
+
+If you have a test helper function called from a test you can
+use the ``pytest.fail`` marker to fail a test with a certain message.
+The test support function will not show up in the traceback if you
+set the ``__tracebackhide__`` option somewhere in the helper function.
+Example:
+
+.. code-block:: python
+
+ # content of test_checkconfig.py
+ import pytest
+ def checkconfig(x):
+ __tracebackhide__ = True
+ if not hasattr(x, "config"):
+ pytest.fail("not configured: %s" %(x,))
+
+ def test_something():
+ checkconfig(42)
+
+The ``__tracebackhide__`` setting influences ``pytest`` showing
+of tracebacks: the ``checkconfig`` function will not be shown
+unless the ``--full-trace`` command line option is specified.
+Let's run our little function::
+
+ $ pytest -q test_checkconfig.py
+ F [100%]
+ ================================= FAILURES =================================
+ ______________________________ test_something ______________________________
+
+ def test_something():
+ > checkconfig(42)
+ E Failed: not configured: 42
+
+ test_checkconfig.py:8: Failed
+ 1 failed in 0.12 seconds
+
+If you only want to hide certain exceptions, you can set ``__tracebackhide__``
+to a callable which gets the ``ExceptionInfo`` object. You can for example use
+this to make sure unexpected exception types aren't hidden:
+
+.. code-block:: python
+
+ import operator
+ import pytest
+
+ class ConfigException(Exception):
+ pass
+
+ def checkconfig(x):
+ __tracebackhide__ = operator.methodcaller('errisinstance', ConfigException)
+ if not hasattr(x, "config"):
+ raise ConfigException("not configured: %s" %(x,))
+
+ def test_something():
+ checkconfig(42)
+
+This will avoid hiding the exception traceback on unrelated exceptions (i.e.
+bugs in assertion helpers).
+
+
+Detect if running from within a pytest run
+--------------------------------------------------------------
+
+.. regendoc:wipe
+
+Usually it is a bad idea to make application code
+behave differently if called from a test. But if you
+absolutely must find out if your application code is
+running from a test you can do something like this:
+
+.. code-block:: python
+
+ # content of conftest.py
+
+ def pytest_configure(config):
+ import sys
+ sys._called_from_test = True
+
+ def pytest_unconfigure(config):
+ import sys
+ del sys._called_from_test
+
+and then check for the ``sys._called_from_test`` flag:
+
+.. code-block:: python
+
+ if hasattr(sys, '_called_from_test'):
+ # called from within a test run
+ else:
+ # called "normally"
+
+accordingly in your application. It's also a good idea
+to use your own application module rather than ``sys``
+for handling flag.
+
+Adding info to test report header
+--------------------------------------------------------------
+
+.. regendoc:wipe
+
+It's easy to present extra information in a ``pytest`` run:
+
+.. code-block:: python
+
+ # content of conftest.py
+
+ def pytest_report_header(config):
+ return "project deps: mylib-1.1"
+
+which will add the string to the test header accordingly::
+
+ $ pytest
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ project deps: mylib-1.1
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 0 items
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+.. regendoc:wipe
+
+It is also possible to return a list of strings which will be considered as several
+lines of information. You may consider ``config.getoption('verbose')`` in order to
+display more information if applicable:
+
+.. code-block:: python
+
+ # content of conftest.py
+
+ def pytest_report_header(config):
+ if config.getoption('verbose') > 0:
+ return ["info1: did you know that ...", "did you?"]
+
+which will add info only when run with "--v"::
+
+ $ pytest -v
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ info1: did you know that ...
+ did you?
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collecting ... collected 0 items
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+and nothing when run plainly::
+
+ $ pytest
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 0 items
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+profiling test duration
+--------------------------
+
+.. regendoc:wipe
+
+.. versionadded: 2.2
+
+If you have a slow running large test suite you might want to find
+out which tests are the slowest. Let's make an artificial test suite:
+
+.. code-block:: python
+
+ # content of test_some_are_slow.py
+ import time
+
+ def test_funcfast():
+ time.sleep(0.1)
+
+ def test_funcslow1():
+ time.sleep(0.2)
+
+ def test_funcslow2():
+ time.sleep(0.3)
+
+Now we can profile which test functions execute the slowest::
+
+ $ pytest --durations=3
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 3 items
+
+ test_some_are_slow.py ... [100%]
+
+ ========================= slowest 3 test durations =========================
+ 0.30s call test_some_are_slow.py::test_funcslow2
+ 0.20s call test_some_are_slow.py::test_funcslow1
+ 0.10s call test_some_are_slow.py::test_funcfast
+ ========================= 3 passed in 0.12 seconds =========================
+
+incremental testing - test steps
+---------------------------------------------------
+
+.. regendoc:wipe
+
+Sometimes you may have a testing situation which consists of a series
+of test steps. If one step fails it makes no sense to execute further
+steps as they are all expected to fail anyway and their tracebacks
+add no insight. Here is a simple ``conftest.py`` file which introduces
+an ``incremental`` marker which is to be used on classes:
+
+.. code-block:: python
+
+ # content of conftest.py
+
+ import pytest
+
+ def pytest_runtest_makereport(item, call):
+ if "incremental" in item.keywords:
+ if call.excinfo is not None:
+ parent = item.parent
+ parent._previousfailed = item
+
+ def pytest_runtest_setup(item):
+ if "incremental" in item.keywords:
+ previousfailed = getattr(item.parent, "_previousfailed", None)
+ if previousfailed is not None:
+ pytest.xfail("previous test failed (%s)" %previousfailed.name)
+
+These two hook implementations work together to abort incremental-marked
+tests in a class. Here is a test module example:
+
+.. code-block:: python
+
+ # content of test_step.py
+
+ import pytest
+
+ @pytest.mark.incremental
+ class TestUserHandling(object):
+ def test_login(self):
+ pass
+ def test_modification(self):
+ assert 0
+ def test_deletion(self):
+ pass
+
+ def test_normal():
+ pass
+
+If we run this::
+
+ $ pytest -rx
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 4 items
+
+ test_step.py .Fx. [100%]
+ ========================= short test summary info ==========================
+ XFAIL test_step.py::TestUserHandling::()::test_deletion
+ reason: previous test failed (test_modification)
+
+ ================================= FAILURES =================================
+ ____________________ TestUserHandling.test_modification ____________________
+
+ self = <test_step.TestUserHandling object at 0xdeadbeef>
+
+ def test_modification(self):
+ > assert 0
+ E assert 0
+
+ test_step.py:9: AssertionError
+ ============== 1 failed, 2 passed, 1 xfailed in 0.12 seconds ===============
+
+We'll see that ``test_deletion`` was not executed because ``test_modification``
+failed. It is reported as an "expected failure".
+
+
+Package/Directory-level fixtures (setups)
+-------------------------------------------------------
+
+If you have nested test directories, you can have per-directory fixture scopes
+by placing fixture functions in a ``conftest.py`` file in that directory
+You can use all types of fixtures including :ref:`autouse fixtures
+<autouse fixtures>` which are the equivalent of xUnit's setup/teardown
+concept. It's however recommended to have explicit fixture references in your
+tests or test classes rather than relying on implicitly executing
+setup/teardown functions, especially if they are far away from the actual tests.
+
+Here is an example for making a ``db`` fixture available in a directory:
+
+.. code-block:: python
+
+ # content of a/conftest.py
+ import pytest
+
+ class DB(object):
+ pass
+
+ @pytest.fixture(scope="session")
+ def db():
+ return DB()
+
+and then a test module in that directory:
+
+.. code-block:: python
+
+ # content of a/test_db.py
+ def test_a1(db):
+ assert 0, db # to show value
+
+another test module:
+
+.. code-block:: python
+
+ # content of a/test_db2.py
+ def test_a2(db):
+ assert 0, db # to show value
+
+and then a module in a sister directory which will not see
+the ``db`` fixture:
+
+.. code-block:: python
+
+ # content of b/test_error.py
+ def test_root(db): # no db here, will error out
+ pass
+
+We can run this::
+
+ $ pytest
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 7 items
+
+ test_step.py .Fx. [ 57%]
+ a/test_db.py F [ 71%]
+ a/test_db2.py F [ 85%]
+ b/test_error.py E [100%]
+
+ ================================== ERRORS ==================================
+ _______________________ ERROR at setup of test_root ________________________
+ file $REGENDOC_TMPDIR/b/test_error.py, line 1
+ def test_root(db): # no db here, will error out
+ E fixture 'db' not found
+ > available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_xml_property, recwarn, tmpdir, tmpdir_factory
+ > use 'pytest --fixtures [testpath]' for help on them.
+
+ $REGENDOC_TMPDIR/b/test_error.py:1
+ ================================= FAILURES =================================
+ ____________________ TestUserHandling.test_modification ____________________
+
+ self = <test_step.TestUserHandling object at 0xdeadbeef>
+
+ def test_modification(self):
+ > assert 0
+ E assert 0
+
+ test_step.py:9: AssertionError
+ _________________________________ test_a1 __________________________________
+
+ db = <conftest.DB object at 0xdeadbeef>
+
+ def test_a1(db):
+ > assert 0, db # to show value
+ E AssertionError: <conftest.DB object at 0xdeadbeef>
+ E assert 0
+
+ a/test_db.py:2: AssertionError
+ _________________________________ test_a2 __________________________________
+
+ db = <conftest.DB object at 0xdeadbeef>
+
+ def test_a2(db):
+ > assert 0, db # to show value
+ E AssertionError: <conftest.DB object at 0xdeadbeef>
+ E assert 0
+
+ a/test_db2.py:2: AssertionError
+ ========== 3 failed, 2 passed, 1 xfailed, 1 error in 0.12 seconds ==========
+
+The two test modules in the ``a`` directory see the same ``db`` fixture instance
+while the one test in the sister-directory ``b`` doesn't see it. We could of course
+also define a ``db`` fixture in that sister directory's ``conftest.py`` file.
+Note that each fixture is only instantiated if there is a test actually needing
+it (unless you use "autouse" fixture which are always executed ahead of the first test
+executing).
+
+
+post-process test reports / failures
+---------------------------------------
+
+If you want to postprocess test reports and need access to the executing
+environment you can implement a hook that gets called when the test
+"report" object is about to be created. Here we write out all failing
+test calls and also access a fixture (if it was used by the test) in
+case you want to query/look at it during your post processing. In our
+case we just write some information out to a ``failures`` file:
+
+.. code-block:: python
+
+ # content of conftest.py
+
+ import pytest
+ import os.path
+
+ @pytest.hookimpl(tryfirst=True, hookwrapper=True)
+ def pytest_runtest_makereport(item, call):
+ # execute all other hooks to obtain the report object
+ outcome = yield
+ rep = outcome.get_result()
+
+ # we only look at actual failing test calls, not setup/teardown
+ if rep.when == "call" and rep.failed:
+ mode = "a" if os.path.exists("failures") else "w"
+ with open("failures", mode) as f:
+ # let's also access a fixture for the fun of it
+ if "tmpdir" in item.fixturenames:
+ extra = " (%s)" % item.funcargs["tmpdir"]
+ else:
+ extra = ""
+
+ f.write(rep.nodeid + extra + "\n")
+
+
+if you then have failing tests:
+
+.. code-block:: python
+
+ # content of test_module.py
+ def test_fail1(tmpdir):
+ assert 0
+ def test_fail2():
+ assert 0
+
+and run them::
+
+ $ pytest test_module.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 2 items
+
+ test_module.py FF [100%]
+
+ ================================= FAILURES =================================
+ ________________________________ test_fail1 ________________________________
+
+ tmpdir = local('PYTEST_TMPDIR/test_fail10')
+
+ def test_fail1(tmpdir):
+ > assert 0
+ E assert 0
+
+ test_module.py:2: AssertionError
+ ________________________________ test_fail2 ________________________________
+
+ def test_fail2():
+ > assert 0
+ E assert 0
+
+ test_module.py:4: AssertionError
+ ========================= 2 failed in 0.12 seconds =========================
+
+you will have a "failures" file which contains the failing test ids::
+
+ $ cat failures
+ test_module.py::test_fail1 (PYTEST_TMPDIR/test_fail10)
+ test_module.py::test_fail2
+
+Making test result information available in fixtures
+-----------------------------------------------------------
+
+.. regendoc:wipe
+
+If you want to make test result reports available in fixture finalizers
+here is a little example implemented via a local plugin:
+
+.. code-block:: python
+
+ # content of conftest.py
+
+ import pytest
+
+ @pytest.hookimpl(tryfirst=True, hookwrapper=True)
+ def pytest_runtest_makereport(item, call):
+ # execute all other hooks to obtain the report object
+ outcome = yield
+ rep = outcome.get_result()
+
+ # set a report attribute for each phase of a call, which can
+ # be "setup", "call", "teardown"
+
+ setattr(item, "rep_" + rep.when, rep)
+
+
+ @pytest.fixture
+ def something(request):
+ yield
+ # request.node is an "item" because we use the default
+ # "function" scope
+ if request.node.rep_setup.failed:
+ print ("setting up a test failed!", request.node.nodeid)
+ elif request.node.rep_setup.passed:
+ if request.node.rep_call.failed:
+ print ("executing test failed", request.node.nodeid)
+
+
+if you then have failing tests:
+
+.. code-block:: python
+
+ # content of test_module.py
+
+ import pytest
+
+ @pytest.fixture
+ def other():
+ assert 0
+
+ def test_setup_fails(something, other):
+ pass
+
+ def test_call_fails(something):
+ assert 0
+
+ def test_fail2():
+ assert 0
+
+and run it::
+
+ $ pytest -s test_module.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 3 items
+
+ test_module.py Esetting up a test failed! test_module.py::test_setup_fails
+ Fexecuting test failed test_module.py::test_call_fails
+ F [100%]
+
+ ================================== ERRORS ==================================
+ ____________________ ERROR at setup of test_setup_fails ____________________
+
+ @pytest.fixture
+ def other():
+ > assert 0
+ E assert 0
+
+ test_module.py:6: AssertionError
+ ================================= FAILURES =================================
+ _____________________________ test_call_fails ______________________________
+
+ something = None
+
+ def test_call_fails(something):
+ > assert 0
+ E assert 0
+
+ test_module.py:12: AssertionError
+ ________________________________ test_fail2 ________________________________
+
+ def test_fail2():
+ > assert 0
+ E assert 0
+
+ test_module.py:15: AssertionError
+ ==================== 2 failed, 1 error in 0.12 seconds =====================
+
+You'll see that the fixture finalizers could use the precise reporting
+information.
+
+``PYTEST_CURRENT_TEST`` environment variable
+--------------------------------------------
+
+.. versionadded:: 3.2
+
+Sometimes a test session might get stuck and there might be no easy way to figure out
+which test got stuck, for example if pytest was run in quiet mode (``-q``) or you don't have access to the console
+output. This is particularly a problem if the problem helps only sporadically, the famous "flaky" kind of tests.
+
+``pytest`` sets a ``PYTEST_CURRENT_TEST`` environment variable when running tests, which can be inspected
+by process monitoring utilities or libraries like `psutil <https://pypi.python.org/pypi/psutil>`_ to discover which
+test got stuck if necessary:
+
+.. code-block:: python
+
+ import psutil
+
+ for pid in psutil.pids():
+ environ = psutil.Process(pid).environ()
+ if 'PYTEST_CURRENT_TEST' in environ:
+ print(f'pytest process {pid} running: {environ["PYTEST_CURRENT_TEST"]}')
+
+During the test session pytest will set ``PYTEST_CURRENT_TEST`` to the current test
+:ref:`nodeid <nodeids>` and the current stage, which can be ``setup``, ``call``
+and ``teardown``.
+
+For example, when running a single test function named ``test_foo`` from ``foo_module.py``,
+``PYTEST_CURRENT_TEST`` will be set to:
+
+#. ``foo_module.py::test_foo (setup)``
+#. ``foo_module.py::test_foo (call)``
+#. ``foo_module.py::test_foo (teardown)``
+
+In that order.
+
+.. note::
+
+ The contents of ``PYTEST_CURRENT_TEST`` is meant to be human readable and the actual format
+ can be changed between releases (even bug fixes) so it shouldn't be relied on for scripting
+ or automation.
+
+Freezing pytest
+---------------
+
+If you freeze your application using a tool like
+`PyInstaller <https://pyinstaller.readthedocs.io>`_
+in order to distribute it to your end-users, it is a good idea to also package
+your test runner and run your tests using the frozen application. This way packaging
+errors such as dependencies not being included into the executable can be detected early
+while also allowing you to send test files to users so they can run them in their
+machines, which can be useful to obtain more information about a hard to reproduce bug.
+
+Fortunately recent ``PyInstaller`` releases already have a custom hook
+for pytest, but if you are using another tool to freeze executables
+such as ``cx_freeze`` or ``py2exe``, you can use ``pytest.freeze_includes()``
+to obtain the full list of internal pytest modules. How to configure the tools
+to find the internal modules varies from tool to tool, however.
+
+Instead of freezing the pytest runner as a separate executable, you can make
+your frozen program work as the pytest runner by some clever
+argument handling during program startup. This allows you to
+have a single executable, which is usually more convenient.
+
+.. code-block:: python
+
+ # contents of app_main.py
+ import sys
+
+ if len(sys.argv) > 1 and sys.argv[1] == '--pytest':
+ import pytest
+ sys.exit(pytest.main(sys.argv[2:]))
+ else:
+ # normal application execution: at this point argv can be parsed
+ # by your argument-parsing library of choice as usual
+ ...
+
+
+This allows you to execute tests using the frozen
+application with standard ``pytest`` command-line options::
+
+ ./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/special.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/special.rst
new file mode 100644
index 00000000000..4437e1cc30e
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/special.rst
@@ -0,0 +1,72 @@
+A session-fixture which can look at all collected tests
+----------------------------------------------------------------
+
+A session-scoped fixture effectively has access to all
+collected test items. Here is an example of a fixture
+function which walks all collected tests and looks
+if their test class defines a ``callme`` method and
+calls it::
+
+ # content of conftest.py
+
+ import pytest
+
+ @pytest.fixture(scope="session", autouse=True)
+ def callattr_ahead_of_alltests(request):
+ print ("callattr_ahead_of_alltests called")
+ seen = set([None])
+ session = request.node
+ for item in session.items:
+ cls = item.getparent(pytest.Class)
+ if cls not in seen:
+ if hasattr(cls.obj, "callme"):
+ cls.obj.callme()
+ seen.add(cls)
+
+test classes may now define a ``callme`` method which
+will be called ahead of running any tests::
+
+ # content of test_module.py
+
+ class TestHello(object):
+ @classmethod
+ def callme(cls):
+ print ("callme called!")
+
+ def test_method1(self):
+ print ("test_method1 called")
+
+ def test_method2(self):
+ print ("test_method1 called")
+
+ class TestOther(object):
+ @classmethod
+ def callme(cls):
+ print ("callme other called")
+ def test_other(self):
+ print ("test other")
+
+ # works with unittest as well ...
+ import unittest
+
+ class SomeTest(unittest.TestCase):
+ @classmethod
+ def callme(self):
+ print ("SomeTest callme called")
+
+ def test_unit1(self):
+ print ("test_unit1 method called")
+
+If you run this without output capturing::
+
+ $ pytest -q -s test_module.py
+ callattr_ahead_of_alltests called
+ callme called!
+ callme other called
+ SomeTest callme called
+ test_method1 called
+ .test_method1 called
+ .test other
+ .test_unit1 method called
+ . [100%]
+ 4 passed in 0.12 seconds
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/xfail_demo.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/xfail_demo.py
index 5648575e878..5648575e878 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/example/xfail_demo.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/example/xfail_demo.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/existingtestsuite.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/existingtestsuite.rst
new file mode 100644
index 00000000000..d304b30c9a6
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/existingtestsuite.rst
@@ -0,0 +1,34 @@
+.. _existingtestsuite:
+
+Using pytest with an existing test suite
+===========================================
+
+Pytest can be used with most existing test suites, but its
+behavior differs from other test runners such as :ref:`nose <noseintegration>` or
+Python's default unittest framework.
+
+Before using this section you will want to :ref:`install pytest <getstarted>`.
+
+Running an existing test suite with pytest
+---------------------------------------------
+
+Say you want to contribute to an existing repository somewhere.
+After pulling the code into your development space using some
+flavor of version control and (optionally) setting up a virtualenv
+you will want to run::
+
+ cd <repository>
+ pip install -e . # Environment dependent alternatives include
+ # 'python setup.py develop' and 'conda develop'
+
+in your project root. This will set up a symlink to your code in
+site-packages, allowing you to edit your code while your tests
+run against it as if it were installed.
+
+Setting up your project in development mode lets you avoid having to
+reinstall every time you want to run your tests, and is less brittle than
+mucking about with sys.path to point your tests at local code.
+
+Also consider using :ref:`tox <use tox>`.
+
+.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/faq.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/faq.rst
new file mode 100644
index 00000000000..27d74e1148c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/faq.rst
@@ -0,0 +1,156 @@
+Some Issues and Questions
+==================================
+
+.. note::
+
+ This FAQ is here only mostly for historic reasons. Checkout
+ `pytest Q&A at Stackoverflow <http://stackoverflow.com/search?q=pytest>`_
+ for many questions and answers related to pytest and/or use
+ :ref:`contact channels` to get help.
+
+On naming, nosetests, licensing and magic
+------------------------------------------------
+
+How does pytest relate to nose and unittest?
++++++++++++++++++++++++++++++++++++++++++++++++++
+
+``pytest`` and nose_ share basic philosophy when it comes
+to running and writing Python tests. In fact, you can run many tests
+written for nose with ``pytest``. nose_ was originally created
+as a clone of ``pytest`` when ``pytest`` was in the ``0.8`` release
+cycle. Note that starting with pytest-2.0 support for running unittest
+test suites is majorly improved.
+
+how does pytest relate to twisted's trial?
+++++++++++++++++++++++++++++++++++++++++++++++
+
+Since some time ``pytest`` has builtin support for supporting tests
+written using trial. It does not itself start a reactor, however,
+and does not handle Deferreds returned from a test in pytest style.
+If you are using trial's unittest.TestCase chances are that you can
+just run your tests even if you return Deferreds. In addition,
+there also is a dedicated `pytest-twisted
+<http://pypi.python.org/pypi/pytest-twisted>`_ plugin which allows you to
+return deferreds from pytest-style tests, allowing the use of
+:ref:`fixtures` and other features.
+
+how does pytest work with Django?
+++++++++++++++++++++++++++++++++++++++++++++++
+
+In 2012, some work is going into the `pytest-django plugin <http://pypi.python.org/pypi/pytest-django>`_. It substitutes the usage of Django's
+``manage.py test`` and allows the use of all pytest features_ most of which
+are not available from Django directly.
+
+.. _features: features.html
+
+
+What's this "magic" with pytest? (historic notes)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Around 2007 (version ``0.8``) some people thought that ``pytest``
+was using too much "magic". It had been part of the `pylib`_ which
+contains a lot of unrelated python library code. Around 2010 there
+was a major cleanup refactoring, which removed unused or deprecated code
+and resulted in the new ``pytest`` PyPI package which strictly contains
+only test-related code. This release also brought a complete pluginification
+such that the core is around 300 lines of code and everything else is
+implemented in plugins. Thus ``pytest`` today is a small, universally runnable
+and customizable testing framework for Python. Note, however, that
+``pytest`` uses metaprogramming techniques and reading its source is
+thus likely not something for Python beginners.
+
+A second "magic" issue was the assert statement debugging feature.
+Nowadays, ``pytest`` explicitly rewrites assert statements in test modules
+in order to provide more useful :ref:`assert feedback <assertfeedback>`.
+This completely avoids previous issues of confusing assertion-reporting.
+It also means, that you can use Python's ``-O`` optimization without losing
+assertions in test modules.
+
+You can also turn off all assertion interaction using the
+``--assert=plain`` option.
+
+.. _`py namespaces`: index.html
+.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/trunk/py/__init__.py
+
+
+Why can I use both ``pytest`` and ``py.test`` commands?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+pytest used to be part of the py package, which provided several developer
+utilities, all starting with ``py.<TAB>``, thus providing nice TAB-completion.
+If you install ``pip install pycmd`` you get these tools from a separate
+package. Once ``pytest`` became a separate package, the ``py.test`` name was
+retained due to avoid a naming conflict with another tool. This conflict was
+eventually resolved, and the ``pytest`` command was therefore introduced. In
+future versions of pytest, we may deprecate and later remove the ``py.test``
+command to avoid perpetuating the confusion.
+
+pytest fixtures, parametrized tests
+-------------------------------------------------------
+
+.. _funcargs: funcargs.html
+
+Is using pytest fixtures versus xUnit setup a style question?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+For simple applications and for people experienced with nose_ or
+unittest-style test setup using `xUnit style setup`_ probably
+feels natural. For larger test suites, parametrized testing
+or setup of complex test resources using fixtures_ may feel more natural.
+Moreover, fixtures are ideal for writing advanced test support
+code (like e.g. the monkeypatch_, the tmpdir_ or capture_ fixtures)
+because the support code can register setup/teardown functions
+in a managed class/module/function scope.
+
+.. _monkeypatch: monkeypatch.html
+.. _tmpdir: tmpdir.html
+.. _capture: capture.html
+.. _fixtures: fixture.html
+
+.. _`why pytest_pyfuncarg__ methods?`:
+
+.. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration
+
+Can I yield multiple values from a fixture function?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+There are two conceptual reasons why yielding from a factory function
+is not possible:
+
+* If multiple factories yielded values there would
+ be no natural place to determine the combination
+ policy - in real-world examples some combinations
+ often should not run.
+
+* Calling factories for obtaining test function arguments
+ is part of setting up and running a test. At that
+ point it is not possible to add new test calls to
+ the test collection anymore.
+
+However, with pytest-2.3 you can use the :ref:`@pytest.fixture` decorator
+and specify ``params`` so that all tests depending on the factory-created
+resource will run multiple times with different parameters.
+
+You can also use the ``pytest_generate_tests`` hook to
+implement the `parametrization scheme of your choice`_. See also
+:ref:`paramexamples` for more examples.
+
+.. _`parametrization scheme of your choice`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
+
+pytest interaction with other packages
+---------------------------------------------------
+
+Issues with pytest, multiprocess and setuptools?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+On Windows the multiprocess package will instantiate sub processes
+by pickling and thus implicitly re-import a lot of local modules.
+Unfortunately, setuptools-0.6.11 does not ``if __name__=='__main__'``
+protect its generated command line script. This leads to infinite
+recursion when running a test that instantiates Processes.
+
+As of mid-2013, there shouldn't be a problem anymore when you
+use the standard setuptools (note that distribute has been merged
+back into setuptools which is now shipped directly with virtualenv).
+
+.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/fixture.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/fixture.rst
new file mode 100644
index 00000000000..01a941ddf65
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/fixture.rst
@@ -0,0 +1,1088 @@
+.. _fixture:
+.. _fixtures:
+.. _`fixture functions`:
+
+pytest fixtures: explicit, modular, scalable
+========================================================
+
+.. currentmodule:: _pytest.python
+
+.. versionadded:: 2.0/2.3/2.4
+
+.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
+.. _`purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software
+.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection
+
+The `purpose of test fixtures`_ is to provide a fixed baseline
+upon which tests can reliably and repeatedly execute. pytest fixtures
+offer dramatic improvements over the classic xUnit style of setup/teardown
+functions:
+
+* fixtures have explicit names and are activated by declaring their use
+ from test functions, modules, classes or whole projects.
+
+* fixtures are implemented in a modular manner, as each fixture name
+ triggers a *fixture function* which can itself use other fixtures.
+
+* fixture management scales from simple unit to complex
+ functional testing, allowing to parametrize fixtures and tests according
+ to configuration and component options, or to re-use fixtures
+ across function, class, module or whole test session scopes.
+
+In addition, pytest continues to support :ref:`xunitsetup`. You can mix
+both styles, moving incrementally from classic to new style, as you
+prefer. You can also start out from existing :ref:`unittest.TestCase
+style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
+
+
+.. _`funcargs`:
+.. _`funcarg mechanism`:
+.. _`fixture function`:
+.. _`@pytest.fixture`:
+.. _`pytest.fixture`:
+
+Fixtures as Function arguments
+-----------------------------------------
+
+Test functions can receive fixture objects by naming them as an input
+argument. For each argument name, a fixture function with that name provides
+the fixture object. Fixture functions are registered by marking them with
+:py:func:`@pytest.fixture <_pytest.python.fixture>`. Let's look at a simple
+self-contained test module containing a fixture and a test function
+using it::
+
+ # content of ./test_smtpsimple.py
+ import pytest
+
+ @pytest.fixture
+ def smtp():
+ import smtplib
+ return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
+
+ def test_ehlo(smtp):
+ response, msg = smtp.ehlo()
+ assert response == 250
+ assert 0 # for demo purposes
+
+Here, the ``test_ehlo`` needs the ``smtp`` fixture value. pytest
+will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
+marked ``smtp`` fixture function. Running the test looks like this::
+
+ $ pytest test_smtpsimple.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 1 item
+
+ test_smtpsimple.py F [100%]
+
+ ================================= FAILURES =================================
+ ________________________________ test_ehlo _________________________________
+
+ smtp = <smtplib.SMTP object at 0xdeadbeef>
+
+ def test_ehlo(smtp):
+ response, msg = smtp.ehlo()
+ assert response == 250
+ > assert 0 # for demo purposes
+ E assert 0
+
+ test_smtpsimple.py:11: AssertionError
+ ========================= 1 failed in 0.12 seconds =========================
+
+In the failure traceback we see that the test function was called with a
+``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture
+function. The test function fails on our deliberate ``assert 0``. Here is
+the exact protocol used by ``pytest`` to call the test function this way:
+
+1. pytest :ref:`finds <test discovery>` the ``test_ehlo`` because
+ of the ``test_`` prefix. The test function needs a function argument
+ named ``smtp``. A matching fixture function is discovered by
+ looking for a fixture-marked function named ``smtp``.
+
+2. ``smtp()`` is called to create an instance.
+
+3. ``test_ehlo(<SMTP instance>)`` is called and fails in the last
+ line of the test function.
+
+Note that if you misspell a function argument or want
+to use one that isn't available, you'll see an error
+with a list of available function arguments.
+
+.. note::
+
+ You can always issue::
+
+ pytest --fixtures test_simplefactory.py
+
+ to see available fixtures.
+
+Fixtures: a prime example of dependency injection
+---------------------------------------------------
+
+Fixtures allow test functions to easily receive and work
+against specific pre-initialized application objects without having
+to care about import/setup/cleanup details.
+It's a prime example of `dependency injection`_ where fixture
+functions take the role of the *injector* and test functions are the
+*consumers* of fixture objects.
+
+.. _`conftest.py`:
+.. _`conftest`:
+
+``conftest.py``: sharing fixture functions
+------------------------------------------
+
+If during implementing your tests you realize that you
+want to use a fixture function from multiple test files you can move it
+to a ``conftest.py`` file.
+You don't need to import the fixture you want to use in a test, it
+automatically gets discovered by pytest. The discovery of
+fixture functions starts at test classes, then test modules, then
+``conftest.py`` files and finally builtin and third party plugins.
+
+You can also use the ``conftest.py`` file to implement
+:ref:`local per-directory plugins <conftest.py plugins>`.
+
+Sharing test data
+-----------------
+
+If you want to make test data from files available to your tests, a good way
+to do this is by loading these data in a fixture for use by your tests.
+This makes use of the automatic caching mechanisms of pytest.
+
+Another good approach is by adding the data files in the ``tests`` folder.
+There are also community plugins available to help managing this aspect of
+testing, e.g. `pytest-datadir <https://github.com/gabrielcnr/pytest-datadir>`__
+and `pytest-datafiles <https://pypi.python.org/pypi/pytest-datafiles>`__.
+
+.. _smtpshared:
+
+Scope: sharing a fixture instance across tests in a class, module or session
+----------------------------------------------------------------------------
+
+.. regendoc:wipe
+
+Fixtures requiring network access depend on connectivity and are
+usually time-expensive to create. Extending the previous example, we
+can add a ``scope='module'`` parameter to the
+:py:func:`@pytest.fixture <_pytest.python.fixture>` invocation
+to cause the decorated ``smtp`` fixture function to only be invoked once
+per test *module* (the default is to invoke once per test *function*).
+Multiple test functions in a test module will thus
+each receive the same ``smtp`` fixture instance, thus saving time.
+
+The next example puts the fixture function into a separate ``conftest.py`` file
+so that tests from multiple test modules in the directory can
+access the fixture function::
+
+ # content of conftest.py
+ import pytest
+ import smtplib
+
+ @pytest.fixture(scope="module")
+ def smtp():
+ return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
+
+The name of the fixture again is ``smtp`` and you can access its result by
+listing the name ``smtp`` as an input parameter in any test or fixture
+function (in or below the directory where ``conftest.py`` is located)::
+
+ # content of test_module.py
+
+ def test_ehlo(smtp):
+ response, msg = smtp.ehlo()
+ assert response == 250
+ assert b"smtp.gmail.com" in msg
+ assert 0 # for demo purposes
+
+ def test_noop(smtp):
+ response, msg = smtp.noop()
+ assert response == 250
+ assert 0 # for demo purposes
+
+We deliberately insert failing ``assert 0`` statements in order to
+inspect what is going on and can now run the tests::
+
+ $ pytest test_module.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 2 items
+
+ test_module.py FF [100%]
+
+ ================================= FAILURES =================================
+ ________________________________ test_ehlo _________________________________
+
+ smtp = <smtplib.SMTP object at 0xdeadbeef>
+
+ def test_ehlo(smtp):
+ response, msg = smtp.ehlo()
+ assert response == 250
+ assert b"smtp.gmail.com" in msg
+ > assert 0 # for demo purposes
+ E assert 0
+
+ test_module.py:6: AssertionError
+ ________________________________ test_noop _________________________________
+
+ smtp = <smtplib.SMTP object at 0xdeadbeef>
+
+ def test_noop(smtp):
+ response, msg = smtp.noop()
+ assert response == 250
+ > assert 0 # for demo purposes
+ E assert 0
+
+ test_module.py:11: AssertionError
+ ========================= 2 failed in 0.12 seconds =========================
+
+You see the two ``assert 0`` failing and more importantly you can also see
+that the same (module-scoped) ``smtp`` object was passed into the two
+test functions because pytest shows the incoming argument values in the
+traceback. As a result, the two test functions using ``smtp`` run as
+quick as a single one because they reuse the same instance.
+
+If you decide that you rather want to have a session-scoped ``smtp``
+instance, you can simply declare it:
+
+.. code-block:: python
+
+ @pytest.fixture(scope="session")
+ def smtp(...):
+ # the returned fixture value will be shared for
+ # all tests needing it
+
+Finally, the ``class`` scope will invoke the fixture once per test *class*.
+
+.. _`finalization`:
+
+Fixture finalization / executing teardown code
+-------------------------------------------------------------
+
+pytest supports execution of fixture specific finalization code
+when the fixture goes out of scope. By using a ``yield`` statement instead of ``return``, all
+the code after the *yield* statement serves as the teardown code:
+
+.. code-block:: python
+
+ # content of conftest.py
+
+ import smtplib
+ import pytest
+
+ @pytest.fixture(scope="module")
+ def smtp():
+ smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
+ yield smtp # provide the fixture value
+ print("teardown smtp")
+ smtp.close()
+
+The ``print`` and ``smtp.close()`` statements will execute when the last test in
+the module has finished execution, regardless of the exception status of the
+tests.
+
+Let's execute it::
+
+ $ pytest -s -q --tb=no
+ FF [100%]teardown smtp
+
+ 2 failed in 0.12 seconds
+
+We see that the ``smtp`` instance is finalized after the two
+tests finished execution. Note that if we decorated our fixture
+function with ``scope='function'`` then fixture setup and cleanup would
+occur around each single test. In either case the test
+module itself does not need to change or know about these details
+of fixture setup.
+
+Note that we can also seamlessly use the ``yield`` syntax with ``with`` statements:
+
+.. code-block:: python
+
+ # content of test_yield2.py
+
+ import smtplib
+ import pytest
+
+ @pytest.fixture(scope="module")
+ def smtp():
+ with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp:
+ yield smtp # provide the fixture value
+
+
+The ``smtp`` connection will be closed after the test finished execution
+because the ``smtp`` object automatically closes when
+the ``with`` statement ends.
+
+Note that if an exception happens during the *setup* code (before the ``yield`` keyword), the
+*teardown* code (after the ``yield``) will not be called.
+
+An alternative option for executing *teardown* code is to
+make use of the ``addfinalizer`` method of the `request-context`_ object to register
+finalization functions.
+
+Here's the ``smtp`` fixture changed to use ``addfinalizer`` for cleanup:
+
+.. code-block:: python
+
+ # content of conftest.py
+ import smtplib
+ import pytest
+
+ @pytest.fixture(scope="module")
+ def smtp(request):
+ smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
+ def fin():
+ print ("teardown smtp")
+ smtp.close()
+ request.addfinalizer(fin)
+ return smtp # provide the fixture value
+
+
+Both ``yield`` and ``addfinalizer`` methods work similarly by calling their code after the test
+ends, but ``addfinalizer`` has two key differences over ``yield``:
+
+1. It is possible to register multiple finalizer functions.
+
+2. Finalizers will always be called regardless if the fixture *setup* code raises an exception.
+ This is handy to properly close all resources created by a fixture even if one of them
+ fails to be created/acquired::
+
+ @pytest.fixture
+ def equipments(request):
+ r = []
+ for port in ('C1', 'C3', 'C28'):
+ equip = connect(port)
+ request.addfinalizer(equip.disconnect)
+ r.append(equip)
+ return r
+
+ In the example above, if ``"C28"`` fails with an exception, ``"C1"`` and ``"C3"`` will still
+ be properly closed. Of course, if an exception happens before the finalize function is
+ registered then it will not be executed.
+
+
+.. _`request-context`:
+
+Fixtures can introspect the requesting test context
+-------------------------------------------------------------
+
+Fixture function can accept the :py:class:`request <FixtureRequest>` object
+to introspect the "requesting" test function, class or module context.
+Further extending the previous ``smtp`` fixture example, let's
+read an optional server URL from the test module which uses our fixture::
+
+ # content of conftest.py
+ import pytest
+ import smtplib
+
+ @pytest.fixture(scope="module")
+ def smtp(request):
+ server = getattr(request.module, "smtpserver", "smtp.gmail.com")
+ smtp = smtplib.SMTP(server, 587, timeout=5)
+ yield smtp
+ print ("finalizing %s (%s)" % (smtp, server))
+ smtp.close()
+
+We use the ``request.module`` attribute to optionally obtain an
+``smtpserver`` attribute from the test module. If we just execute
+again, nothing much has changed::
+
+ $ pytest -s -q --tb=no
+ FF [100%]finalizing <smtplib.SMTP object at 0xdeadbeef> (smtp.gmail.com)
+
+ 2 failed in 0.12 seconds
+
+Let's quickly create another test module that actually sets the
+server URL in its module namespace::
+
+ # content of test_anothersmtp.py
+
+ smtpserver = "mail.python.org" # will be read by smtp fixture
+
+ def test_showhelo(smtp):
+ assert 0, smtp.helo()
+
+Running it::
+
+ $ pytest -qq --tb=short test_anothersmtp.py
+ F [100%]
+ ================================= FAILURES =================================
+ ______________________________ test_showhelo _______________________________
+ test_anothersmtp.py:5: in test_showhelo
+ assert 0, smtp.helo()
+ E AssertionError: (250, b'mail.python.org')
+ E assert 0
+ ------------------------- Captured stdout teardown -------------------------
+ finalizing <smtplib.SMTP object at 0xdeadbeef> (mail.python.org)
+
+voila! The ``smtp`` fixture function picked up our mail server name
+from the module namespace.
+
+.. _`fixture-parametrize`:
+
+Parametrizing fixtures
+-----------------------------------------------------------------
+
+Fixture functions can be parametrized in which case they will be called
+multiple times, each time executing the set of dependent tests, i. e. the
+tests that depend on this fixture. Test functions do usually not need
+to be aware of their re-running. Fixture parametrization helps to
+write exhaustive functional tests for components which themselves can be
+configured in multiple ways.
+
+Extending the previous example, we can flag the fixture to create two
+``smtp`` fixture instances which will cause all tests using the fixture
+to run twice. The fixture function gets access to each parameter
+through the special :py:class:`request <FixtureRequest>` object::
+
+ # content of conftest.py
+ import pytest
+ import smtplib
+
+ @pytest.fixture(scope="module",
+ params=["smtp.gmail.com", "mail.python.org"])
+ def smtp(request):
+ smtp = smtplib.SMTP(request.param, 587, timeout=5)
+ yield smtp
+ print ("finalizing %s" % smtp)
+ smtp.close()
+
+The main change is the declaration of ``params`` with
+:py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values
+for each of which the fixture function will execute and can access
+a value via ``request.param``. No test function code needs to change.
+So let's just do another run::
+
+ $ pytest -q test_module.py
+ FFFF [100%]
+ ================================= FAILURES =================================
+ ________________________ test_ehlo[smtp.gmail.com] _________________________
+
+ smtp = <smtplib.SMTP object at 0xdeadbeef>
+
+ def test_ehlo(smtp):
+ response, msg = smtp.ehlo()
+ assert response == 250
+ assert b"smtp.gmail.com" in msg
+ > assert 0 # for demo purposes
+ E assert 0
+
+ test_module.py:6: AssertionError
+ ________________________ test_noop[smtp.gmail.com] _________________________
+
+ smtp = <smtplib.SMTP object at 0xdeadbeef>
+
+ def test_noop(smtp):
+ response, msg = smtp.noop()
+ assert response == 250
+ > assert 0 # for demo purposes
+ E assert 0
+
+ test_module.py:11: AssertionError
+ ________________________ test_ehlo[mail.python.org] ________________________
+
+ smtp = <smtplib.SMTP object at 0xdeadbeef>
+
+ def test_ehlo(smtp):
+ response, msg = smtp.ehlo()
+ assert response == 250
+ > assert b"smtp.gmail.com" in msg
+ E AssertionError: assert b'smtp.gmail.com' in b'mail.python.org\nPIPELINING\nSIZE 51200000\nETRN\nSTARTTLS\nAUTH DIGEST-MD5 NTLM CRAM-MD5\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8'
+
+ test_module.py:5: AssertionError
+ -------------------------- Captured stdout setup ---------------------------
+ finalizing <smtplib.SMTP object at 0xdeadbeef>
+ ________________________ test_noop[mail.python.org] ________________________
+
+ smtp = <smtplib.SMTP object at 0xdeadbeef>
+
+ def test_noop(smtp):
+ response, msg = smtp.noop()
+ assert response == 250
+ > assert 0 # for demo purposes
+ E assert 0
+
+ test_module.py:11: AssertionError
+ ------------------------- Captured stdout teardown -------------------------
+ finalizing <smtplib.SMTP object at 0xdeadbeef>
+ 4 failed in 0.12 seconds
+
+We see that our two test functions each ran twice, against the different
+``smtp`` instances. Note also, that with the ``mail.python.org``
+connection the second test fails in ``test_ehlo`` because a
+different server string is expected than what arrived.
+
+pytest will build a string that is the test ID for each fixture value
+in a parametrized fixture, e.g. ``test_ehlo[smtp.gmail.com]`` and
+``test_ehlo[mail.python.org]`` in the above examples. These IDs can
+be used with ``-k`` to select specific cases to run, and they will
+also identify the specific case when one is failing. Running pytest
+with ``--collect-only`` will show the generated IDs.
+
+Numbers, strings, booleans and None will have their usual string
+representation used in the test ID. For other objects, pytest will
+make a string based on the argument name. It is possible to customise
+the string used in a test ID for a certain fixture value by using the
+``ids`` keyword argument::
+
+ # content of test_ids.py
+ import pytest
+
+ @pytest.fixture(params=[0, 1], ids=["spam", "ham"])
+ def a(request):
+ return request.param
+
+ def test_a(a):
+ pass
+
+ def idfn(fixture_value):
+ if fixture_value == 0:
+ return "eggs"
+ else:
+ return None
+
+ @pytest.fixture(params=[0, 1], ids=idfn)
+ def b(request):
+ return request.param
+
+ def test_b(b):
+ pass
+
+The above shows how ``ids`` can be either a list of strings to use or
+a function which will be called with the fixture value and then
+has to return a string to use. In the latter case if the function
+return ``None`` then pytest's auto-generated ID will be used.
+
+Running the above tests results in the following test IDs being used::
+
+ $ pytest --collect-only
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 10 items
+ <Module 'test_anothersmtp.py'>
+ <Function 'test_showhelo[smtp.gmail.com]'>
+ <Function 'test_showhelo[mail.python.org]'>
+ <Module 'test_ids.py'>
+ <Function 'test_a[spam]'>
+ <Function 'test_a[ham]'>
+ <Function 'test_b[eggs]'>
+ <Function 'test_b[1]'>
+ <Module 'test_module.py'>
+ <Function 'test_ehlo[smtp.gmail.com]'>
+ <Function 'test_noop[smtp.gmail.com]'>
+ <Function 'test_ehlo[mail.python.org]'>
+ <Function 'test_noop[mail.python.org]'>
+
+ ======================= no tests ran in 0.12 seconds =======================
+
+.. _`interdependent fixtures`:
+
+Modularity: using fixtures from a fixture function
+----------------------------------------------------------
+
+You can not only use fixtures in test functions but fixture functions
+can use other fixtures themselves. This contributes to a modular design
+of your fixtures and allows re-use of framework-specific fixtures across
+many projects. As a simple example, we can extend the previous example
+and instantiate an object ``app`` where we stick the already defined
+``smtp`` resource into it::
+
+ # content of test_appsetup.py
+
+ import pytest
+
+ class App(object):
+ def __init__(self, smtp):
+ self.smtp = smtp
+
+ @pytest.fixture(scope="module")
+ def app(smtp):
+ return App(smtp)
+
+ def test_smtp_exists(app):
+ assert app.smtp
+
+Here we declare an ``app`` fixture which receives the previously defined
+``smtp`` fixture and instantiates an ``App`` object with it. Let's run it::
+
+ $ pytest -v test_appsetup.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collecting ... collected 2 items
+
+ test_appsetup.py::test_smtp_exists[smtp.gmail.com] PASSED [ 50%]
+ test_appsetup.py::test_smtp_exists[mail.python.org] PASSED [100%]
+
+ ========================= 2 passed in 0.12 seconds =========================
+
+Due to the parametrization of ``smtp`` the test will run twice with two
+different ``App`` instances and respective smtp servers. There is no
+need for the ``app`` fixture to be aware of the ``smtp`` parametrization
+as pytest will fully analyse the fixture dependency graph.
+
+Note, that the ``app`` fixture has a scope of ``module`` and uses a
+module-scoped ``smtp`` fixture. The example would still work if ``smtp``
+was cached on a ``session`` scope: it is fine for fixtures to use
+"broader" scoped fixtures but not the other way round:
+A session-scoped fixture could not use a module-scoped one in a
+meaningful way.
+
+
+.. _`automatic per-resource grouping`:
+
+Automatic grouping of tests by fixture instances
+----------------------------------------------------------
+
+.. regendoc: wipe
+
+pytest minimizes the number of active fixtures during test runs.
+If you have a parametrized fixture, then all the tests using it will
+first execute with one instance and then finalizers are called
+before the next fixture instance is created. Among other things,
+this eases testing of applications which create and use global state.
+
+The following example uses two parametrized fixture, one of which is
+scoped on a per-module basis, and all the functions perform ``print`` calls
+to show the setup/teardown flow::
+
+ # content of test_module.py
+ import pytest
+
+ @pytest.fixture(scope="module", params=["mod1", "mod2"])
+ def modarg(request):
+ param = request.param
+ print (" SETUP modarg %s" % param)
+ yield param
+ print (" TEARDOWN modarg %s" % param)
+
+ @pytest.fixture(scope="function", params=[1,2])
+ def otherarg(request):
+ param = request.param
+ print (" SETUP otherarg %s" % param)
+ yield param
+ print (" TEARDOWN otherarg %s" % param)
+
+ def test_0(otherarg):
+ print (" RUN test0 with otherarg %s" % otherarg)
+ def test_1(modarg):
+ print (" RUN test1 with modarg %s" % modarg)
+ def test_2(otherarg, modarg):
+ print (" RUN test2 with otherarg %s and modarg %s" % (otherarg, modarg))
+
+
+Let's run the tests in verbose mode and with looking at the print-output::
+
+ $ pytest -v -s test_module.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
+ cachedir: .cache
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collecting ... collected 8 items
+
+ test_module.py::test_0[1] SETUP otherarg 1
+ RUN test0 with otherarg 1
+ PASSED [ 12%] TEARDOWN otherarg 1
+
+ test_module.py::test_0[2] SETUP otherarg 2
+ RUN test0 with otherarg 2
+ PASSED [ 25%] TEARDOWN otherarg 2
+
+ test_module.py::test_1[mod1] SETUP modarg mod1
+ RUN test1 with modarg mod1
+ PASSED [ 37%]
+ test_module.py::test_2[1-mod1] SETUP otherarg 1
+ RUN test2 with otherarg 1 and modarg mod1
+ PASSED [ 50%] TEARDOWN otherarg 1
+
+ test_module.py::test_2[2-mod1] SETUP otherarg 2
+ RUN test2 with otherarg 2 and modarg mod1
+ PASSED [ 62%] TEARDOWN otherarg 2
+
+ test_module.py::test_1[mod2] TEARDOWN modarg mod1
+ SETUP modarg mod2
+ RUN test1 with modarg mod2
+ PASSED [ 75%]
+ test_module.py::test_2[1-mod2] SETUP otherarg 1
+ RUN test2 with otherarg 1 and modarg mod2
+ PASSED [ 87%] TEARDOWN otherarg 1
+
+ test_module.py::test_2[2-mod2] SETUP otherarg 2
+ RUN test2 with otherarg 2 and modarg mod2
+ PASSED [100%] TEARDOWN otherarg 2
+ TEARDOWN modarg mod2
+
+
+ ========================= 8 passed in 0.12 seconds =========================
+
+You can see that the parametrized module-scoped ``modarg`` resource caused an
+ordering of test execution that lead to the fewest possible "active" resources.
+The finalizer for the ``mod1`` parametrized resource was executed before the
+``mod2`` resource was setup.
+
+In particular notice that test_0 is completely independent and finishes first.
+Then test_1 is executed with ``mod1``, then test_2 with ``mod1``, then test_1
+with ``mod2`` and finally test_2 with ``mod2``.
+
+The ``otherarg`` parametrized resource (having function scope) was set up before
+and teared down after every test that used it.
+
+
+.. _`usefixtures`:
+
+Using fixtures from classes, modules or projects
+----------------------------------------------------------------------
+
+.. regendoc:wipe
+
+Sometimes test functions do not directly need access to a fixture object.
+For example, tests may require to operate with an empty directory as the
+current working directory but otherwise do not care for the concrete
+directory. Here is how you can use the standard `tempfile
+<http://docs.python.org/library/tempfile.html>`_ and pytest fixtures to
+achieve it. We separate the creation of the fixture into a conftest.py
+file::
+
+ # content of conftest.py
+
+ import pytest
+ import tempfile
+ import os
+
+ @pytest.fixture()
+ def cleandir():
+ newpath = tempfile.mkdtemp()
+ os.chdir(newpath)
+
+and declare its use in a test module via a ``usefixtures`` marker::
+
+ # content of test_setenv.py
+ import os
+ import pytest
+
+ @pytest.mark.usefixtures("cleandir")
+ class TestDirectoryInit(object):
+ def test_cwd_starts_empty(self):
+ assert os.listdir(os.getcwd()) == []
+ with open("myfile", "w") as f:
+ f.write("hello")
+
+ def test_cwd_again_starts_empty(self):
+ assert os.listdir(os.getcwd()) == []
+
+Due to the ``usefixtures`` marker, the ``cleandir`` fixture
+will be required for the execution of each test method, just as if
+you specified a "cleandir" function argument to each of them. Let's run it
+to verify our fixture is activated and the tests pass::
+
+ $ pytest -q
+ .. [100%]
+ 2 passed in 0.12 seconds
+
+You can specify multiple fixtures like this:
+
+.. code-block:: python
+
+ @pytest.mark.usefixtures("cleandir", "anotherfixture")
+
+and you may specify fixture usage at the test module level, using
+a generic feature of the mark mechanism:
+
+.. code-block:: python
+
+ pytestmark = pytest.mark.usefixtures("cleandir")
+
+Note that the assigned variable *must* be called ``pytestmark``, assigning e.g.
+``foomark`` will not activate the fixtures.
+
+Lastly you can put fixtures required by all tests in your project
+into an ini-file:
+
+.. code-block:: ini
+
+ # content of pytest.ini
+ [pytest]
+ usefixtures = cleandir
+
+
+.. _`autouse`:
+.. _`autouse fixtures`:
+
+Autouse fixtures (xUnit setup on steroids)
+----------------------------------------------------------------------
+
+.. regendoc:wipe
+
+Occasionally, you may want to have fixtures get invoked automatically
+without declaring a function argument explicitly or a `usefixtures`_ decorator.
+As a practical example, suppose we have a database fixture which has a
+begin/rollback/commit architecture and we want to automatically surround
+each test method by a transaction and a rollback. Here is a dummy
+self-contained implementation of this idea::
+
+ # content of test_db_transact.py
+
+ import pytest
+
+ class DB(object):
+ def __init__(self):
+ self.intransaction = []
+ def begin(self, name):
+ self.intransaction.append(name)
+ def rollback(self):
+ self.intransaction.pop()
+
+ @pytest.fixture(scope="module")
+ def db():
+ return DB()
+
+ class TestClass(object):
+ @pytest.fixture(autouse=True)
+ def transact(self, request, db):
+ db.begin(request.function.__name__)
+ yield
+ db.rollback()
+
+ def test_method1(self, db):
+ assert db.intransaction == ["test_method1"]
+
+ def test_method2(self, db):
+ assert db.intransaction == ["test_method2"]
+
+The class-level ``transact`` fixture is marked with *autouse=true*
+which implies that all test methods in the class will use this fixture
+without a need to state it in the test function signature or with a
+class-level ``usefixtures`` decorator.
+
+If we run it, we get two passing tests::
+
+ $ pytest -q
+ .. [100%]
+ 2 passed in 0.12 seconds
+
+Here is how autouse fixtures work in other scopes:
+
+- autouse fixtures obey the ``scope=`` keyword-argument: if an autouse fixture
+ has ``scope='session'`` it will only be run once, no matter where it is
+ defined. ``scope='class'`` means it will be run once per class, etc.
+
+- if an autouse fixture is defined in a test module, all its test
+ functions automatically use it.
+
+- if an autouse fixture is defined in a conftest.py file then all tests in
+ all test modules below its directory will invoke the fixture.
+
+- lastly, and **please use that with care**: if you define an autouse
+ fixture in a plugin, it will be invoked for all tests in all projects
+ where the plugin is installed. This can be useful if a fixture only
+ anyway works in the presence of certain settings e. g. in the ini-file. Such
+ a global fixture should always quickly determine if it should do
+ any work and avoid otherwise expensive imports or computation.
+
+Note that the above ``transact`` fixture may very well be a fixture that
+you want to make available in your project without having it generally
+active. The canonical way to do that is to put the transact definition
+into a conftest.py file **without** using ``autouse``::
+
+ # content of conftest.py
+ @pytest.fixture
+ def transact(request, db):
+ db.begin()
+ yield
+ db.rollback()
+
+and then e.g. have a TestClass using it by declaring the need::
+
+ @pytest.mark.usefixtures("transact")
+ class TestClass(object):
+ def test_method1(self):
+ ...
+
+All test methods in this TestClass will use the transaction fixture while
+other test classes or functions in the module will not use it unless
+they also add a ``transact`` reference.
+
+Overriding fixtures on various levels
+-------------------------------------
+
+In relatively large test suite, you most likely need to ``override`` a ``global`` or ``root`` fixture with a ``locally``
+defined one, keeping the test code readable and maintainable.
+
+Override a fixture on a folder (conftest) level
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Given the tests file structure is:
+
+::
+
+ tests/
+ __init__.py
+
+ conftest.py
+ # content of tests/conftest.py
+ import pytest
+
+ @pytest.fixture
+ def username():
+ return 'username'
+
+ test_something.py
+ # content of tests/test_something.py
+ def test_username(username):
+ assert username == 'username'
+
+ subfolder/
+ __init__.py
+
+ conftest.py
+ # content of tests/subfolder/conftest.py
+ import pytest
+
+ @pytest.fixture
+ def username(username):
+ return 'overridden-' + username
+
+ test_something.py
+ # content of tests/subfolder/test_something.py
+ def test_username(username):
+ assert username == 'overridden-username'
+
+As you can see, a fixture with the same name can be overridden for certain test folder level.
+Note that the ``base`` or ``super`` fixture can be accessed from the ``overriding``
+fixture easily - used in the example above.
+
+Override a fixture on a test module level
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Given the tests file structure is:
+
+::
+
+ tests/
+ __init__.py
+
+ conftest.py
+ # content of tests/conftest.py
+ @pytest.fixture
+ def username():
+ return 'username'
+
+ test_something.py
+ # content of tests/test_something.py
+ import pytest
+
+ @pytest.fixture
+ def username(username):
+ return 'overridden-' + username
+
+ def test_username(username):
+ assert username == 'overridden-username'
+
+ test_something_else.py
+ # content of tests/test_something_else.py
+ import pytest
+
+ @pytest.fixture
+ def username(username):
+ return 'overridden-else-' + username
+
+ def test_username(username):
+ assert username == 'overridden-else-username'
+
+In the example above, a fixture with the same name can be overridden for certain test module.
+
+
+Override a fixture with direct test parametrization
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Given the tests file structure is:
+
+::
+
+ tests/
+ __init__.py
+
+ conftest.py
+ # content of tests/conftest.py
+ import pytest
+
+ @pytest.fixture
+ def username():
+ return 'username'
+
+ @pytest.fixture
+ def other_username(username):
+ return 'other-' + username
+
+ test_something.py
+ # content of tests/test_something.py
+ import pytest
+
+ @pytest.mark.parametrize('username', ['directly-overridden-username'])
+ def test_username(username):
+ assert username == 'directly-overridden-username'
+
+ @pytest.mark.parametrize('username', ['directly-overridden-username-other'])
+ def test_username_other(other_username):
+ assert other_username == 'other-directly-overridden-username-other'
+
+In the example above, a fixture value is overridden by the test parameter value. Note that the value of the fixture
+can be overridden this way even if the test doesn't use it directly (doesn't mention it in the function prototype).
+
+
+Override a parametrized fixture with non-parametrized one and vice versa
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Given the tests file structure is:
+
+::
+
+ tests/
+ __init__.py
+
+ conftest.py
+ # content of tests/conftest.py
+ import pytest
+
+ @pytest.fixture(params=['one', 'two', 'three'])
+ def parametrized_username(request):
+ return request.param
+
+ @pytest.fixture
+ def non_parametrized_username(request):
+ return 'username'
+
+ test_something.py
+ # content of tests/test_something.py
+ import pytest
+
+ @pytest.fixture
+ def parametrized_username():
+ return 'overridden-username'
+
+ @pytest.fixture(params=['one', 'two', 'three'])
+ def non_parametrized_username(request):
+ return request.param
+
+ def test_username(parametrized_username):
+ assert parametrized_username == 'overridden-username'
+
+ def test_parametrized_username(non_parametrized_username):
+ assert non_parametrized_username in ['one', 'two', 'three']
+
+ test_something_else.py
+ # content of tests/test_something_else.py
+ def test_username(parametrized_username):
+ assert parametrized_username in ['one', 'two', 'three']
+
+ def test_username(non_parametrized_username):
+ assert non_parametrized_username == 'username'
+
+In the example above, a parametrized fixture is overridden with a non-parametrized version, and
+a non-parametrized fixture is overridden with a parametrized version for certain test module.
+The same applies for the test folder level obviously.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/funcarg_compare.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/funcarg_compare.rst
new file mode 100644
index 00000000000..b857a014d31
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/funcarg_compare.rst
@@ -0,0 +1,218 @@
+:orphan:
+
+.. _`funcargcompare`:
+
+pytest-2.3: reasoning for fixture/funcarg evolution
+=============================================================
+
+**Target audience**: Reading this document requires basic knowledge of
+python testing, xUnit setup methods and the (previous) basic pytest
+funcarg mechanism, see http://pytest.org/2.2.4/funcargs.html
+If you are new to pytest, then you can simply ignore this
+section and read the other sections.
+
+.. currentmodule:: _pytest
+
+Shortcomings of the previous ``pytest_funcarg__`` mechanism
+--------------------------------------------------------------
+
+The pre pytest-2.3 funcarg mechanism calls a factory each time a
+funcarg for a test function is required. If a factory wants to
+re-use a resource across different scopes, it often used
+the ``request.cached_setup()`` helper to manage caching of
+resources. Here is a basic example how we could implement
+a per-session Database object::
+
+ # content of conftest.py
+ class Database(object):
+ def __init__(self):
+ print ("database instance created")
+ def destroy(self):
+ print ("database instance destroyed")
+
+ def pytest_funcarg__db(request):
+ return request.cached_setup(setup=DataBase,
+ teardown=lambda db: db.destroy,
+ scope="session")
+
+There are several limitations and difficulties with this approach:
+
+1. Scoping funcarg resource creation is not straight forward, instead one must
+ understand the intricate cached_setup() method mechanics.
+
+2. parametrizing the "db" resource is not straight forward:
+ you need to apply a "parametrize" decorator or implement a
+ :py:func:`~hookspec.pytest_generate_tests` hook
+ calling :py:func:`~python.Metafunc.parametrize` which
+ performs parametrization at the places where the resource
+ is used. Moreover, you need to modify the factory to use an
+ ``extrakey`` parameter containing ``request.param`` to the
+ :py:func:`~python.Request.cached_setup` call.
+
+3. Multiple parametrized session-scoped resources will be active
+ at the same time, making it hard for them to affect global state
+ of the application under test.
+
+4. there is no way how you can make use of funcarg factories
+ in xUnit setup methods.
+
+5. A non-parametrized fixture function cannot use a parametrized
+ funcarg resource if it isn't stated in the test function signature.
+
+All of these limitations are addressed with pytest-2.3 and its
+improved :ref:`fixture mechanism <fixture>`.
+
+
+Direct scoping of fixture/funcarg factories
+--------------------------------------------------------
+
+Instead of calling cached_setup() with a cache scope, you can use the
+:ref:`@pytest.fixture <pytest.fixture>` decorator and directly state
+the scope::
+
+ @pytest.fixture(scope="session")
+ def db(request):
+ # factory will only be invoked once per session -
+ db = DataBase()
+ request.addfinalizer(db.destroy) # destroy when session is finished
+ return db
+
+This factory implementation does not need to call ``cached_setup()`` anymore
+because it will only be invoked once per session. Moreover, the
+``request.addfinalizer()`` registers a finalizer according to the specified
+resource scope on which the factory function is operating.
+
+
+Direct parametrization of funcarg resource factories
+----------------------------------------------------------
+
+Previously, funcarg factories could not directly cause parametrization.
+You needed to specify a ``@parametrize`` decorator on your test function
+or implement a ``pytest_generate_tests`` hook to perform
+parametrization, i.e. calling a test multiple times with different value
+sets. pytest-2.3 introduces a decorator for use on the factory itself::
+
+ @pytest.fixture(params=["mysql", "pg"])
+ def db(request):
+ ... # use request.param
+
+Here the factory will be invoked twice (with the respective "mysql"
+and "pg" values set as ``request.param`` attributes) and all of
+the tests requiring "db" will run twice as well. The "mysql" and
+"pg" values will also be used for reporting the test-invocation variants.
+
+This new way of parametrizing funcarg factories should in many cases
+allow to re-use already written factories because effectively
+``request.param`` was already used when test functions/classes were
+parametrized via
+:py:func:`~_pytest.python.Metafunc.parametrize(indirect=True)` calls.
+
+Of course it's perfectly fine to combine parametrization and scoping::
+
+ @pytest.fixture(scope="session", params=["mysql", "pg"])
+ def db(request):
+ if request.param == "mysql":
+ db = MySQL()
+ elif request.param == "pg":
+ db = PG()
+ request.addfinalizer(db.destroy) # destroy when session is finished
+ return db
+
+This would execute all tests requiring the per-session "db" resource twice,
+receiving the values created by the two respective invocations to the
+factory function.
+
+
+No ``pytest_funcarg__`` prefix when using @fixture decorator
+-------------------------------------------------------------------
+
+When using the ``@fixture`` decorator the name of the function
+denotes the name under which the resource can be accessed as a function
+argument::
+
+ @pytest.fixture()
+ def db(request):
+ ...
+
+The name under which the funcarg resource can be requested is ``db``.
+
+You can still use the "old" non-decorator way of specifying funcarg factories
+aka::
+
+ def pytest_funcarg__db(request):
+ ...
+
+
+But it is then not possible to define scoping and parametrization.
+It is thus recommended to use the factory decorator.
+
+
+solving per-session setup / autouse fixtures
+--------------------------------------------------------------
+
+pytest for a long time offered a pytest_configure and a pytest_sessionstart
+hook which are often used to setup global resources. This suffers from
+several problems:
+
+1. in distributed testing the master process would setup test resources
+ that are never needed because it only co-ordinates the test run
+ activities of the slave processes.
+
+2. if you only perform a collection (with "--collect-only")
+ resource-setup will still be executed.
+
+3. If a pytest_sessionstart is contained in some subdirectories
+ conftest.py file, it will not be called. This stems from the
+ fact that this hook is actually used for reporting, in particular
+ the test-header with platform/custom information.
+
+Moreover, it was not easy to define a scoped setup from plugins or
+conftest files other than to implement a ``pytest_runtest_setup()`` hook
+and caring for scoping/caching yourself. And it's virtually impossible
+to do this with parametrization as ``pytest_runtest_setup()`` is called
+during test execution and parametrization happens at collection time.
+
+It follows that pytest_configure/session/runtest_setup are often not
+appropriate for implementing common fixture needs. Therefore,
+pytest-2.3 introduces :ref:`autouse fixtures` which fully
+integrate with the generic :ref:`fixture mechanism <fixture>`
+and obsolete many prior uses of pytest hooks.
+
+funcargs/fixture discovery now happens at collection time
+---------------------------------------------------------------------
+
+Since pytest-2.3, discovery of fixture/funcarg factories are taken care of
+at collection time. This is more efficient especially for large test suites.
+Moreover, a call to "pytest --collect-only" should be able to in the future
+show a lot of setup-information and thus presents a nice method to get an
+overview of fixture management in your project.
+
+.. _`compatibility notes`:
+
+.. _`funcargscompat`:
+
+Conclusion and compatibility notes
+---------------------------------------------------------
+
+**funcargs** were originally introduced to pytest-2.0. In pytest-2.3
+the mechanism was extended and refined and is now described as
+fixtures:
+
+* previously funcarg factories were specified with a special
+ ``pytest_funcarg__NAME`` prefix instead of using the
+ ``@pytest.fixture`` decorator.
+
+* Factories received a ``request`` object which managed caching through
+ ``request.cached_setup()`` calls and allowed using other funcargs via
+ ``request.getfuncargvalue()`` calls. These intricate APIs made it hard
+ to do proper parametrization and implement resource caching. The
+ new :py:func:`pytest.fixture` decorator allows to declare the scope
+ and let pytest figure things out for you.
+
+* if you used parametrization and funcarg factories which made use of
+ ``request.cached_setup()`` it is recommended to invest a few minutes
+ and simplify your fixture function code to use the :ref:`@pytest.fixture`
+ decorator instead. This will also allow to take advantage of
+ the automatic per-resource grouping of tests.
+
+
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/funcargs.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/funcargs.rst
index bc2c0430239..bc2c0430239 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/funcargs.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/funcargs.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/genapi.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/genapi.py
new file mode 100644
index 00000000000..0ede44fa2de
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/genapi.py
@@ -0,0 +1,41 @@
+import textwrap
+import inspect
+
+class Writer(object):
+ def __init__(self, clsname):
+ self.clsname = clsname
+
+ def __enter__(self):
+ self.file = open("%s.api" % self.clsname, "w")
+ return self
+
+ def __exit__(self, *args):
+ self.file.close()
+ print "wrote", self.file.name
+
+ def line(self, line):
+ self.file.write(line+"\n")
+
+ def docmethod(self, method):
+ doc = " ".join(method.__doc__.split())
+ indent = " "
+ w = textwrap.TextWrapper(initial_indent=indent,
+ subsequent_indent=indent)
+
+ spec = inspect.getargspec(method)
+ del spec.args[0]
+ self.line(".. py:method:: " + method.__name__ +
+ inspect.formatargspec(*spec))
+ self.line("")
+ self.line(w.fill(doc))
+ self.line("")
+
+def pytest_funcarg__a(request):
+ with Writer("request") as writer:
+ writer.docmethod(request.getfixturevalue)
+ writer.docmethod(request.cached_setup)
+ writer.docmethod(request.addfinalizer)
+ writer.docmethod(request.applymarker)
+
+def test_hello(a):
+ pass
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/getting-started.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/getting-started.rst
new file mode 100644
index 00000000000..64b0108262d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/getting-started.rst
@@ -0,0 +1,198 @@
+Installation and Getting Started
+===================================
+
+**Pythons**: Python 2.7, 3.4, 3.5, 3.6, Jython, PyPy-2.3
+
+**Platforms**: Unix/Posix and Windows
+
+**PyPI package name**: `pytest <http://pypi.python.org/pypi/pytest>`_
+
+**dependencies**: `py <http://pypi.python.org/pypi/py>`_,
+`colorama (Windows) <http://pypi.python.org/pypi/colorama>`_,
+
+**documentation as PDF**: `download latest <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
+
+.. _`getstarted`:
+.. _installation:
+
+Installation
+----------------------------------------
+
+Installation::
+
+ pip install -U pytest
+
+To check your installation has installed the correct version::
+
+ $ pytest --version
+ This is pytest version 3.x.y, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py
+
+.. _`simpletest`:
+
+Our first test run
+----------------------------------------------------------
+
+Let's create a first test file with a simple test function::
+
+ # content of test_sample.py
+ def func(x):
+ return x + 1
+
+ def test_answer():
+ assert func(3) == 5
+
+That's it. You can execute the test function now::
+
+ $ pytest
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 1 item
+
+ test_sample.py F [100%]
+
+ ================================= FAILURES =================================
+ _______________________________ test_answer ________________________________
+
+ def test_answer():
+ > assert func(3) == 5
+ E assert 4 == 5
+ E + where 4 = func(3)
+
+ test_sample.py:5: AssertionError
+ ========================= 1 failed in 0.12 seconds =========================
+
+We got a failure report because our little ``func(3)`` call did not return ``5``.
+
+.. note::
+
+ You can simply use the ``assert`` statement for asserting test
+ expectations. pytest's :ref:`assert introspection` will intelligently
+ report intermediate values of the assert expression freeing
+ you from the need to learn the many names of `JUnit legacy methods`_.
+
+.. _`JUnit legacy methods`: http://docs.python.org/library/unittest.html#test-cases
+
+.. _`assert statement`: http://docs.python.org/reference/simple_stmts.html#the-assert-statement
+
+Running multiple tests
+----------------------------------------------------------
+
+``pytest`` will run all files in the current directory and its subdirectories of the form test_*.py or \*_test.py. More generally, it follows :ref:`standard test discovery rules <test discovery>`.
+
+
+Asserting that a certain exception is raised
+--------------------------------------------------------------
+
+If you want to assert that some code raises an exception you can
+use the ``raises`` helper::
+
+ # content of test_sysexit.py
+ import pytest
+ def f():
+ raise SystemExit(1)
+
+ def test_mytest():
+ with pytest.raises(SystemExit):
+ f()
+
+Running it with, this time in "quiet" reporting mode::
+
+ $ pytest -q test_sysexit.py
+ . [100%]
+ 1 passed in 0.12 seconds
+
+Grouping multiple tests in a class
+--------------------------------------------------------------
+
+Once you start to have more than a few tests it often makes sense
+to group tests logically, in classes and modules. Let's write a class
+containing two tests::
+
+ # content of test_class.py
+ class TestClass(object):
+ def test_one(self):
+ x = "this"
+ assert 'h' in x
+
+ def test_two(self):
+ x = "hello"
+ assert hasattr(x, 'check')
+
+The two tests are found because of the standard :ref:`test discovery`.
+There is no need to subclass anything. We can simply
+run the module by passing its filename::
+
+ $ pytest -q test_class.py
+ .F [100%]
+ ================================= FAILURES =================================
+ ____________________________ TestClass.test_two ____________________________
+
+ self = <test_class.TestClass object at 0xdeadbeef>
+
+ def test_two(self):
+ x = "hello"
+ > assert hasattr(x, 'check')
+ E AssertionError: assert False
+ E + where False = hasattr('hello', 'check')
+
+ test_class.py:8: AssertionError
+ 1 failed, 1 passed in 0.12 seconds
+
+The first test passed, the second failed. Again we can easily see
+the intermediate values used in the assertion, helping us to
+understand the reason for the failure.
+
+Going functional: requesting a unique temporary directory
+--------------------------------------------------------------
+
+For functional tests one often needs to create some files
+and pass them to application objects. pytest provides
+:ref:`builtinfixtures` which allow to request arbitrary
+resources, for example a unique temporary directory::
+
+ # content of test_tmpdir.py
+ def test_needsfiles(tmpdir):
+ print (tmpdir)
+ assert 0
+
+We list the name ``tmpdir`` in the test function signature and
+``pytest`` will lookup and call a fixture factory to create the resource
+before performing the test function call. Let's just run it::
+
+ $ pytest -q test_tmpdir.py
+ F [100%]
+ ================================= FAILURES =================================
+ _____________________________ test_needsfiles ______________________________
+
+ tmpdir = local('PYTEST_TMPDIR/test_needsfiles0')
+
+ def test_needsfiles(tmpdir):
+ print (tmpdir)
+ > assert 0
+ E assert 0
+
+ test_tmpdir.py:3: AssertionError
+ --------------------------- Captured stdout call ---------------------------
+ PYTEST_TMPDIR/test_needsfiles0
+ 1 failed in 0.12 seconds
+
+Before the test runs, a unique-per-test-invocation temporary directory
+was created. More info at :ref:`tmpdir handling`.
+
+You can find out what kind of builtin :ref:`fixtures` exist by typing::
+
+ pytest --fixtures # shows builtin and custom fixtures
+
+Where to go next
+-------------------------------------
+
+Here are a few suggestions where to go next:
+
+* :ref:`cmdline` for command line invocation examples
+* :ref:`good practices <goodpractices>` for virtualenv, test layout
+* :ref:`existingtestsuite` for working with pre-existing tests
+* :ref:`fixtures` for providing a functional baseline to your tests
+* :ref:`plugins` managing and writing plugins
+
+.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/goodpractices.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/goodpractices.rst
new file mode 100644
index 00000000000..16fdd24c392
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/goodpractices.rst
@@ -0,0 +1,299 @@
+.. highlightlang:: python
+.. _`goodpractices`:
+
+Good Integration Practices
+=================================================
+
+
+.. _`test discovery`:
+.. _`Python test discovery`:
+
+Conventions for Python test discovery
+-------------------------------------------------
+
+``pytest`` implements the following standard test discovery:
+
+* If no arguments are specified then collection starts from :confval:`testpaths`
+ (if configured) or the current directory. Alternatively, command line arguments
+ can be used in any combination of directories, file names or node ids.
+* Recurse into directories, unless they match :confval:`norecursedirs`.
+* In those directories, search for ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
+* From those files, collect test items:
+
+ * ``test_`` prefixed test functions or methods outside of class
+ * ``test_`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method)
+
+For examples of how to customize your test discovery :doc:`example/pythoncollection`.
+
+Within Python modules, ``pytest`` also discovers tests using the standard
+:ref:`unittest.TestCase <unittest.TestCase>` subclassing technique.
+
+
+Choosing a test layout / import rules
+-------------------------------------
+
+``pytest`` supports two common test layouts:
+
+Tests outside application code
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Putting tests into an extra directory outside your actual application code
+might be useful if you have many functional tests or for other reasons want
+to keep tests separate from actual application code (often a good idea)::
+
+ setup.py
+ mypkg/
+ __init__.py
+ app.py
+ view.py
+ tests/
+ test_app.py
+ test_view.py
+ ...
+
+This way your tests can run easily against an installed version
+of ``mypkg``.
+
+Note that using this scheme your test files must have **unique names**, because
+``pytest`` will import them as *top-level* modules since there are no packages
+to derive a full package name from. In other words, the test files in the example above will
+be imported as ``test_app`` and ``test_view`` top-level modules by adding ``tests/`` to
+``sys.path``.
+
+If you need to have test modules with the same name, you might add ``__init__.py`` files to your
+``tests`` folder and subfolders, changing them to packages::
+
+ setup.py
+ mypkg/
+ ...
+ tests/
+ __init__.py
+ foo/
+ __init__.py
+ test_view.py
+ bar/
+ __init__.py
+ test_view.py
+
+Now pytest will load the modules as ``tests.foo.test_view`` and ``tests.bar.test_view``, allowing
+you to have modules with the same name. But now this introduces a subtle problem: in order to load
+the test modules from the ``tests`` directory, pytest prepends the root of the repository to
+``sys.path``, which adds the side-effect that now ``mypkg`` is also importable.
+This is problematic if you are using a tool like `tox`_ to test your package in a virtual environment,
+because you want to test the *installed* version of your package, not the local code from the repository.
+
+In this situation, it is **strongly** suggested to use a ``src`` layout where application root package resides in a
+sub-directory of your root::
+
+ setup.py
+ src/
+ mypkg/
+ __init__.py
+ app.py
+ view.py
+ tests/
+ __init__.py
+ foo/
+ __init__.py
+ test_view.py
+ bar/
+ __init__.py
+ test_view.py
+
+
+This layout prevents a lot of common pitfalls and has many benefits, which are better explained in this excellent
+`blog post by Ionel Cristian Mărieș <https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure>`_.
+
+Tests as part of application code
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Inlining test directories into your application package
+is useful if you have direct relation between tests and application modules and
+want to distribute them along with your application::
+
+ setup.py
+ mypkg/
+ __init__.py
+ app.py
+ view.py
+ test/
+ __init__.py
+ test_app.py
+ test_view.py
+ ...
+
+In this scheme, it is easy to run your tests using the ``--pyargs`` option::
+
+ pytest --pyargs mypkg
+
+``pytest`` will discover where ``mypkg`` is installed and collect tests from there.
+
+Note that this layout also works in conjunction with the ``src`` layout mentioned in the previous section.
+
+
+.. note::
+
+ You can use Python3 namespace packages (PEP420) for your application
+ but pytest will still perform `test package name`_ discovery based on the
+ presence of ``__init__.py`` files. If you use one of the
+ two recommended file system layouts above but leave away the ``__init__.py``
+ files from your directories it should just work on Python3.3 and above. From
+ "inlined tests", however, you will need to use absolute imports for
+ getting at your application code.
+
+.. _`test package name`:
+
+.. note::
+
+ If ``pytest`` finds a "a/b/test_module.py" test file while
+ recursing into the filesystem it determines the import name
+ as follows:
+
+ * determine ``basedir``: this is the first "upward" (towards the root)
+ directory not containing an ``__init__.py``. If e.g. both ``a``
+ and ``b`` contain an ``__init__.py`` file then the parent directory
+ of ``a`` will become the ``basedir``.
+
+ * perform ``sys.path.insert(0, basedir)`` to make the test module
+ importable under the fully qualified import name.
+
+ * ``import a.b.test_module`` where the path is determined
+ by converting path separators ``/`` into "." characters. This means
+ you must follow the convention of having directory and file
+ names map directly to the import names.
+
+ The reason for this somewhat evolved importing technique is
+ that in larger projects multiple test modules might import
+ from each other and thus deriving a canonical import name helps
+ to avoid surprises such as a test module getting imported twice.
+
+
+.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
+.. _`buildout`: http://www.buildout.org/
+.. _pip: http://pypi.python.org/pypi/pip
+
+.. _`use tox`:
+
+Tox
+------
+
+For development, we recommend to use virtualenv_ environments and pip_
+for installing your application and any dependencies
+as well as the ``pytest`` package itself. This ensures your code and
+dependencies are isolated from the system Python installation.
+
+You can then install your package in "editable" mode::
+
+ pip install -e .
+
+which lets you change your source code (both tests and application) and rerun tests at will.
+This is similar to running `python setup.py develop` or `conda develop` in that it installs
+your package using a symlink to your development code.
+
+Once you are done with your work and want to make sure that your actual
+package passes all tests you may want to look into `tox`_, the
+virtualenv test automation tool and its `pytest support
+<https://tox.readthedocs.io/en/latest/example/pytest.html>`_.
+Tox helps you to setup virtualenv environments with pre-defined
+dependencies and then executing a pre-configured test command with
+options. It will run tests against the installed package and not
+against your source code checkout, helping to detect packaging
+glitches.
+
+
+Integrating with setuptools / ``python setup.py test`` / ``pytest-runner``
+--------------------------------------------------------------------------
+
+You can integrate test runs into your setuptools based project
+with the `pytest-runner <https://pypi.python.org/pypi/pytest-runner>`_ plugin.
+
+Add this to ``setup.py`` file:
+
+.. code-block:: python
+
+ from setuptools import setup
+
+ setup(
+ #...,
+ setup_requires=['pytest-runner', ...],
+ tests_require=['pytest', ...],
+ #...,
+ )
+
+
+And create an alias into ``setup.cfg`` file:
+
+
+.. code-block:: ini
+
+ [aliases]
+ test=pytest
+
+If you now type::
+
+ python setup.py test
+
+this will execute your tests using ``pytest-runner``. As this is a
+standalone version of ``pytest`` no prior installation whatsoever is
+required for calling the test command. You can also pass additional
+arguments to pytest such as your test directory or other
+options using ``--addopts``.
+
+You can also specify other pytest-ini options in your ``setup.cfg`` file
+by putting them into a ``[tool:pytest]`` section:
+
+.. code-block:: ini
+
+ [tool:pytest]
+ addopts = --verbose
+ python_files = testing/*/*.py
+
+
+Manual Integration
+^^^^^^^^^^^^^^^^^^
+
+If for some reason you don't want/can't use ``pytest-runner``, you can write
+your own setuptools Test command for invoking pytest.
+
+.. code-block:: python
+
+ import sys
+
+ from setuptools.command.test import test as TestCommand
+
+
+ class PyTest(TestCommand):
+ user_options = [('pytest-args=', 'a', "Arguments to pass to pytest")]
+
+ def initialize_options(self):
+ TestCommand.initialize_options(self)
+ self.pytest_args = ''
+
+ def run_tests(self):
+ import shlex
+ #import here, cause outside the eggs aren't loaded
+ import pytest
+ errno = pytest.main(shlex.split(self.pytest_args))
+ sys.exit(errno)
+
+
+ setup(
+ #...,
+ tests_require=['pytest'],
+ cmdclass = {'test': PyTest},
+ )
+
+Now if you run::
+
+ python setup.py test
+
+this will download ``pytest`` if needed and then run your tests
+as you would expect it to. You can pass a single string of arguments
+using the ``--pytest-args`` or ``-a`` command-line option. For example::
+
+ python setup.py test -a "--durations=5"
+
+is equivalent to running ``pytest --durations=5``.
+
+
+.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/historical-notes.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/historical-notes.rst
new file mode 100644
index 00000000000..028ceff9b17
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/historical-notes.rst
@@ -0,0 +1,177 @@
+Historical Notes
+================
+
+This page lists features or behavior from previous versions of pytest which have changed over the years. They are
+kept here as a historical note so users looking at old code can find documentation related to them.
+
+cache plugin integrated into the core
+-------------------------------------
+
+.. versionadded:: 2.8
+
+The functionality of the :ref:`core cache <cache>` plugin was previously distributed
+as a third party plugin named ``pytest-cache``. The core plugin
+is compatible regarding command line options and API usage except that you
+can only store/receive data between test runs that is json-serializable.
+
+
+funcargs and ``pytest_funcarg__``
+---------------------------------
+
+.. versionchanged:: 2.3
+
+In versions prior to 2.3 there was no ``@pytest.fixture`` marker
+and you had to use a magic ``pytest_funcarg__NAME`` prefix
+for the fixture factory. This remains and will remain supported
+but is not anymore advertised as the primary means of declaring fixture
+functions.
+
+
+``@pytest.yield_fixture`` decorator
+-----------------------------------
+
+.. versionchanged:: 2.10
+
+Prior to version 2.10, in order to use a ``yield`` statement to execute teardown code one
+had to mark a fixture using the ``yield_fixture`` marker. From 2.10 onward, normal
+fixtures can use ``yield`` directly so the ``yield_fixture`` decorator is no longer needed
+and considered deprecated.
+
+
+``[pytest]`` header in ``setup.cfg``
+------------------------------------
+
+.. versionchanged:: 3.0
+
+Prior to 3.0, the supported section name was ``[pytest]``. Due to how
+this may collide with some distutils commands, the recommended
+section name for ``setup.cfg`` files is now ``[tool:pytest]``.
+
+Note that for ``pytest.ini`` and ``tox.ini`` files the section
+name is ``[pytest]``.
+
+
+Applying marks to ``@pytest.mark.parametrize`` parameters
+---------------------------------------------------------
+
+.. versionchanged:: 3.1
+
+Prior to version 3.1 the supported mechanism for marking values
+used the syntax::
+
+ import pytest
+ @pytest.mark.parametrize("test_input,expected", [
+ ("3+5", 8),
+ ("2+4", 6),
+ pytest.mark.xfail(("6*9", 42),),
+ ])
+ def test_eval(test_input, expected):
+ assert eval(test_input) == expected
+
+
+This was an initial hack to support the feature but soon was demonstrated to be incomplete,
+broken for passing functions or applying multiple marks with the same name but different parameters.
+
+The old syntax is planned to be removed in pytest-4.0.
+
+
+``@pytest.mark.parametrize`` argument names as a tuple
+------------------------------------------------------
+
+.. versionchanged:: 2.4
+
+In versions prior to 2.4 one needed to specify the argument
+names as a tuple. This remains valid but the simpler ``"name1,name2,..."``
+comma-separated-string syntax is now advertised first because
+it's easier to write and produces less line noise.
+
+
+setup: is now an "autouse fixture"
+----------------------------------
+
+.. versionchanged:: 2.3
+
+During development prior to the pytest-2.3 release the name
+``pytest.setup`` was used but before the release it was renamed
+and moved to become part of the general fixture mechanism,
+namely :ref:`autouse fixtures`
+
+
+.. _string conditions:
+
+Conditions as strings instead of booleans
+-----------------------------------------
+
+.. versionchanged:: 2.4
+
+Prior to pytest-2.4 the only way to specify skipif/xfail conditions was
+to use strings::
+
+ import sys
+ @pytest.mark.skipif("sys.version_info >= (3,3)")
+ def test_function():
+ ...
+
+During test function setup the skipif condition is evaluated by calling
+``eval('sys.version_info >= (3,0)', namespace)``. The namespace contains
+all the module globals, and ``os`` and ``sys`` as a minimum.
+
+Since pytest-2.4 :ref:`boolean conditions <condition booleans>` are considered preferable
+because markers can then be freely imported between test modules.
+With strings you need to import not only the marker but all variables
+used by the marker, which violates encapsulation.
+
+The reason for specifying the condition as a string was that ``pytest`` can
+report a summary of skip conditions based purely on the condition string.
+With conditions as booleans you are required to specify a ``reason`` string.
+
+Note that string conditions will remain fully supported and you are free
+to use them if you have no need for cross-importing markers.
+
+The evaluation of a condition string in ``pytest.mark.skipif(conditionstring)``
+or ``pytest.mark.xfail(conditionstring)`` takes place in a namespace
+dictionary which is constructed as follows:
+
+* the namespace is initialized by putting the ``sys`` and ``os`` modules
+ and the pytest ``config`` object into it.
+
+* updated with the module globals of the test function for which the
+ expression is applied.
+
+The pytest ``config`` object allows you to skip based on a test
+configuration value which you might have added::
+
+ @pytest.mark.skipif("not config.getvalue('db')")
+ def test_function(...):
+ ...
+
+The equivalent with "boolean conditions" is::
+
+ @pytest.mark.skipif(not pytest.config.getvalue("db"),
+ reason="--db was not specified")
+ def test_function(...):
+ pass
+
+.. note::
+
+ You cannot use ``pytest.config.getvalue()`` in code
+ imported before pytest's argument parsing takes place. For example,
+ ``conftest.py`` files are imported before command line parsing and thus
+ ``config.getvalue()`` will not execute correctly.
+
+``pytest.set_trace()``
+----------------------
+
+.. versionchanged:: 2.4
+
+Previous to version 2.4 to set a break point in code one needed to use ``pytest.set_trace()``::
+
+ import pytest
+ def test_function():
+ ...
+ pytest.set_trace() # invoke PDB debugger and tracing
+
+
+This is no longer needed and one can use the native ``import pdb;pdb.set_trace()`` call directly.
+
+For more details see :ref:`breakpoints`.
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/cramer2.png b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/cramer2.png
index 6bf0e92e20d..6bf0e92e20d 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/cramer2.png
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/cramer2.png
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/freiburg2.jpg b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/freiburg2.jpg
index 3383d3023da..3383d3023da 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/freiburg2.jpg
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/freiburg2.jpg
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/gaynor3.png b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/gaynor3.png
index a577c168b39..a577c168b39 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/gaynor3.png
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/gaynor3.png
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/keleshev.png b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/keleshev.png
index 0d5e571e261..0d5e571e261 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/keleshev.png
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/keleshev.png
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pullrequest.png b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pullrequest.png
index 4af293b2137..4af293b2137 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pullrequest.png
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pullrequest.png
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pylib.png b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pylib.png
index 2e10d438866..2e10d438866 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pylib.png
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pylib.png
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pytest1.png b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pytest1.png
index e8064a694ca..e8064a694ca 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pytest1.png
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pytest1.png
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pytest1favi.ico b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pytest1favi.ico
index 6a34fe5c9f7..6a34fe5c9f7 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/pytest1favi.ico
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/pytest1favi.ico
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/theuni.png b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/theuni.png
index abeb737e79b..abeb737e79b 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/img/theuni.png
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/img/theuni.png
Binary files differ
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/index.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/index.rst
new file mode 100644
index 00000000000..66c59f08d34
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/index.rst
@@ -0,0 +1,90 @@
+:orphan:
+
+.. _features:
+
+pytest: helps you write better programs
+=======================================
+
+
+The ``pytest`` framework makes it easy to write small tests, yet
+scales to support complex functional testing for applications and libraries.
+
+An example of a simple test:
+
+.. code-block:: python
+
+ # content of test_sample.py
+ def inc(x):
+ return x + 1
+
+ def test_answer():
+ assert inc(3) == 5
+
+
+To execute it::
+
+ $ pytest
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 1 item
+
+ test_sample.py F [100%]
+
+ ================================= FAILURES =================================
+ _______________________________ test_answer ________________________________
+
+ def test_answer():
+ > assert inc(3) == 5
+ E assert 4 == 5
+ E + where 4 = inc(3)
+
+ test_sample.py:5: AssertionError
+ ========================= 1 failed in 0.12 seconds =========================
+
+Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used.
+See :ref:`Getting Started <getstarted>` for more examples.
+
+
+Features
+--------
+
+- Detailed info on failing :ref:`assert statements <assert>` (no need to remember ``self.assert*`` names);
+
+- :ref:`Auto-discovery <test discovery>` of test modules and functions;
+
+- :ref:`Modular fixtures <fixture>` for managing small or parametrized long-lived test resources;
+
+- Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box;
+
+- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested);
+
+- Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community;
+
+
+Documentation
+-------------
+
+Please see :ref:`Contents <toc>` for full documentation, including installation, tutorials and PDF documents.
+
+
+Bugs/Requests
+-------------
+
+Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features.
+
+
+Changelog
+---------
+
+Consult the :ref:`Changelog <changelog>` page for fixes and enhancements of each version.
+
+
+License
+-------
+
+Copyright Holger Krekel and others, 2004-2017.
+
+Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
+
+.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/license.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/license.rst
new file mode 100644
index 00000000000..b8c0dce1bea
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/license.rst
@@ -0,0 +1,32 @@
+.. _license:
+
+License
+-------
+
+Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
+
+::
+
+ The MIT License (MIT)
+
+ Copyright (c) 2004-2017 Holger Krekel and others
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
+ this software and associated documentation files (the "Software"), to deal in
+ the Software without restriction, including without limitation the rights to
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is furnished to do
+ so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/links.inc b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/links.inc
new file mode 100644
index 00000000000..b69390baa84
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/links.inc
@@ -0,0 +1,21 @@
+
+.. _`skipping plugin`: plugin/skipping.html
+.. _`funcargs mechanism`: funcargs.html
+.. _`doctest.py`: http://docs.python.org/library/doctest.html
+.. _`xUnit style setup`: xunit_setup.html
+.. _`pytest_nose`: plugin/nose.html
+.. _`reStructured Text`: http://docutils.sourceforge.net
+.. _`Python debugger`: http://docs.python.org/lib/module-pdb.html
+.. _nose: https://nose.readthedocs.io/en/latest/
+.. _pytest: http://pypi.python.org/pypi/pytest
+.. _mercurial: http://mercurial.selenic.com/wiki/
+.. _`setuptools`: http://pypi.python.org/pypi/setuptools
+.. _`easy_install`:
+.. _`distribute docs`:
+.. _`distribute`: http://pypi.python.org/pypi/distribute
+.. _`pip`: http://pypi.python.org/pypi/pip
+.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
+.. _hudson: http://hudson-ci.org/
+.. _jenkins: http://jenkins-ci.org/
+.. _tox: http://testrun.org/tox
+.. _pylib: https://py.readthedocs.io/en/latest/
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/logging.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/logging.rst
new file mode 100644
index 00000000000..e3bf5603887
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/logging.rst
@@ -0,0 +1,192 @@
+.. _logging:
+
+Logging
+-------
+
+.. versionadded 3.3.0
+
+.. note::
+
+ This feature is a drop-in replacement for the `pytest-catchlog
+ <https://pypi.org/project/pytest-catchlog/>`_ plugin and they will conflict
+ with each other. The backward compatibility API with ``pytest-capturelog``
+ has been dropped when this feature was introduced, so if for that reason you
+ still need ``pytest-catchlog`` you can disable the internal feature by
+ adding to your ``pytest.ini``:
+
+ .. code-block:: ini
+
+ [pytest]
+ addopts=-p no:logging
+
+Log messages are captured by default and for each failed test will be shown in
+the same manner as captured stdout and stderr.
+
+Running without options::
+
+ pytest
+
+Shows failed tests like so::
+
+ ----------------------- Captured stdlog call ----------------------
+ test_reporting.py 26 INFO text going to logger
+ ----------------------- Captured stdout call ----------------------
+ text going to stdout
+ ----------------------- Captured stderr call ----------------------
+ text going to stderr
+ ==================== 2 failed in 0.02 seconds =====================
+
+By default each captured log message shows the module, line number, log level
+and message. Showing the exact module and line number is useful for testing and
+debugging. If desired the log format and date format can be specified to
+anything that the logging module supports.
+
+Running pytest specifying formatting options::
+
+ pytest --log-format="%(asctime)s %(levelname)s %(message)s" \
+ --log-date-format="%Y-%m-%d %H:%M:%S"
+
+Shows failed tests like so::
+
+ ----------------------- Captured stdlog call ----------------------
+ 2010-04-10 14:48:44 INFO text going to logger
+ ----------------------- Captured stdout call ----------------------
+ text going to stdout
+ ----------------------- Captured stderr call ----------------------
+ text going to stderr
+ ==================== 2 failed in 0.02 seconds =====================
+
+These options can also be customized through a configuration file:
+
+.. code-block:: ini
+
+ [pytest]
+ log_format = %(asctime)s %(levelname)s %(message)s
+ log_date_format = %Y-%m-%d %H:%M:%S
+
+Further it is possible to disable reporting logs on failed tests completely
+with::
+
+ pytest --no-print-logs
+
+Or in you ``pytest.ini``:
+
+.. code-block:: ini
+
+ [pytest]
+ log_print = False
+
+
+Shows failed tests in the normal manner as no logs were captured::
+
+ ----------------------- Captured stdout call ----------------------
+ text going to stdout
+ ----------------------- Captured stderr call ----------------------
+ text going to stderr
+ ==================== 2 failed in 0.02 seconds =====================
+
+Inside tests it is possible to change the log level for the captured log
+messages. This is supported by the ``caplog`` fixture::
+
+ def test_foo(caplog):
+ caplog.set_level(logging.INFO)
+ pass
+
+By default the level is set on the handler used to catch the log messages,
+however as a convenience it is also possible to set the log level of any
+logger::
+
+ def test_foo(caplog):
+ caplog.set_level(logging.CRITICAL, logger='root.baz')
+ pass
+
+It is also possible to use a context manager to temporarily change the log
+level::
+
+ def test_bar(caplog):
+ with caplog.at_level(logging.INFO):
+ pass
+
+Again, by default the level of the handler is affected but the level of any
+logger can be changed instead with::
+
+ def test_bar(caplog):
+ with caplog.at_level(logging.CRITICAL, logger='root.baz'):
+ pass
+
+Lastly all the logs sent to the logger during the test run are made available on
+the fixture in the form of both the LogRecord instances and the final log text.
+This is useful for when you want to assert on the contents of a message::
+
+ def test_baz(caplog):
+ func_under_test()
+ for record in caplog.records:
+ assert record.levelname != 'CRITICAL'
+ assert 'wally' not in caplog.text
+
+For all the available attributes of the log records see the
+``logging.LogRecord`` class.
+
+You can also resort to ``record_tuples`` if all you want to do is to ensure,
+that certain messages have been logged under a given logger name with a given
+severity and message::
+
+ def test_foo(caplog):
+ logging.getLogger().info('boo %s', 'arg')
+
+ assert caplog.record_tuples == [
+ ('root', logging.INFO, 'boo arg'),
+ ]
+
+You can call ``caplog.clear()`` to reset the captured log records in a test::
+
+ def test_something_with_clearing_records(caplog):
+ some_method_that_creates_log_records()
+ caplog.clear()
+ your_test_method()
+ assert ['Foo'] == [rec.message for rec in caplog.records]
+
+Live Logs
+^^^^^^^^^
+
+By default, pytest will output any logging records with a level higher or
+equal to WARNING. In order to actually see these logs in the console you have to
+disable pytest output capture by passing ``-s``.
+
+You can specify the logging level for which log records with equal or higher
+level are printed to the console by passing ``--log-cli-level``. This setting
+accepts the logging level names as seen in python's documentation or an integer
+as the logging level num.
+
+Additionally, you can also specify ``--log-cli-format`` and
+``--log-cli-date-format`` which mirror and default to ``--log-format`` and
+``--log-date-format`` if not provided, but are applied only to the console
+logging handler.
+
+All of the CLI log options can also be set in the configuration INI file. The
+option names are:
+
+* ``log_cli_level``
+* ``log_cli_format``
+* ``log_cli_date_format``
+
+If you need to record the whole test suite logging calls to a file, you can pass
+``--log-file=/path/to/log/file``. This log file is opened in write mode which
+means that it will be overwritten at each run tests session.
+
+You can also specify the logging level for the log file by passing
+``--log-file-level``. This setting accepts the logging level names as seen in
+python's documentation(ie, uppercased level names) or an integer as the logging
+level num.
+
+Additionally, you can also specify ``--log-file-format`` and
+``--log-file-date-format`` which are equal to ``--log-format`` and
+``--log-date-format`` but are applied to the log file logging handler.
+
+All of the log file options can also be set in the configuration INI file. The
+option names are:
+
+* ``log_file``
+* ``log_file_level``
+* ``log_file_format``
+* ``log_file_date_format``
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/mark.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/mark.rst
new file mode 100644
index 00000000000..0b0e072a09b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/mark.rst
@@ -0,0 +1,41 @@
+
+.. _mark:
+
+Marking test functions with attributes
+=================================================================
+
+.. currentmodule:: _pytest.mark
+
+By using the ``pytest.mark`` helper you can easily set
+metadata on your test functions. There are
+some builtin markers, for example:
+
+* :ref:`skip <skip>` - always skip a test function
+* :ref:`skipif <skipif>` - skip a test function if a certain condition is met
+* :ref:`xfail <xfail>` - produce an "expected failure" outcome if a certain
+ condition is met
+* :ref:`parametrize <parametrizemark>` to perform multiple calls
+ to the same test function.
+
+It's easy to create custom markers or to apply markers
+to whole test classes or modules. See :ref:`mark examples` for examples
+which also serve as documentation.
+
+.. note::
+
+ Marks can only be applied to tests, having no effect on
+ :ref:`fixtures <fixtures>`.
+
+
+API reference for mark related objects
+------------------------------------------------
+
+.. autoclass:: MarkGenerator
+ :members:
+
+.. autoclass:: MarkDecorator
+ :members:
+
+.. autoclass:: MarkInfo
+ :members:
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/monkeypatch.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/monkeypatch.rst
new file mode 100644
index 00000000000..0c07b2f44fc
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/monkeypatch.rst
@@ -0,0 +1,75 @@
+
+Monkeypatching/mocking modules and environments
+================================================================
+
+.. currentmodule:: _pytest.monkeypatch
+
+Sometimes tests need to invoke functionality which depends
+on global settings or which invokes code which cannot be easily
+tested such as network access. The ``monkeypatch`` fixture
+helps you to safely set/delete an attribute, dictionary item or
+environment variable or to modify ``sys.path`` for importing.
+See the `monkeypatch blog post`_ for some introduction material
+and a discussion of its motivation.
+
+.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
+
+
+Simple example: monkeypatching functions
+---------------------------------------------------
+
+If you want to pretend that ``os.expanduser`` returns a certain
+directory, you can use the :py:meth:`monkeypatch.setattr` method to
+patch this function before calling into a function which uses it::
+
+ # content of test_module.py
+ import os.path
+ def getssh(): # pseudo application code
+ return os.path.join(os.path.expanduser("~admin"), '.ssh')
+
+ def test_mytest(monkeypatch):
+ def mockreturn(path):
+ return '/abc'
+ monkeypatch.setattr(os.path, 'expanduser', mockreturn)
+ x = getssh()
+ assert x == '/abc/.ssh'
+
+Here our test function monkeypatches ``os.path.expanduser`` and
+then calls into a function that calls it. After the test function
+finishes the ``os.path.expanduser`` modification will be undone.
+
+example: preventing "requests" from remote operations
+------------------------------------------------------
+
+If you want to prevent the "requests" library from performing http
+requests in all your tests, you can do::
+
+ # content of conftest.py
+ import pytest
+ @pytest.fixture(autouse=True)
+ def no_requests(monkeypatch):
+ monkeypatch.delattr("requests.sessions.Session.request")
+
+This autouse fixture will be executed for each test function and it
+will delete the method ``request.session.Session.request``
+so that any attempts within tests to create http requests will fail.
+
+
+.. note::
+
+ Be advised that it is not recommended to patch builtin functions such as ``open``,
+ ``compile``, etc., because it might break pytest's internals. If that's
+ unavoidable, passing ``--tb=native``, ``--assert=plain`` and ``--capture=no`` might
+ help although there's no guarantee.
+
+
+Method reference of the monkeypatch fixture
+-------------------------------------------
+
+.. autoclass:: MonkeyPatch
+ :members:
+
+``monkeypatch.setattr/delattr/delitem/delenv()`` all
+by default raise an Exception if the target does not exist.
+Pass ``raising=False`` if you want to skip this check.
+
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/naming20.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/naming20.rst
index 5a81df2698d..5a81df2698d 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/naming20.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/naming20.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/nose.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/nose.rst
new file mode 100644
index 00000000000..10a10633ab0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/nose.rst
@@ -0,0 +1,75 @@
+.. _`noseintegration`:
+
+Running tests written for nose
+=======================================
+
+.. include:: links.inc
+
+``pytest`` has basic support for running tests written for nose_.
+
+.. _nosestyle:
+
+Usage
+-------------
+
+After :ref:`installation` type::
+
+ python setup.py develop # make sure tests can import our package
+ pytest # instead of 'nosetests'
+
+and you should be able to run your nose style tests and
+make use of pytest's capabilities.
+
+Supported nose Idioms
+----------------------
+
+* setup and teardown at module/class/method level
+* SkipTest exceptions and markers
+* setup/teardown decorators
+* ``yield``-based tests and their setup (considered deprecated as of pytest 3.0)
+* ``__test__`` attribute on modules/classes/functions
+* general usage of nose utilities
+
+Unsupported idioms / known issues
+----------------------------------
+
+- unittest-style ``setUp, tearDown, setUpClass, tearDownClass``
+ are recognized only on ``unittest.TestCase`` classes but not
+ on plain classes. ``nose`` supports these methods also on plain
+ classes but pytest deliberately does not. As nose and pytest already
+ both support ``setup_class, teardown_class, setup_method, teardown_method``
+ it doesn't seem useful to duplicate the unittest-API like nose does.
+ If you however rather think pytest should support the unittest-spelling on
+ plain classes please post `to this issue
+ <https://github.com/pytest-dev/pytest/issues/377/>`_.
+
+- nose imports test modules with the same import path (e.g.
+ ``tests.test_mod``) but different file system paths
+ (e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
+ by extending sys.path/import semantics. pytest does not do that
+ but there is discussion in `#268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support. Note that
+ `nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.io/en/latest/differences.html#test-discovery-and-loading>`_.
+
+ If you place a conftest.py file in the root directory of your project
+ (as determined by pytest) pytest will run tests "nose style" against
+ the code below that directory by adding it to your ``sys.path`` instead of
+ running against your installed code.
+
+ You may find yourself wanting to do this if you ran ``python setup.py install``
+ to set up your project, as opposed to ``python setup.py develop`` or any of
+ the package manager equivalents. Installing with develop in a
+ virtual environment like Tox is recommended over this pattern.
+
+- nose-style doctests are not collected and executed correctly,
+ also doctest fixtures don't work.
+
+- no nose-configuration is recognized.
+
+- ``yield``-based methods don't support ``setup`` properly because
+ the ``setup`` method is always called in the same class instance.
+ There are no plans to fix this currently because ``yield``-tests
+ are deprecated in pytest 3.0, with ``pytest.mark.parametrize``
+ being the recommended alternative.
+
+
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/parametrize.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/parametrize.rst
new file mode 100644
index 00000000000..7a4ac2e1877
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/parametrize.rst
@@ -0,0 +1,217 @@
+
+.. _`test generators`:
+.. _`parametrizing-tests`:
+.. _`parametrized test functions`:
+.. _`parametrize`:
+
+.. _`parametrize-basics`:
+
+Parametrizing fixtures and test functions
+==========================================================================
+
+pytest enables test parametrization at several levels:
+
+- :py:func:`pytest.fixture` allows one to :ref:`parametrize fixture
+ functions <fixture-parametrize>`.
+
+* `@pytest.mark.parametrize`_ allows one to define multiple sets of
+ arguments and fixtures at the test function or class.
+
+* `pytest_generate_tests`_ allows one to define custom parametrization
+ schemes or extensions.
+
+.. _parametrizemark:
+.. _`@pytest.mark.parametrize`:
+
+
+``@pytest.mark.parametrize``: parametrizing test functions
+---------------------------------------------------------------------
+
+.. regendoc: wipe
+
+.. versionadded:: 2.2
+.. versionchanged:: 2.4
+ Several improvements.
+
+The builtin ``pytest.mark.parametrize`` decorator enables
+parametrization of arguments for a test function. Here is a typical example
+of a test function that implements checking that a certain input leads
+to an expected output::
+
+ # content of test_expectation.py
+ import pytest
+ @pytest.mark.parametrize("test_input,expected", [
+ ("3+5", 8),
+ ("2+4", 6),
+ ("6*9", 42),
+ ])
+ def test_eval(test_input, expected):
+ assert eval(test_input) == expected
+
+Here, the ``@parametrize`` decorator defines three different ``(test_input,expected)``
+tuples so that the ``test_eval`` function will run three times using
+them in turn::
+
+ $ pytest
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 3 items
+
+ test_expectation.py ..F [100%]
+
+ ================================= FAILURES =================================
+ ____________________________ test_eval[6*9-42] _____________________________
+
+ test_input = '6*9', expected = 42
+
+ @pytest.mark.parametrize("test_input,expected", [
+ ("3+5", 8),
+ ("2+4", 6),
+ ("6*9", 42),
+ ])
+ def test_eval(test_input, expected):
+ > assert eval(test_input) == expected
+ E AssertionError: assert 54 == 42
+ E + where 54 = eval('6*9')
+
+ test_expectation.py:8: AssertionError
+ ==================== 1 failed, 2 passed in 0.12 seconds ====================
+
+As designed in this example, only one pair of input/output values fails
+the simple test function. And as usual with test function arguments,
+you can see the ``input`` and ``output`` values in the traceback.
+
+Note that you could also use the parametrize marker on a class or a module
+(see :ref:`mark`) which would invoke several functions with the argument sets.
+
+It is also possible to mark individual test instances within parametrize,
+for example with the builtin ``mark.xfail``::
+
+ # content of test_expectation.py
+ import pytest
+ @pytest.mark.parametrize("test_input,expected", [
+ ("3+5", 8),
+ ("2+4", 6),
+ pytest.param("6*9", 42,
+ marks=pytest.mark.xfail),
+ ])
+ def test_eval(test_input, expected):
+ assert eval(test_input) == expected
+
+Let's run this::
+
+ $ pytest
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 3 items
+
+ test_expectation.py ..x [100%]
+
+ =================== 2 passed, 1 xfailed in 0.12 seconds ====================
+
+The one parameter set which caused a failure previously now
+shows up as an "xfailed (expected to fail)" test.
+
+To get all combinations of multiple parametrized arguments you can stack
+``parametrize`` decorators::
+
+ import pytest
+ @pytest.mark.parametrize("x", [0, 1])
+ @pytest.mark.parametrize("y", [2, 3])
+ def test_foo(x, y):
+ pass
+
+This will run the test with the arguments set to ``x=0/y=2``, ``x=0/y=3``, ``x=1/y=2`` and
+``x=1/y=3``.
+
+.. _`pytest_generate_tests`:
+
+Basic ``pytest_generate_tests`` example
+---------------------------------------------
+
+Sometimes you may want to implement your own parametrization scheme
+or implement some dynamism for determining the parameters or scope
+of a fixture. For this, you can use the ``pytest_generate_tests`` hook
+which is called when collecting a test function. Through the passed in
+``metafunc`` object you can inspect the requesting test context and, most
+importantly, you can call ``metafunc.parametrize()`` to cause
+parametrization.
+
+For example, let's say we want to run a test taking string inputs which
+we want to set via a new ``pytest`` command line option. Let's first write
+a simple test accepting a ``stringinput`` fixture function argument::
+
+ # content of test_strings.py
+
+ def test_valid_string(stringinput):
+ assert stringinput.isalpha()
+
+Now we add a ``conftest.py`` file containing the addition of a
+command line option and the parametrization of our test function::
+
+ # content of conftest.py
+
+ def pytest_addoption(parser):
+ parser.addoption("--stringinput", action="append", default=[],
+ help="list of stringinputs to pass to test functions")
+
+ def pytest_generate_tests(metafunc):
+ if 'stringinput' in metafunc.fixturenames:
+ metafunc.parametrize("stringinput",
+ metafunc.config.getoption('stringinput'))
+
+If we now pass two stringinput values, our test will run twice::
+
+ $ pytest -q --stringinput="hello" --stringinput="world" test_strings.py
+ .. [100%]
+ 2 passed in 0.12 seconds
+
+Let's also run with a stringinput that will lead to a failing test::
+
+ $ pytest -q --stringinput="!" test_strings.py
+ F [100%]
+ ================================= FAILURES =================================
+ ___________________________ test_valid_string[!] ___________________________
+
+ stringinput = '!'
+
+ def test_valid_string(stringinput):
+ > assert stringinput.isalpha()
+ E AssertionError: assert False
+ E + where False = <built-in method isalpha of str object at 0xdeadbeef>()
+ E + where <built-in method isalpha of str object at 0xdeadbeef> = '!'.isalpha
+
+ test_strings.py:3: AssertionError
+ 1 failed in 0.12 seconds
+
+As expected our test function fails.
+
+If you don't specify a stringinput it will be skipped because
+``metafunc.parametrize()`` will be called with an empty parameter
+list::
+
+ $ pytest -q -rs test_strings.py
+ s [100%]
+ ========================= short test summary info ==========================
+ SKIP [1] test_strings.py: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:1
+ 1 skipped in 0.12 seconds
+
+Note that when calling ``metafunc.parametrize`` multiple times with different parameter sets, all parameter names across
+those sets cannot be duplicated, otherwise an error will be raised.
+
+More examples
+-------------
+
+For further examples, you might want to look at :ref:`more
+parametrization examples <paramexamples>`.
+
+.. _`metafunc object`:
+
+The **metafunc** object
+-------------------------------------------
+
+.. currentmodule:: _pytest.python
+.. autoclass:: Metafunc
+ :members:
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/plugins.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/plugins.rst
new file mode 100644
index 00000000000..400418aee25
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/plugins.rst
@@ -0,0 +1,154 @@
+.. _`external plugins`:
+.. _`extplugins`:
+.. _`using plugins`:
+
+Installing and Using plugins
+============================
+
+This section talks about installing and using third party plugins.
+For writing your own plugins, please refer to :ref:`writing-plugins`.
+
+Installing a third party plugin can be easily done with ``pip``::
+
+ pip install pytest-NAME
+ pip uninstall pytest-NAME
+
+If a plugin is installed, ``pytest`` automatically finds and integrates it,
+there is no need to activate it.
+
+Here is a little annotated list for some popular plugins:
+
+.. _`django`: https://www.djangoproject.com/
+
+* `pytest-django <http://pypi.python.org/pypi/pytest-django>`_: write tests
+ for `django`_ apps, using pytest integration.
+
+* `pytest-twisted <http://pypi.python.org/pypi/pytest-twisted>`_: write tests
+ for `twisted <http://twistedmatrix.com>`_ apps, starting a reactor and
+ processing deferreds from test functions.
+
+* `pytest-cov <http://pypi.python.org/pypi/pytest-cov>`_:
+ coverage reporting, compatible with distributed testing
+
+* `pytest-xdist <http://pypi.python.org/pypi/pytest-xdist>`_:
+ to distribute tests to CPUs and remote hosts, to run in boxed
+ mode which allows to survive segmentation faults, to run in
+ looponfailing mode, automatically re-running failing tests
+ on file changes.
+
+* `pytest-instafail <http://pypi.python.org/pypi/pytest-instafail>`_:
+ to report failures while the test run is happening.
+
+* `pytest-bdd <http://pypi.python.org/pypi/pytest-bdd>`_ and
+ `pytest-konira <http://pypi.python.org/pypi/pytest-konira>`_
+ to write tests using behaviour-driven testing.
+
+* `pytest-timeout <http://pypi.python.org/pypi/pytest-timeout>`_:
+ to timeout tests based on function marks or global definitions.
+
+* `pytest-pep8 <http://pypi.python.org/pypi/pytest-pep8>`_:
+ a ``--pep8`` option to enable PEP8 compliance checking.
+
+* `pytest-flakes <https://pypi.python.org/pypi/pytest-flakes>`_:
+ check source code with pyflakes.
+
+* `oejskit <http://pypi.python.org/pypi/oejskit>`_:
+ a plugin to run javascript unittests in live browsers.
+
+To see a complete list of all plugins with their latest testing
+status against different pytest and Python versions, please visit
+`plugincompat <http://plugincompat.herokuapp.com/>`_.
+
+You may also discover more plugins through a `pytest- pypi.python.org search`_.
+
+.. _`available installable plugins`:
+.. _`pytest- pypi.python.org search`: http://pypi.python.org/pypi?%3Aaction=search&term=pytest-&submit=search
+
+
+Requiring/Loading plugins in a test module or conftest file
+-----------------------------------------------------------
+
+You can require plugins in a test module or a conftest file like this::
+
+ pytest_plugins = "myapp.testsupport.myplugin",
+
+When the test module or conftest plugin is loaded the specified plugins
+will be loaded as well.
+
+ pytest_plugins = "myapp.testsupport.myplugin"
+
+which will import the specified module as a ``pytest`` plugin.
+
+.. _`findpluginname`:
+
+Finding out which plugins are active
+------------------------------------
+
+If you want to find out which plugins are active in your
+environment you can type::
+
+ pytest --trace-config
+
+and will get an extended test header which shows activated plugins
+and their names. It will also print local plugins aka
+:ref:`conftest.py <conftest.py plugins>` files when they are loaded.
+
+.. _`cmdunregister`:
+
+Deactivating / unregistering a plugin by name
+---------------------------------------------
+
+You can prevent plugins from loading or unregister them::
+
+ pytest -p no:NAME
+
+This means that any subsequent try to activate/load the named
+plugin will not work.
+
+If you want to unconditionally disable a plugin for a project, you can add
+this option to your ``pytest.ini`` file:
+
+.. code-block:: ini
+
+ [pytest]
+ addopts = -p no:NAME
+
+Alternatively to disable it only in certain environments (for example in a
+CI server), you can set ``PYTEST_ADDOPTS`` environment variable to
+``-p no:name``.
+
+See :ref:`findpluginname` for how to obtain the name of a plugin.
+
+.. _`builtin plugins`:
+
+Pytest default plugin reference
+-------------------------------
+
+
+You can find the source code for the following plugins
+in the `pytest repository <https://github.com/pytest-dev/pytest>`_.
+
+.. autosummary::
+
+ _pytest.assertion
+ _pytest.cacheprovider
+ _pytest.capture
+ _pytest.config
+ _pytest.doctest
+ _pytest.helpconfig
+ _pytest.junitxml
+ _pytest.mark
+ _pytest.monkeypatch
+ _pytest.nose
+ _pytest.pastebin
+ _pytest.debugging
+ _pytest.pytester
+ _pytest.python
+ _pytest.recwarn
+ _pytest.resultlog
+ _pytest.runner
+ _pytest.main
+ _pytest.skipping
+ _pytest.terminal
+ _pytest.tmpdir
+ _pytest.unittest
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/projects.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/projects.rst
new file mode 100644
index 00000000000..86df99ab2c5
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/projects.rst
@@ -0,0 +1,85 @@
+.. _projects:
+
+.. image:: img/gaynor3.png
+ :width: 400px
+ :align: right
+
+.. image:: img/theuni.png
+ :width: 400px
+ :align: right
+
+.. image:: img/cramer2.png
+ :width: 400px
+ :align: right
+
+.. image:: img/keleshev.png
+ :width: 400px
+ :align: right
+
+
+Project examples
+==========================
+
+Here are some examples of projects using ``pytest`` (please send notes via :ref:`contact`):
+
+* `PyPy <http://pypy.org>`_, Python with a JIT compiler, running over
+ `21000 tests <http://buildbot.pypy.org/summary?branch=%3Ctrunk%3E>`_
+* the `MoinMoin <http://moinmo.in>`_ Wiki Engine
+* `sentry <https://getsentry.com/welcome/>`_, realtime app-maintenance and exception tracking
+* `Astropy <http://www.astropy.org/>`_ and `affiliated packages <http://www.astropy.org/affiliated/index.html>`_
+* `tox <http://testrun.org/tox>`_, virtualenv/Hudson integration tool
+* `PIDA <http://pida.co.uk>`_ framework for integrated development
+* `PyPM <http://code.activestate.com/pypm/>`_ ActiveState's package manager
+* `Fom <http://packages.python.org/Fom/>`_ a fluid object mapper for FluidDB
+* `applib <https://github.com/ActiveState/applib>`_ cross-platform utilities
+* `six <http://pypi.python.org/pypi/six/>`_ Python 2 and 3 compatibility utilities
+* `pediapress <http://code.pediapress.com/wiki/wiki>`_ MediaWiki articles
+* `mwlib <http://pypi.python.org/pypi/mwlib>`_ mediawiki parser and utility library
+* `The Translate Toolkit <http://translate.sourceforge.net/wiki/toolkit/index>`_ for localization and conversion
+* `execnet <http://codespeak.net/execnet>`_ rapid multi-Python deployment
+* `pylib <http://py.rtfd.org>`_ cross-platform path, IO, dynamic code library
+* `Pacha <http://pacha.cafepais.com/>`_ configuration management in five minutes
+* `bbfreeze <http://pypi.python.org/pypi/bbfreeze>`_ create standalone executables from Python scripts
+* `pdb++ <http://bitbucket.org/antocuni/pdb>`_ a fancier version of PDB
+* `py-s3fuse <http://code.google.com/p/py-s3fuse/>`_ Amazon S3 FUSE based filesystem
+* `waskr <http://code.google.com/p/waskr/>`_ WSGI Stats Middleware
+* `guachi <http://code.google.com/p/guachi/>`_ global persistent configs for Python modules
+* `Circuits <http://pypi.python.org/pypi/circuits>`_ lightweight Event Driven Framework
+* `pygtk-helpers <http://bitbucket.org/aafshar/pygtkhelpers-main/>`_ easy interaction with PyGTK
+* `QuantumCore <http://quantumcore.org/>`_ statusmessage and repoze openid plugin
+* `pydataportability <http://pydataportability.net/>`_ libraries for managing the open web
+* `XIST <http://www.livinglogic.de/Python/xist/>`_ extensible HTML/XML generator
+* `tiddlyweb <http://pypi.python.org/pypi/tiddlyweb>`_ optionally headless, extensible RESTful datastore
+* `fancycompleter <http://bitbucket.org/antocuni/fancycompleter/src>`_ for colorful tab-completion
+* `Paludis <http://paludis.exherbo.org/>`_ tools for Gentoo Paludis package manager
+* `Gerald <http://halfcooked.com/code/gerald/>`_ schema comparison tool
+* `abjad <http://code.google.com/p/abjad/>`_ Python API for Formalized Score control
+* `bu <http://packages.python.org/bu/>`_ a microscopic build system
+* `katcp <https://bitbucket.org/hodgestar/katcp>`_ Telescope communication protocol over Twisted
+* `kss plugin timer <http://pypi.python.org/pypi/kss.plugin.timer>`_
+* `pyudev <https://pyudev.readthedocs.io/en/latest/tests/plugins.html>`_ a pure Python binding to the Linux library libudev
+* `pytest-localserver <https://bitbucket.org/pytest-dev/pytest-localserver/>`_ a plugin for pytest that provides an httpserver and smtpserver
+* `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus/>`_ a plugin that extends monkeypatch
+
+These projects help integrate ``pytest`` into other Python frameworks:
+
+* `pytest-django <http://pypi.python.org/pypi/pytest-django/>`_ for Django
+* `zope.pytest <http://packages.python.org/zope.pytest/>`_ for Zope and Grok
+* `pytest_gae <http://pypi.python.org/pypi/pytest_gae/0.2.1>`_ for Google App Engine
+* There is `some work <https://github.com/Kotti/Kotti/blob/master/kotti/testing.py>`_ underway for Kotti, a CMS built in Pyramid/Pylons
+
+
+Some organisations using pytest
+-----------------------------------
+
+* `Square Kilometre Array, Cape Town <http://ska.ac.za/>`_
+* `Some Mozilla QA people <http://www.theautomatedtester.co.uk/blog/2011/pytest_and_xdist_plugin.html>`_ use pytest to distribute their Selenium tests
+* `Tandberg <http://www.tandberg.com/>`_
+* `Shootq <http://web.shootq.com/>`_
+* `Stups department of Heinrich Heine University Duesseldorf <http://www.stups.uni-duesseldorf.de/projects.php>`_
+* `cellzome <http://www.cellzome.com/>`_
+* `Open End, Gothenborg <http://www.openend.se>`_
+* `Laboratory of Bioinformatics, Warsaw <http://genesilico.pl/>`_
+* `merlinux, Germany <http://merlinux.eu>`_
+* `ESSS, Brazil <http://www.esss.com.br>`_
+* many more ... (please be so kind to send a note via :ref:`contact`)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/proposals/parametrize_with_fixtures.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/proposals/parametrize_with_fixtures.rst
new file mode 100644
index 00000000000..146032aa471
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/proposals/parametrize_with_fixtures.rst
@@ -0,0 +1,160 @@
+:orphan:
+
+===================================
+PROPOSAL: Parametrize with fixtures
+===================================
+
+.. warning::
+
+ This document outlines a proposal around using fixtures as input
+ of parametrized tests or fixtures.
+
+Problem
+-------
+
+As a user I have functional tests that I would like to run against various
+scenarios.
+
+In this particular example we want to generate a new project based on a
+cookiecutter template. We want to test default values but also data that
+emulates user input.
+
+- use default values
+
+- emulate user input
+
+ - specify 'author'
+
+ - specify 'project_slug'
+
+ - specify 'author' and 'project_slug'
+
+This is how a functional test could look like:
+
+.. code-block:: python
+
+ import pytest
+
+ @pytest.fixture
+ def default_context():
+ return {'extra_context': {}}
+
+
+ @pytest.fixture(params=[
+ {'author': 'alice'},
+ {'project_slug': 'helloworld'},
+ {'author': 'bob', 'project_slug': 'foobar'},
+ ])
+ def extra_context(request):
+ return {'extra_context': request.param}
+
+
+ @pytest.fixture(params=['default', 'extra'])
+ def context(request):
+ if request.param == 'default':
+ return request.getfuncargvalue('default_context')
+ else:
+ return request.getfuncargvalue('extra_context')
+
+
+ def test_generate_project(cookies, context):
+ """Call the cookiecutter API to generate a new project from a
+ template.
+ """
+ result = cookies.bake(extra_context=context)
+
+ assert result.exit_code == 0
+ assert result.exception is None
+ assert result.project.isdir()
+
+
+Issues
+------
+
+* By using ``request.getfuncargvalue()`` we rely on actual fixture function
+ execution to know what fixtures are involved, due to it's dynamic nature
+* More importantly, ``request.getfuncargvalue()`` cannot be combined with
+ parametrized fixtures, such as ``extra_context``
+* This is very inconvenient if you wish to extend an existing test suite by
+ certain parameters for fixtures that are already used by tests
+
+pytest version 3.0 reports an error if you try to run above code::
+
+ Failed: The requested fixture has no parameter defined for the current
+ test.
+
+ Requested fixture 'extra_context'
+
+
+Proposed solution
+-----------------
+
+A new function that can be used in modules can be used to dynamically define
+fixtures from existing ones.
+
+.. code-block:: python
+
+ pytest.define_combined_fixture(
+ name='context',
+ fixtures=['default_context', 'extra_context'],
+ )
+
+The new fixture ``context`` inherits the scope from the used fixtures and yield
+the following values.
+
+- ``{}``
+
+- ``{'author': 'alice'}``
+
+- ``{'project_slug': 'helloworld'}``
+
+- ``{'author': 'bob', 'project_slug': 'foobar'}``
+
+Alternative approach
+--------------------
+
+A new helper function named ``fixture_request`` would tell pytest to yield
+all parameters marked as a fixture.
+
+.. note::
+
+ The `pytest-lazy-fixture <https://pypi.python.org/pypi/pytest-lazy-fixture>`_ plugin implements a very
+ similar solution to the proposal below, make sure to check it out.
+
+.. code-block:: python
+
+ @pytest.fixture(params=[
+ pytest.fixture_request('default_context'),
+ pytest.fixture_request('extra_context'),
+ ])
+ def context(request):
+ """Returns all values for ``default_context``, one-by-one before it
+ does the same for ``extra_context``.
+
+ request.param:
+ - {}
+ - {'author': 'alice'}
+ - {'project_slug': 'helloworld'}
+ - {'author': 'bob', 'project_slug': 'foobar'}
+ """
+ return request.param
+
+The same helper can be used in combination with ``pytest.mark.parametrize``.
+
+.. code-block:: python
+
+
+ @pytest.mark.parametrize(
+ 'context, expected_response_code',
+ [
+ (pytest.fixture_request('default_context'), 0),
+ (pytest.fixture_request('extra_context'), 0),
+ ],
+ )
+ def test_generate_project(cookies, context, exit_code):
+ """Call the cookiecutter API to generate a new project from a
+ template.
+ """
+ result = cookies.bake(extra_context=context)
+
+ assert result.exit_code == exit_code
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/pytest.ini b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/pytest.ini
index 7604360561c..7604360561c 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/pytest.ini
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/pytest.ini
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/pythonpath.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/pythonpath.rst
new file mode 100644
index 00000000000..b6474276887
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/pythonpath.rst
@@ -0,0 +1,76 @@
+.. _pythonpath:
+
+pytest import mechanisms and ``sys.path``/``PYTHONPATH``
+========================================================
+
+Here's a list of scenarios where pytest may need to change ``sys.path`` in order
+to import test modules or ``conftest.py`` files.
+
+Test modules / ``conftest.py`` files inside packages
+----------------------------------------------------
+
+Consider this file and directory layout::
+
+ root/
+ |- foo/
+ |- __init__.py
+ |- conftest.py
+ |- bar/
+ |- __init__.py
+ |- tests/
+ |- __init__.py
+ |- test_foo.py
+
+
+When executing::
+
+ pytest root/
+
+
+
+pytest will find ``foo/bar/tests/test_foo.py`` and realize it is part of a package given that
+there's an ``__init__.py`` file in the same folder. It will then search upwards until it can find the
+last folder which still contains an ``__init__.py`` file in order to find the package *root* (in
+this case ``foo/``). To load the module, it will insert ``root/`` to the front of
+``sys.path`` (if not there already) in order to load
+``test_foo.py`` as the *module* ``foo.bar.tests.test_foo``.
+
+The same logic applies to the ``conftest.py`` file: it will be imported as ``foo.conftest`` module.
+
+Preserving the full package name is important when tests live in a package to avoid problems
+and allow test modules to have duplicated names. This is also discussed in details in
+:ref:`test discovery`.
+
+Standalone test modules / ``conftest.py`` files
+-----------------------------------------------
+
+Consider this file and directory layout::
+
+ root/
+ |- foo/
+ |- conftest.py
+ |- bar/
+ |- tests/
+ |- test_foo.py
+
+
+When executing::
+
+ pytest root/
+
+pytest will find ``foo/bar/tests/test_foo.py`` and realize it is NOT part of a package given that
+there's no ``__init__.py`` file in the same folder. It will then add ``root/foo/bar/tests`` to
+``sys.path`` in order to import ``test_foo.py`` as the *module* ``test_foo``. The same is done
+with the ``conftest.py`` file by adding ``root/foo`` to ``sys.path`` to import it as ``conftest``.
+
+For this reason this layout cannot have test modules with the same name, as they all will be
+imported in the global import namespace.
+
+This is also discussed in details in :ref:`test discovery`.
+
+Invoking ``pytest`` versus ``python -m pytest``
+-----------------------------------------------
+
+Running pytest with ``python -m pytest [...]`` instead of ``pytest [...]`` yields nearly
+equivalent behaviour, except that the former call will add the current directory to ``sys.path``.
+See also :ref:`cmdline`.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/recwarn.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/recwarn.rst
new file mode 100644
index 00000000000..513af0d450e
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/recwarn.rst
@@ -0,0 +1,3 @@
+:orphan:
+
+This page has been moved, please see :ref:`assertwarnings`.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/requirements.txt b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/requirements.txt
new file mode 100644
index 00000000000..72bb60a811f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/requirements.txt
@@ -0,0 +1,3 @@
+# pinning sphinx to 1.4.* due to search issues with rtd:
+# https://github.com/rtfd/readthedocs-sphinx-ext/issues/25
+sphinx ==1.4.*
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/skipping.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/skipping.rst
new file mode 100644
index 00000000000..7e001929b23
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/skipping.rst
@@ -0,0 +1,378 @@
+.. _`skip and xfail`:
+
+.. _skipping:
+
+Skip and xfail: dealing with tests that cannot succeed
+======================================================
+
+You can mark test functions that cannot be run on certain platforms
+or that you expect to fail so pytest can deal with them accordingly and
+present a summary of the test session, while keeping the test suite *green*.
+
+A **skip** means that you expect your test to pass only if some conditions are met,
+otherwise pytest should skip running the test altogether. Common examples are skipping
+windows-only tests on non-windows platforms, or skipping tests that depend on an external
+resource which is not available at the moment (for example a database).
+
+A **xfail** means that you expect a test to fail for some reason.
+A common example is a test for a feature not yet implemented, or a bug not yet fixed.
+When a test passes despite being expected to fail (marked with ``pytest.mark.xfail``),
+it's an **xpass** and will be reported in the test summary.
+
+``pytest`` counts and lists *skip* and *xfail* tests separately. Detailed
+information about skipped/xfailed tests is not shown by default to avoid
+cluttering the output. You can use the ``-r`` option to see details
+corresponding to the "short" letters shown in the test progress::
+
+ pytest -rxXs # show extra info on xfailed, xpassed, and skipped tests
+
+More details on the ``-r`` option can be found by running ``pytest -h``.
+
+(See :ref:`how to change command line options defaults`)
+
+.. _skipif:
+.. _skip:
+.. _`condition booleans`:
+
+Skipping test functions
+-----------------------
+
+.. versionadded:: 2.9
+
+The simplest way to skip a test function is to mark it with the ``skip`` decorator
+which may be passed an optional ``reason``:
+
+.. code-block:: python
+
+ @pytest.mark.skip(reason="no way of currently testing this")
+ def test_the_unknown():
+ ...
+
+
+Alternatively, it is also possible to skip imperatively during test execution or setup
+by calling the ``pytest.skip(reason)`` function:
+
+.. code-block:: python
+
+ def test_function():
+ if not valid_config():
+ pytest.skip("unsupported configuration")
+
+It is also possible to skip the whole module using
+``pytest.skip(reason, allow_module_level=True)`` at the module level:
+
+.. code-block:: python
+
+ import pytest
+
+ if not pytest.config.getoption("--custom-flag"):
+ pytest.skip("--custom-flag is missing, skipping tests", allow_module_level=True)
+
+The imperative method is useful when it is not possible to evaluate the skip condition
+during import time.
+
+``skipif``
+~~~~~~~~~~
+
+.. versionadded:: 2.0
+
+If you wish to skip something conditionally then you can use ``skipif`` instead.
+Here is an example of marking a test function to be skipped
+when run on a Python3.6 interpreter::
+
+ import sys
+ @pytest.mark.skipif(sys.version_info < (3,6),
+ reason="requires python3.6")
+ def test_function():
+ ...
+
+If the condition evaluates to ``True`` during collection, the test function will be skipped,
+with the specified reason appearing in the summary when using ``-rs``.
+
+You can share ``skipif`` markers between modules. Consider this test module::
+
+ # content of test_mymodule.py
+ import mymodule
+ minversion = pytest.mark.skipif(mymodule.__versioninfo__ < (1,1),
+ reason="at least mymodule-1.1 required")
+ @minversion
+ def test_function():
+ ...
+
+You can import the marker and reuse it in another test module::
+
+ # test_myothermodule.py
+ from test_mymodule import minversion
+
+ @minversion
+ def test_anotherfunction():
+ ...
+
+For larger test suites it's usually a good idea to have one file
+where you define the markers which you then consistently apply
+throughout your test suite.
+
+Alternatively, you can use :ref:`condition strings
+<string conditions>` instead of booleans, but they can't be shared between modules easily
+so they are supported mainly for backward compatibility reasons.
+
+
+Skip all test functions of a class or module
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can use the ``skipif`` marker (as any other marker) on classes::
+
+ @pytest.mark.skipif(sys.platform == 'win32',
+ reason="does not run on windows")
+ class TestPosixCalls(object):
+
+ def test_function(self):
+ "will not be setup or run under 'win32' platform"
+
+If the condition is ``True``, this marker will produce a skip result for
+each of the test methods of that class.
+
+.. warning::
+
+ The use of ``skipif`` on classes that use inheritance is strongly
+ discouraged. `A Known bug <https://github.com/pytest-dev/pytest/issues/568>`_
+ in pytest's markers may cause unexpected behavior in super classes.
+
+If you want to skip all test functions of a module, you may use
+the ``pytestmark`` name on the global level:
+
+.. code-block:: python
+
+ # test_module.py
+ pytestmark = pytest.mark.skipif(...)
+
+If multiple ``skipif`` decorators are applied to a test function, it
+will be skipped if any of the skip conditions is true.
+
+.. _`whole class- or module level`: mark.html#scoped-marking
+
+
+Skipping files or directories
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes you may need to skip an entire file or directory, for example if the
+tests rely on Python version-specific features or contain code that you do not
+wish pytest to run. In this case, you must exclude the files and directories
+from collection. Refer to :ref:`customizing-test-collection` for more
+information.
+
+
+Skipping on a missing import dependency
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can use the following helper at module level
+or within a test or test setup function::
+
+ docutils = pytest.importorskip("docutils")
+
+If ``docutils`` cannot be imported here, this will lead to a
+skip outcome of the test. You can also skip based on the
+version number of a library::
+
+ docutils = pytest.importorskip("docutils", minversion="0.3")
+
+The version will be read from the specified
+module's ``__version__`` attribute.
+
+Summary
+~~~~~~~
+
+Here's a quick guide on how to skip tests in a module in different situations:
+
+1. Skip all tests in a module unconditionally:
+
+ .. code-block:: python
+
+ pytestmark = pytest.mark.skip('all tests still WIP')
+
+2. Skip all tests in a module based on some condition:
+
+ .. code-block:: python
+
+ pytestmark = pytest.mark.skipif(sys.platform == 'win32', 'tests for linux only')
+
+3. Skip all tests in a module if some import is missing:
+
+ .. code-block:: python
+
+ pexpect = pytest.importorskip('pexpect')
+
+
+.. _xfail:
+
+XFail: mark test functions as expected to fail
+----------------------------------------------
+
+You can use the ``xfail`` marker to indicate that you
+expect a test to fail::
+
+ @pytest.mark.xfail
+ def test_function():
+ ...
+
+This test will be run but no traceback will be reported
+when it fails. Instead terminal reporting will list it in the
+"expected to fail" (``XFAIL``) or "unexpectedly passing" (``XPASS``) sections.
+
+Alternatively, you can also mark a test as ``XFAIL`` from within a test or setup function
+imperatively:
+
+.. code-block:: python
+
+ def test_function():
+ if not valid_config():
+ pytest.xfail("failing configuration (but should work)")
+
+This will unconditionally make ``test_function`` ``XFAIL``. Note that no other code is executed
+after ``pytest.xfail`` call, differently from the marker. That's because it is implemented
+internally by raising a known exception.
+
+Here's the signature of the ``xfail`` **marker** (not the function), using Python 3 keyword-only
+arguments syntax:
+
+.. code-block:: python
+
+ def xfail(condition=None, *, reason=None, raises=None, run=True, strict=False):
+
+
+
+
+``strict`` parameter
+~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 2.9
+
+Both ``XFAIL`` and ``XPASS`` don't fail the test suite, unless the ``strict`` keyword-only
+parameter is passed as ``True``:
+
+.. code-block:: python
+
+ @pytest.mark.xfail(strict=True)
+ def test_function():
+ ...
+
+
+This will make ``XPASS`` ("unexpectedly passing") results from this test to fail the test suite.
+
+You can change the default value of the ``strict`` parameter using the
+``xfail_strict`` ini option:
+
+.. code-block:: ini
+
+ [pytest]
+ xfail_strict=true
+
+
+``reason`` parameter
+~~~~~~~~~~~~~~~~~~~~
+
+As with skipif_ you can also mark your expectation of a failure
+on a particular platform::
+
+ @pytest.mark.xfail(sys.version_info >= (3,6),
+ reason="python3.6 api changes")
+ def test_function():
+ ...
+
+
+``raises`` parameter
+~~~~~~~~~~~~~~~~~~~~
+
+If you want to be more specific as to why the test is failing, you can specify
+a single exception, or a list of exceptions, in the ``raises`` argument.
+
+.. code-block:: python
+
+ @pytest.mark.xfail(raises=RuntimeError)
+ def test_function():
+ ...
+
+Then the test will be reported as a regular failure if it fails with an
+exception not mentioned in ``raises``.
+
+``run`` parameter
+~~~~~~~~~~~~~~~~~
+
+If a test should be marked as xfail and reported as such but should not be
+even executed, use the ``run`` parameter as ``False``:
+
+.. code-block:: python
+
+ @pytest.mark.xfail(run=False)
+ def test_function():
+ ...
+
+This is specially useful for xfailing tests that are crashing the interpreter and should be
+investigated later.
+
+
+Ignoring xfail
+~~~~~~~~~~~~~~
+
+By specifying on the commandline::
+
+ pytest --runxfail
+
+you can force the running and reporting of an ``xfail`` marked test
+as if it weren't marked at all. This also causes ``pytest.xfail`` to produce no effect.
+
+Examples
+~~~~~~~~
+
+Here is a simple test file with the several usages:
+
+.. literalinclude:: example/xfail_demo.py
+
+Running it with the report-on-xfail option gives this output::
+
+ example $ pytest -rx xfail_demo.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR/example, inifile:
+ collected 7 items
+
+ xfail_demo.py xxxxxxx [100%]
+ ========================= short test summary info ==========================
+ XFAIL xfail_demo.py::test_hello
+ XFAIL xfail_demo.py::test_hello2
+ reason: [NOTRUN]
+ XFAIL xfail_demo.py::test_hello3
+ condition: hasattr(os, 'sep')
+ XFAIL xfail_demo.py::test_hello4
+ bug 110
+ XFAIL xfail_demo.py::test_hello5
+ condition: pytest.__version__[0] != "17"
+ XFAIL xfail_demo.py::test_hello6
+ reason: reason
+ XFAIL xfail_demo.py::test_hello7
+
+ ======================== 7 xfailed in 0.12 seconds =========================
+
+.. _`skip/xfail with parametrize`:
+
+Skip/xfail with parametrize
+---------------------------
+
+It is possible to apply markers like skip and xfail to individual
+test instances when using parametrize:
+
+.. code-block:: python
+
+ import pytest
+
+ @pytest.mark.parametrize(("n", "expected"), [
+ (1, 2),
+ pytest.param(1, 0, marks=pytest.mark.xfail),
+ pytest.param(1, 3, marks=pytest.mark.xfail(reason="some bug")),
+ (2, 3),
+ (3, 4),
+ (4, 5),
+ pytest.param(10, 11, marks=pytest.mark.skipif(sys.version_info >= (3, 0), reason="py2k")),
+ ])
+ def test_increment(n, expected):
+ assert n + 1 == expected
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/talks.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/talks.rst
new file mode 100644
index 00000000000..bf593db4b4b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/talks.rst
@@ -0,0 +1,105 @@
+
+Talks and Tutorials
+==========================
+
+..
+ .. sidebar:: Next Open Trainings
+
+ `Professional Testing with Python
+ <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_,
+ 26-28 April 2017, Leipzig, Germany.
+
+.. _`funcargs`: funcargs.html
+
+Books
+---------------------------------------------
+
+- `Python Testing with pytest, by Brian Okken (2017)
+ <https://pragprog.com/book/bopytest/python-testing-with-pytest>`_.
+
+Talks and blog postings
+---------------------------------------------
+
+- `Pythonic testing, Igor Starikov (Russian, PyNsk, November 2016)
+ <https://www.youtube.com/watch?v=_92nfdd5nK8>`_.
+
+- `pytest - Rapid Simple Testing, Florian Bruhin, Swiss Python Summit 2016
+ <https://www.youtube.com/watch?v=rCBHkQ_LVIs>`_.
+
+- `Improve your testing with Pytest and Mock, Gabe Hollombe, PyCon SG 2015
+ <https://www.youtube.com/watch?v=RcN26hznmk4>`_.
+
+- `Introduction to pytest, Andreas Pelme, EuroPython 2014
+ <https://www.youtube.com/watch?v=LdVJj65ikRY>`_.
+
+- `Advanced Uses of py.test Fixtures, Floris Bruynooghe, EuroPython
+ 2014 <https://www.youtube.com/watch?v=IBC_dxr-4ps>`_.
+
+- `Why i use py.test and maybe you should too, Andy Todd, Pycon AU 2013
+ <https://www.youtube.com/watch?v=P-AhpukDIik>`_
+
+- `3-part blog series about pytest from @pydanny alias Daniel Greenfeld (January
+ 2014) <http://pydanny.com/pytest-no-boilerplate-testing.html>`_
+
+- `pytest: helps you write better Django apps, Andreas Pelme, DjangoCon
+ Europe 2014 <https://www.youtube.com/watch?v=aaArYVh6XSM>`_.
+
+- :ref:`fixtures`
+
+- `Testing Django Applications with pytest, Andreas Pelme, EuroPython
+ 2013 <https://www.youtube.com/watch?v=aUf8Fkb7TaY>`_.
+
+- `Testes pythonics com py.test, Vinicius Belchior Assef Neto, Plone
+ Conf 2013, Brazil <https://www.youtube.com/watch?v=QUKoq2K7bis>`_.
+
+- `Introduction to py.test fixtures, FOSDEM 2013, Floris Bruynooghe
+ <https://www.youtube.com/watch?v=bJhRW4eZMco>`_.
+
+- `pytest feature and release highlights, Holger Krekel (GERMAN, October 2013)
+ <http://pyvideo.org/video/2429/pytest-feature-and-new-release-highlights>`_
+
+- `pytest introduction from Brian Okken (January 2013)
+ <http://pythontesting.net/framework/pytest-introduction/>`_
+
+- pycon australia 2012 pytest talk from Brianna Laugher (`video <http://www.youtube.com/watch?v=DTNejE9EraI>`_, `slides <http://www.slideshare.net/pfctdayelise/funcargs-other-fun-with-pytest>`_, `code <https://gist.github.com/3386951>`_)
+- `pycon 2012 US talk video from Holger Krekel <http://www.youtube.com/watch?v=9LVqBQcFmyw>`_
+
+- `monkey patching done right`_ (blog post, consult `monkeypatch plugin`_ for up-to-date API)
+
+Test parametrization:
+
+- `generating parametrized tests with fixtures`_.
+- `test generators and cached setup`_
+- `parametrizing tests, generalized`_ (blog post)
+- `putting test-hooks into local or global plugins`_ (blog post)
+
+Assertion introspection:
+
+- `(07/2011) Behind the scenes of pytest's new assertion rewriting
+ <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_
+
+Distributed testing:
+
+- `simultaneously test your code on all platforms`_ (blog entry)
+
+Plugin specific examples:
+
+- `skipping slow tests by default in pytest`_ (blog entry)
+
+- `many examples in the docs for plugins`_
+
+.. _`skipping slow tests by default in pytest`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
+.. _`many examples in the docs for plugins`: plugins.html
+.. _`monkeypatch plugin`: monkeypatch.html
+.. _`application setup in test functions with fixtures`: fixture.html#interdependent-fixtures
+.. _`simultaneously test your code on all platforms`: http://tetamap.wordpress.com/2009/03/23/new-simultanously-test-your-code-on-all-platforms/
+.. _`monkey patching done right`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
+.. _`putting test-hooks into local or global plugins`: http://tetamap.wordpress.com/2009/05/14/putting-test-hooks-into-local-and-global-plugins/
+.. _`parametrizing tests, generalized`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
+.. _`generating parametrized tests with fixtures`: parametrize.html#test-generators
+.. _`test generators and cached setup`: http://bruynooghe.blogspot.com/2010/06/pytest-test-generators-and-cached-setup.html
+
+
+
+
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/attic.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/attic.rst
new file mode 100644
index 00000000000..06944661cb5
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/attic.rst
@@ -0,0 +1,117 @@
+===============================================
+ATTIC documentation
+===============================================
+
+XXX REVIEW and remove the below XXX
+
+Customizing the testing process
+===============================
+
+writing conftest.py files
+-----------------------------------
+
+You may put conftest.py files containing project-specific
+configuration in your project's root directory, it's usually
+best to put it just into the same directory level as your
+topmost ``__init__.py``. In fact, ``pytest`` performs
+an "upwards" search starting from the directory that you specify
+to be tested and will lookup configuration values right-to-left.
+You may have options that reside e.g. in your home directory
+but note that project specific settings will be considered
+first. There is a flag that helps you debugging your
+conftest.py configurations::
+
+ pytest --trace-config
+
+
+customizing the collecting and running process
+-----------------------------------------------
+
+To introduce different test items you can create
+one or more ``conftest.py`` files in your project.
+When the collection process traverses directories
+and modules the default collectors will produce
+custom Collectors and Items if they are found
+in a local ``conftest.py`` file.
+
+
+Customizing the collection process in a module
+----------------------------------------------
+
+If you have a module where you want to take responsibility for
+collecting your own test Items and possibly even for executing
+a test then you can provide `generative tests`_ that yield
+callables and possibly arguments as a tuple. This is especially
+useful for calling application test machinery with different
+parameter sets but counting each of the calls as a separate
+tests.
+
+.. _`generative tests`: features.html#generative-tests
+
+The other extension possibility is about
+specifying a custom test ``Item`` class which
+is responsible for setting up and executing an underlying
+test. Or you can extend the collection process for a whole
+directory tree by putting Items in a ``conftest.py`` configuration file.
+The collection process dynamically consults the *chain of conftest.py*
+modules to determine collectors and items at ``Directory``, ``Module``,
+``Class``, ``Function`` or ``Generator`` level respectively.
+
+Customizing execution of Items and Functions
+----------------------------------------------------
+
+- ``pytest.Function`` test items control execution
+ of a test function through its ``function.runtest()`` method.
+ This method is responsible for performing setup and teardown
+ ("Test Fixtures") for a test Function.
+
+- ``Function.execute(target, *args)`` methods are invoked by
+ the default ``Function.run()`` to actually execute a python
+ function with the given (usually empty set of) arguments.
+
+.. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev
+
+
+.. _`test generators`: funcargs.html#test-generators
+
+.. _`generative tests`:
+
+generative tests: yielding parametrized tests
+====================================================
+
+Deprecated since 1.0 in favour of `test generators`_.
+
+*Generative tests* are test methods that are *generator functions* which
+``yield`` callables and their arguments. This is useful for running a
+test function multiple times against different parameters. Example::
+
+ def test_generative():
+ for x in (42,17,49):
+ yield check, x
+
+ def check(arg):
+ assert arg % 7 == 0 # second generated tests fails!
+
+Note that ``test_generative()`` will cause three tests
+to get run, notably ``check(42)``, ``check(17)`` and ``check(49)``
+of which the middle one will obviously fail.
+
+To make it easier to distinguish the generated tests it is possible to specify an explicit name for them, like for example::
+
+ def test_generative():
+ for x in (42,17,49):
+ yield "case %d" % x, check, x
+
+
+disabling a test class
+----------------------
+
+If you want to disable a complete test class you
+can set the class-level attribute ``disabled``.
+For example, in order to avoid running some tests on Win32::
+
+ class TestPosixOnly(object):
+ disabled = sys.platform == 'win32'
+
+ def test_xxx(self):
+ ...
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/config.html b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/config.html
index cba5a46f982..cba5a46f982 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/config.html
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/config.html
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/dist.html b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/dist.html
index e328550a85c..e328550a85c 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/dist.html
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/dist.html
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/extend.html b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/extend.html
index cba5a46f982..cba5a46f982 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/extend.html
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/extend.html
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/index.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/index.rst
index 1a3b5a54da0..1a3b5a54da0 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/index.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/index.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/mission.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/mission.rst
new file mode 100644
index 00000000000..51c252dc0d8
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/mission.rst
@@ -0,0 +1,13 @@
+
+Mission
+====================================
+
+``pytest`` strives to make testing a fun and no-boilerplate effort.
+
+The tool is distributed as a `pytest` package. Its project independent
+``pytest`` command line tool helps you to:
+
+* rapidly collect and run tests
+* run unit- or doctests, functional or integration tests
+* distribute tests to multiple environments
+* use local or global plugins for custom test types and setup
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/cov.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/cov.rst
new file mode 100644
index 00000000000..541c7ef9479
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/cov.rst
@@ -0,0 +1,230 @@
+
+produce code coverage reports using the 'coverage' package, including support for distributed testing.
+======================================================================================================
+
+
+.. contents::
+ :local:
+
+This plugin produces coverage reports. It supports centralised testing and distributed testing in
+both load and each modes. It also supports coverage of subprocesses.
+
+All features offered by the coverage package should be available, either through pytest-cov or
+through coverage's config file.
+
+
+Installation
+------------
+
+The `pytest-cov`_ package may be installed with pip or easy_install::
+
+ pip install pytest-cov
+ easy_install pytest-cov
+
+.. _`pytest-cov`: http://pypi.python.org/pypi/pytest-cov/
+
+
+Uninstallation
+--------------
+
+Uninstalling packages is supported by pip::
+
+ pip uninstall pytest-cov
+
+However easy_install does not provide an uninstall facility.
+
+.. IMPORTANT::
+
+ Ensure that you manually delete the init_covmain.pth file in your
+ site-packages directory.
+
+ This file starts coverage collection of subprocesses if appropriate during
+ site initialization at python startup.
+
+
+Usage
+-----
+
+Centralised Testing
+~~~~~~~~~~~~~~~~~~~
+
+Centralised testing will report on the combined coverage of the main process and all of it's
+subprocesses.
+
+Running centralised testing::
+
+ pytest --cov myproj tests/
+
+Shows a terminal report::
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Distributed Testing: Load
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Distributed testing with dist mode set to load will report on the combined coverage of all slaves.
+The slaves may be spread out over any number of hosts and each slave may be located anywhere on the
+file system. Each slave will have it's subprocesses measured.
+
+Running distributed testing with dist mode set to load::
+
+ pytest --cov myproj -n 2 tests/
+
+Shows a terminal report::
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Again but spread over different hosts and different directories::
+
+ pytest --cov myproj --dist load
+ --tx ssh=memedough@host1//chdir=testenv1
+ --tx ssh=memedough@host2//chdir=/tmp/testenv2//python=/tmp/env1/bin/python
+ --rsyncdir myproj --rsyncdir tests --rsync examples
+ tests/
+
+Shows a terminal report::
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Distributed Testing: Each
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Distributed testing with dist mode set to each will report on the combined coverage of all slaves.
+Since each slave is running all tests this allows generating a combined coverage report for multiple
+environments.
+
+Running distributed testing with dist mode set to each::
+
+ pytest --cov myproj --dist each
+ --tx popen//chdir=/tmp/testenv3//python=/usr/local/python27/bin/python
+ --tx ssh=memedough@host2//chdir=/tmp/testenv4//python=/tmp/env2/bin/python
+ --rsyncdir myproj --rsyncdir tests --rsync examples
+ tests/
+
+Shows a terminal report::
+
+ ---------------------------------------- coverage ----------------------------------------
+ platform linux2, python 2.6.5-final-0
+ platform linux2, python 2.7.0-final-0
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Reporting
+---------
+
+It is possible to generate any combination of the reports for a single test run.
+
+The available reports are terminal (with or without missing line numbers shown), HTML, XML and
+annotated source code.
+
+The terminal report without line numbers (default)::
+
+ pytest --cov-report term --cov myproj tests/
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+The terminal report with line numbers::
+
+ pytest --cov-report term-missing --cov myproj tests/
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover Missing
+ --------------------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94% 24-26, 99, 149, 233-236, 297-298, 369-370
+ myproj/feature4286 94 7 92% 183-188, 197
+ --------------------------------------------------
+ TOTAL 353 20 94%
+
+
+The remaining three reports output to files without showing anything on the terminal (useful for
+when the output is going to a continuous integration server)::
+
+ pytest --cov-report html --cov-report xml --cov-report annotate --cov myproj tests/
+
+
+Coverage Data File
+------------------
+
+The data file is erased at the beginning of testing to ensure clean data for each test run.
+
+The data file is left at the end of testing so that it is possible to use normal coverage tools to
+examine it.
+
+
+Limitations
+-----------
+
+For distributed testing the slaves must have the pytest-cov package installed. This is needed since
+the plugin must be registered through setuptools / distribute for pytest to start the plugin on the
+slave.
+
+For subprocess measurement environment variables must make it from the main process to the
+subprocess. The python used by the subprocess must have pytest-cov installed. The subprocess must
+do normal site initialization so that the environment variables can be detected and coverage
+started.
+
+
+Acknowledgments
+----------------
+
+Holger Krekel for pytest with its distributed testing support.
+
+Ned Batchelder for coverage and its ability to combine the coverage results of parallel runs.
+
+Whilst this plugin has been built fresh from the ground up to support distributed testing it has
+been influenced by the work done on pytest-coverage (Ross Lawley, James Mills, Holger Krekel) and
+nose-cover (Jason Pellerin) which are other coverage plugins for pytest and nose respectively.
+
+No doubt others have contributed to these tools as well.
+
+command line options
+--------------------
+
+
+``--cov=path``
+ measure coverage for filesystem path (multi-allowed)
+``--cov-report=type``
+ type of report to generate: term, term-missing, annotate, html, xml (multi-allowed)
+``--cov-config=path``
+ config file for coverage, default: .coveragerc
+
+.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/coverage.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/coverage.rst
new file mode 100644
index 00000000000..71139d008ba
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/coverage.rst
@@ -0,0 +1,51 @@
+
+Write and report coverage data with the 'coverage' package.
+===========================================================
+
+
+.. contents::
+ :local:
+
+Note: Original code by Ross Lawley.
+
+Install
+--------------
+
+Use pip to (un)install::
+
+ pip install pytest-coverage
+ pip uninstall pytest-coverage
+
+or alternatively use easy_install to install::
+
+ easy_install pytest-coverage
+
+
+Usage
+-------------
+
+To get full test coverage reports for a particular package type::
+
+ pytest --cover-report=report
+
+command line options
+--------------------
+
+
+``--cover=COVERPACKAGES``
+ (multi allowed) only include info from specified package.
+``--cover-report=REPORT_TYPE``
+ html: Directory for html output.
+ report: Output a text report.
+ annotate: Annotate your source code for which lines were executed and which were not.
+ xml: Output an xml report compatible with the cobertura plugin for hudson.
+``--cover-directory=DIRECTORY``
+ Directory for the reports (html / annotate results) defaults to ./coverage
+``--cover-xml-file=XML_FILE``
+ File for the xml report defaults to ./coverage.xml
+``--cover-show-missing``
+ Show missing files
+``--cover-ignore-errors=IGNORE_ERRORS``
+ Ignore errors of finding source files for code.
+
+.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/django.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/django.rst
index 061497b385e..061497b385e 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/django.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/django.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/figleaf.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/figleaf.rst
new file mode 100644
index 00000000000..0c1603ade9f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/figleaf.rst
@@ -0,0 +1,44 @@
+
+report test coverage using the 'figleaf' package.
+=================================================
+
+
+.. contents::
+ :local:
+
+Install
+---------------
+
+To install the plugin issue::
+
+ easy_install pytest-figleaf # or
+ pip install pytest-figleaf
+
+and if you are using pip you can also uninstall::
+
+ pip uninstall pytest-figleaf
+
+
+Usage
+---------------
+
+After installation you can simply type::
+
+ pytest --figleaf [...]
+
+to enable figleaf coverage in your test run. A default ".figleaf" data file
+and "html" directory will be created. You can use command line options
+to control where data and html files are created.
+
+command line options
+--------------------
+
+
+``--figleaf``
+ trace python coverage with figleaf and write HTML for files below the current working dir
+``--fig-data=dir``
+ set tracing file, default: ".figleaf".
+``--fig-html=dir``
+ set html reporting dir, default "html".
+
+.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/helpconfig.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/helpconfig.rst
new file mode 100644
index 00000000000..326b75c4552
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/helpconfig.rst
@@ -0,0 +1,36 @@
+
+provide version info, conftest/environment config names.
+========================================================
+
+
+.. contents::
+ :local:
+
+
+
+command line options
+--------------------
+
+
+``--version``
+ display py lib version and import information.
+``-p name``
+ early-load given plugin (multi-allowed).
+``--trace-config``
+ trace considerations of conftest.py files.
+``--debug``
+ generate and show internal debugging information.
+``--help-config``
+ show available conftest.py and ENV-variable names.
+
+Start improving this plugin in 30 seconds
+=========================================
+
+
+1. Download `pytest_helpconfig.py`_ plugin source code
+2. put it somewhere as ``pytest_helpconfig.py`` into your import path
+3. a subsequent ``pytest`` run will use your local version
+
+Checkout customize_, other plugins_ or `get in contact`_.
+
+.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/index.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/index.rst
index 853a4dce681..853a4dce681 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/index.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/index.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/links.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/links.rst
new file mode 100644
index 00000000000..6dec2b4848a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/links.rst
@@ -0,0 +1,45 @@
+.. _`helpconfig`: helpconfig.html
+.. _`pytest_recwarn.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_recwarn.py
+.. _`unittest`: unittest.html
+.. _`pytest_monkeypatch.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_monkeypatch.py
+.. _`pastebin`: pastebin.html
+.. _`skipping`: skipping.html
+.. _`plugins`: index.html
+.. _`mark`: mark.html
+.. _`tmpdir`: tmpdir.html
+.. _`pytest_doctest.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_doctest.py
+.. _`capture`: capture.html
+.. _`pytest_nose.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_nose.py
+.. _`pytest_restdoc.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_restdoc.py
+.. _`restdoc`: restdoc.html
+.. _`xdist`: xdist.html
+.. _`pytest_pastebin.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_pastebin.py
+.. _`pytest_tmpdir.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_tmpdir.py
+.. _`terminal`: terminal.html
+.. _`pytest_hooklog.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_hooklog.py
+.. _`capturelog`: capturelog.html
+.. _`junitxml`: junitxml.html
+.. _`pytest_skipping.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_skipping.py
+.. _`checkout the pytest development version`: ../../install.html#checkout
+.. _`pytest_helpconfig.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_helpconfig.py
+.. _`oejskit`: oejskit.html
+.. _`doctest`: doctest.html
+.. _`pytest_mark.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_mark.py
+.. _`get in contact`: ../../contact.html
+.. _`pytest_capture.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_capture.py
+.. _`figleaf`: figleaf.html
+.. _`customize`: ../customize.html
+.. _`hooklog`: hooklog.html
+.. _`pytest_terminal.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_terminal.py
+.. _`recwarn`: recwarn.html
+.. _`pytest_pdb.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_pdb.py
+.. _`monkeypatch`: monkeypatch.html
+.. _`coverage`: coverage.html
+.. _`resultlog`: resultlog.html
+.. _`cov`: cov.html
+.. _`pytest_junitxml.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_junitxml.py
+.. _`django`: django.html
+.. _`pytest_unittest.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_unittest.py
+.. _`nose`: nose.html
+.. _`pytest_resultlog.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_resultlog.py
+.. _`pdb`: pdb.html
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/nose.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/nose.rst
new file mode 100644
index 00000000000..9eeae5ff697
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/nose.rst
@@ -0,0 +1,56 @@
+
+nose-compatibility plugin: allow to run nose test suites natively.
+==================================================================
+
+
+.. contents::
+ :local:
+
+This is an experimental plugin for allowing to run tests written
+in 'nosetests' style with ``pytest``.
+
+Usage
+-------------
+
+type::
+
+ pytest # instead of 'nosetests'
+
+and you should be able to run nose style tests and at the same
+time can make full use of pytest's capabilities.
+
+Supported nose Idioms
+----------------------
+
+* setup and teardown at module/class/method level
+* SkipTest exceptions and markers
+* setup/teardown decorators
+* yield-based tests and their setup
+* general usage of nose utilities
+
+Unsupported idioms / issues
+----------------------------------
+
+- nose-style doctests are not collected and executed correctly,
+ also fixtures don't work.
+
+- no nose-configuration is recognized
+
+If you find other issues or have suggestions please run::
+
+ pytest --pastebin=all
+
+and send the resulting URL to a ``pytest`` contact channel,
+at best to the mailing list.
+
+Start improving this plugin in 30 seconds
+=========================================
+
+
+1. Download `pytest_nose.py`_ plugin source code
+2. put it somewhere as ``pytest_nose.py`` into your import path
+3. a subsequent ``pytest`` run will use your local version
+
+Checkout customize_, other plugins_ or `get in contact`_.
+
+.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/oejskit.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/oejskit.rst
index 4995aa17c72..4995aa17c72 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/plugin/oejskit.rst
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/oejskit.rst
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/terminal.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/terminal.rst
new file mode 100644
index 00000000000..e07d4f72183
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/terminal.rst
@@ -0,0 +1,38 @@
+
+Implements terminal reporting of the full testing process.
+==========================================================
+
+
+.. contents::
+ :local:
+
+This is a good source for looking at the various reporting hooks.
+
+command line options
+--------------------
+
+
+``-v, --verbose``
+ increase verbosity.
+``-r chars``
+ show extra test summary info as specified by chars (f)ailed, (s)skipped, (x)failed, (X)passed.
+``-l, --showlocals``
+ show locals in tracebacks (disabled by default).
+``--tb=style``
+ traceback print mode (long/short/line/no).
+``--full-trace``
+ don't cut any tracebacks (default is to cut).
+``--fixtures``
+ show available function arguments, sorted by plugin
+
+Start improving this plugin in 30 seconds
+=========================================
+
+
+1. Download `pytest_terminal.py`_ plugin source code
+2. put it somewhere as ``pytest_terminal.py`` into your import path
+3. a subsequent ``pytest`` run will use your local version
+
+Checkout customize_, other plugins_ or `get in contact`_.
+
+.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/xdist.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/xdist.rst
new file mode 100644
index 00000000000..506d240aee1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/plugin/xdist.rst
@@ -0,0 +1,172 @@
+
+loop on failing tests, distribute test runs to CPUs and hosts.
+==============================================================
+
+
+.. contents::
+ :local:
+
+The `pytest-xdist`_ plugin extends ``pytest`` with some unique
+test execution modes:
+
+* Looponfail: run your tests repeatedly in a subprocess. After each run
+ ``pytest`` waits until a file in your project changes and then re-runs the
+ previously failing tests. This is repeated until all tests pass after which
+ again a full run is performed.
+
+* Load-balancing: if you have multiple CPUs or hosts you can use
+ those for a combined test run. This allows to speed up
+ development or to use special resources of remote machines.
+
+* Multi-Platform coverage: you can specify different Python interpreters
+ or different platforms and run tests in parallel on all of them.
+
+Before running tests remotely, ``pytest`` efficiently synchronizes your
+program source code to the remote place. All test results
+are reported back and displayed to your local test session.
+You may specify different Python versions and interpreters.
+
+.. _`pytest-xdist`: http://pypi.python.org/pypi/pytest-xdist
+
+Usage examples
+---------------------
+
+Speed up test runs by sending tests to multiple CPUs
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+To send tests to multiple CPUs, type::
+
+ pytest -n NUM
+
+Especially for longer running tests or tests requiring
+a lot of IO this can lead to considerable speed ups.
+
+
+Running tests in a Python subprocess
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+To instantiate a python2.4 sub process and send tests to it, you may type::
+
+ pytest -d --tx popen//python=python2.4
+
+This will start a subprocess which is run with the "python2.4"
+Python interpreter, found in your system binary lookup path.
+
+If you prefix the --tx option value like this::
+
+ --tx 3*popen//python=python2.4
+
+then three subprocesses would be created and tests
+will be load-balanced across these three processes.
+
+
+Sending tests to remote SSH accounts
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Suppose you have a package ``mypkg`` which contains some
+tests that you can successfully run locally. And you
+have a ssh-reachable machine ``myhost``. Then
+you can ad-hoc distribute your tests by typing::
+
+ pytest -d --tx ssh=myhostpopen --rsyncdir mypkg mypkg
+
+This will synchronize your ``mypkg`` package directory
+to a remote ssh account and then locally collect tests
+and send them to remote places for execution.
+
+You can specify multiple ``--rsyncdir`` directories
+to be sent to the remote side.
+
+**NOTE:** For ``pytest`` to collect and send tests correctly
+you not only need to make sure all code and tests
+directories are rsynced, but that any test (sub) directory
+also has an ``__init__.py`` file because internally
+``pytest`` references tests using their fully qualified python
+module path. **You will otherwise get strange errors**
+during setup of the remote side.
+
+Sending tests to remote Socket Servers
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Download the single-module `socketserver.py`_ Python program
+and run it like this::
+
+ python socketserver.py
+
+It will tell you that it starts listening on the default
+port. You can now on your home machine specify this
+new socket host with something like this::
+
+ pytest -d --tx socket=192.168.1.102:8888 --rsyncdir mypkg mypkg
+
+
+.. _`atonce`:
+
+Running tests on many platforms at once
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+The basic command to run tests on multiple platforms is::
+
+ pytest --dist=each --tx=spec1 --tx=spec2
+
+If you specify a windows host, an OSX host and a Linux
+environment this command will send each tests to all
+platforms - and report back failures from all platforms
+at once. The specifications strings use the `xspec syntax`_.
+
+.. _`xspec syntax`: http://codespeak.net/execnet/trunk/basics.html#xspec
+
+.. _`socketserver.py`: http://codespeak.net/svn/py/dist/py/execnet/script/socketserver.py
+
+.. _`execnet`: http://codespeak.net/execnet
+
+Specifying test exec environments in a conftest.py
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Instead of specifying command line options, you can
+put options values in a ``conftest.py`` file like this::
+
+ option_tx = ['ssh=myhost//python=python2.7', 'popen//python=python2.7']
+ option_dist = True
+
+Any commandline ``--tx`` specifications will add to the list of
+available execution environments.
+
+Specifying "rsync" dirs in a conftest.py
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+In your ``mypkg/conftest.py`` you may specify directories to synchronise
+or to exclude::
+
+ rsyncdirs = ['.', '../plugins']
+ rsyncignore = ['_cache']
+
+These directory specifications are relative to the directory
+where the ``conftest.py`` is found.
+
+command line options
+--------------------
+
+
+``-f, --looponfail``
+ run tests in subprocess, wait for modified files and re-run failing test set until all pass.
+``-n numprocesses``
+ shortcut for '--dist=load --tx=NUM*popen'
+``--boxed``
+ box each test run in a separate process (unix)
+``--dist=distmode``
+ set mode for distributing tests to exec environments.
+
+ each: send each test to each available environment.
+
+ load: send each test to one available environment so it is run only once.
+
+ (default) no: run tests inprocess, don't distribute.
+``--tx=xspec``
+ add a test execution environment. some examples: --tx popen//python=python2.7 --tx socket=192.168.1.102:8888 --tx ssh=user@codespeak.net//chdir=testcache
+``-d``
+ load-balance tests. shortcut for '--dist=load'
+``--rsyncdir=dir1``
+ add directory for rsyncing to remote tx nodes.
+
+.. include:: links.txt
diff --git a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/test.html b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/test.html
index 7d00f718a67..7d00f718a67 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/doc/en/test/test.html
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/test/test.html
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/tmpdir.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/tmpdir.rst
new file mode 100644
index 00000000000..b8174484e1d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/tmpdir.rst
@@ -0,0 +1,111 @@
+
+.. _`tmpdir handling`:
+.. _tmpdir:
+
+Temporary directories and files
+================================================
+
+The 'tmpdir' fixture
+--------------------
+
+You can use the ``tmpdir`` fixture which will
+provide a temporary directory unique to the test invocation,
+created in the `base temporary directory`_.
+
+``tmpdir`` is a `py.path.local`_ object which offers ``os.path`` methods
+and more. Here is an example test usage::
+
+ # content of test_tmpdir.py
+ import os
+ def test_create_file(tmpdir):
+ p = tmpdir.mkdir("sub").join("hello.txt")
+ p.write("content")
+ assert p.read() == "content"
+ assert len(tmpdir.listdir()) == 1
+ assert 0
+
+Running this would result in a passed test except for the last
+``assert 0`` line which we use to look at values::
+
+ $ pytest test_tmpdir.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 1 item
+
+ test_tmpdir.py F [100%]
+
+ ================================= FAILURES =================================
+ _____________________________ test_create_file _____________________________
+
+ tmpdir = local('PYTEST_TMPDIR/test_create_file0')
+
+ def test_create_file(tmpdir):
+ p = tmpdir.mkdir("sub").join("hello.txt")
+ p.write("content")
+ assert p.read() == "content"
+ assert len(tmpdir.listdir()) == 1
+ > assert 0
+ E assert 0
+
+ test_tmpdir.py:7: AssertionError
+ ========================= 1 failed in 0.12 seconds =========================
+
+The 'tmpdir_factory' fixture
+----------------------------
+
+.. versionadded:: 2.8
+
+The ``tmpdir_factory`` is a session-scoped fixture which can be used
+to create arbitrary temporary directories from any other fixture or test.
+
+For example, suppose your test suite needs a large image on disk, which is
+generated procedurally. Instead of computing the same image for each test
+that uses it into its own ``tmpdir``, you can generate it once per-session
+to save time:
+
+.. code-block:: python
+
+ # contents of conftest.py
+ import pytest
+
+ @pytest.fixture(scope='session')
+ def image_file(tmpdir_factory):
+ img = compute_expensive_image()
+ fn = tmpdir_factory.mktemp('data').join('img.png')
+ img.save(str(fn))
+ return fn
+
+ # contents of test_image.py
+ def test_histogram(image_file):
+ img = load_image(image_file)
+ # compute and test histogram
+
+``tmpdir_factory`` instances have the following methods:
+
+.. currentmodule:: _pytest.tmpdir
+
+.. automethod:: TempdirFactory.mktemp
+.. automethod:: TempdirFactory.getbasetemp
+
+.. _`base temporary directory`:
+
+The default base temporary directory
+-----------------------------------------------
+
+Temporary directories are by default created as sub-directories of
+the system temporary directory. The base name will be ``pytest-NUM`` where
+``NUM`` will be incremented with each test run. Moreover, entries older
+than 3 temporary directories will be removed.
+
+You can override the default temporary directory setting like this::
+
+ pytest --basetemp=mydir
+
+When distributing tests on the local machine, ``pytest`` takes care to
+configure a basetemp directory for the sub processes such that all temporary
+data lands below a single per-test run basetemp directory.
+
+.. _`py.path.local`: http://py.rtfd.org/en/latest/path.html
+
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/unittest.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/unittest.rst
new file mode 100644
index 00000000000..b44bda44fa8
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/unittest.rst
@@ -0,0 +1,245 @@
+
+.. _`unittest.TestCase`:
+.. _`unittest`:
+
+unittest.TestCase Support
+=========================
+
+``pytest`` supports running Python ``unittest``-based tests out of the box.
+It's meant for leveraging existing ``unittest``-based test suites
+to use pytest as a test runner and also allow to incrementally adapt
+the test suite to take full advantage of pytest's features.
+
+To run an existing ``unittest``-style test suite using ``pytest``, type::
+
+ pytest tests
+
+
+pytest will automatically collect ``unittest.TestCase`` subclasses and
+their ``test`` methods in ``test_*.py`` or ``*_test.py`` files.
+
+Almost all ``unittest`` features are supported:
+
+* ``@unittest.skip`` style decorators;
+* ``setUp/tearDown``;
+* ``setUpClass/tearDownClass()``;
+
+.. _`load_tests protocol`: https://docs.python.org/3/library/unittest.html#load-tests-protocol
+.. _`setUpModule/tearDownModule`: https://docs.python.org/3/library/unittest.html#setupmodule-and-teardownmodule
+.. _`subtests`: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests
+
+Up to this point pytest does not have support for the following features:
+
+* `load_tests protocol`_;
+* `setUpModule/tearDownModule`_;
+* `subtests`_;
+
+Benefits out of the box
+-----------------------
+
+By running your test suite with pytest you can make use of several features,
+in most cases without having to modify existing code:
+
+* Obtain :ref:`more informative tracebacks <tbreportdemo>`;
+* :ref:`stdout and stderr <captures>` capturing;
+* :ref:`Test selection options <select-tests>` using ``-k`` and ``-m`` flags;
+* :ref:`maxfail`;
+* :ref:`--pdb <pdb-option>` command-line option for debugging on test failures
+ (see :ref:`note <pdb-unittest-note>` below);
+* Distribute tests to multiple CPUs using the `pytest-xdist <http://pypi.python.org/pypi/pytest-xdist>`_ plugin;
+* Use :ref:`plain assert-statements <assert>` instead of ``self.assert*`` functions (`unittest2pytest
+ <https://pypi.python.org/pypi/unittest2pytest/>`__ is immensely helpful in this);
+
+
+pytest features in ``unittest.TestCase`` subclasses
+---------------------------------------------------
+
+The following pytest features work in ``unittest.TestCase`` subclasses:
+
+* :ref:`Marks <mark>`: :ref:`skip <skip>`, :ref:`skipif <skipif>`, :ref:`xfail <xfail>`;
+* :ref:`Auto-use fixtures <mixing-fixtures>`;
+
+The following pytest features **do not** work, and probably
+never will due to different design philosophies:
+
+* :ref:`Fixtures <fixture>` (except for ``autouse`` fixtures, see :ref:`below <mixing-fixtures>`);
+* :ref:`Parametrization <parametrize>`;
+* :ref:`Custom hooks <writing-plugins>`;
+
+
+Third party plugins may or may not work well, depending on the plugin and the test suite.
+
+.. _mixing-fixtures:
+
+Mixing pytest fixtures into ``unittest.TestCase`` subclasses using marks
+------------------------------------------------------------------------
+
+Running your unittest with ``pytest`` allows you to use its
+:ref:`fixture mechanism <fixture>` with ``unittest.TestCase`` style
+tests. Assuming you have at least skimmed the pytest fixture features,
+let's jump-start into an example that integrates a pytest ``db_class``
+fixture, setting up a class-cached database object, and then reference
+it from a unittest-style test::
+
+ # content of conftest.py
+
+ # we define a fixture function below and it will be "used" by
+ # referencing its name from tests
+
+ import pytest
+
+ @pytest.fixture(scope="class")
+ def db_class(request):
+ class DummyDB(object):
+ pass
+ # set a class attribute on the invoking test context
+ request.cls.db = DummyDB()
+
+This defines a fixture function ``db_class`` which - if used - is
+called once for each test class and which sets the class-level
+``db`` attribute to a ``DummyDB`` instance. The fixture function
+achieves this by receiving a special ``request`` object which gives
+access to :ref:`the requesting test context <request-context>` such
+as the ``cls`` attribute, denoting the class from which the fixture
+is used. This architecture de-couples fixture writing from actual test
+code and allows re-use of the fixture by a minimal reference, the fixture
+name. So let's write an actual ``unittest.TestCase`` class using our
+fixture definition::
+
+ # content of test_unittest_db.py
+
+ import unittest
+ import pytest
+
+ @pytest.mark.usefixtures("db_class")
+ class MyTest(unittest.TestCase):
+ def test_method1(self):
+ assert hasattr(self, "db")
+ assert 0, self.db # fail for demo purposes
+
+ def test_method2(self):
+ assert 0, self.db # fail for demo purposes
+
+The ``@pytest.mark.usefixtures("db_class")`` class-decorator makes sure that
+the pytest fixture function ``db_class`` is called once per class.
+Due to the deliberately failing assert statements, we can take a look at
+the ``self.db`` values in the traceback::
+
+ $ pytest test_unittest_db.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 2 items
+
+ test_unittest_db.py FF [100%]
+
+ ================================= FAILURES =================================
+ ___________________________ MyTest.test_method1 ____________________________
+
+ self = <test_unittest_db.MyTest testMethod=test_method1>
+
+ def test_method1(self):
+ assert hasattr(self, "db")
+ > assert 0, self.db # fail for demo purposes
+ E AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef>
+ E assert 0
+
+ test_unittest_db.py:9: AssertionError
+ ___________________________ MyTest.test_method2 ____________________________
+
+ self = <test_unittest_db.MyTest testMethod=test_method2>
+
+ def test_method2(self):
+ > assert 0, self.db # fail for demo purposes
+ E AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef>
+ E assert 0
+
+ test_unittest_db.py:12: AssertionError
+ ========================= 2 failed in 0.12 seconds =========================
+
+This default pytest traceback shows that the two test methods
+share the same ``self.db`` instance which was our intention
+when writing the class-scoped fixture function above.
+
+
+Using autouse fixtures and accessing other fixtures
+---------------------------------------------------
+
+Although it's usually better to explicitly declare use of fixtures you need
+for a given test, you may sometimes want to have fixtures that are
+automatically used in a given context. After all, the traditional
+style of unittest-setup mandates the use of this implicit fixture writing
+and chances are, you are used to it or like it.
+
+You can flag fixture functions with ``@pytest.fixture(autouse=True)``
+and define the fixture function in the context where you want it used.
+Let's look at an ``initdir`` fixture which makes all test methods of a
+``TestCase`` class execute in a temporary directory with a
+pre-initialized ``samplefile.ini``. Our ``initdir`` fixture itself uses
+the pytest builtin :ref:`tmpdir <tmpdir>` fixture to delegate the
+creation of a per-test temporary directory::
+
+ # content of test_unittest_cleandir.py
+ import pytest
+ import unittest
+
+ class MyTest(unittest.TestCase):
+
+ @pytest.fixture(autouse=True)
+ def initdir(self, tmpdir):
+ tmpdir.chdir() # change to pytest-provided temporary directory
+ tmpdir.join("samplefile.ini").write("# testdata")
+
+ def test_method(self):
+ with open("samplefile.ini") as f:
+ s = f.read()
+ assert "testdata" in s
+
+Due to the ``autouse`` flag the ``initdir`` fixture function will be
+used for all methods of the class where it is defined. This is a
+shortcut for using a ``@pytest.mark.usefixtures("initdir")`` marker
+on the class like in the previous example.
+
+Running this test module ...::
+
+ $ pytest -q test_unittest_cleandir.py
+ . [100%]
+ 1 passed in 0.12 seconds
+
+... gives us one passed test because the ``initdir`` fixture function
+was executed ahead of the ``test_method``.
+
+.. note::
+
+ ``unittest.TestCase`` methods cannot directly receive fixture
+ arguments as implementing that is likely to inflict
+ on the ability to run general unittest.TestCase test suites.
+
+ The above ``usefixtures`` and ``autouse`` examples should help to mix in
+ pytest fixtures into unittest suites.
+
+ You can also gradually move away from subclassing from ``unittest.TestCase`` to *plain asserts*
+ and then start to benefit from the full pytest feature set step by step.
+
+.. _pdb-unittest-note:
+
+.. note::
+
+ Running tests from ``unittest.TestCase`` subclasses with ``--pdb`` will
+ disable tearDown and cleanup methods for the case that an Exception
+ occurs. This allows proper post mortem debugging for all applications
+ which have significant logic in their tearDown machinery. However,
+ supporting this feature has the following side effect: If people
+ overwrite ``unittest.TestCase`` ``__call__`` or ``run``, they need to
+ to overwrite ``debug`` in the same way (this is also true for standard
+ unittest).
+
+.. note::
+
+ Due to architectural differences between the two frameworks, setup and
+ teardown for ``unittest``-based tests is performed during the ``call`` phase
+ of testing instead of in ``pytest``'s standard ``setup`` and ``teardown``
+ stages. This can be important to understand in some situations, particularly
+ when reasoning about errors. For example, if a ``unittest``-based suite
+ exhibits errors during setup, ``pytest`` will report no errors during its
+ ``setup`` phase and will instead raise the error during ``call``.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/usage.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/usage.rst
new file mode 100644
index 00000000000..6091db8be38
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/usage.rst
@@ -0,0 +1,392 @@
+
+.. _usage:
+
+Usage and Invocations
+==========================================
+
+
+.. _cmdline:
+
+Calling pytest through ``python -m pytest``
+-----------------------------------------------------
+
+.. versionadded:: 2.0
+
+You can invoke testing through the Python interpreter from the command line::
+
+ python -m pytest [...]
+
+This is almost equivalent to invoking the command line script ``pytest [...]``
+directly, except that calling via ``python`` will also add the current directory to ``sys.path``.
+
+Possible exit codes
+--------------------------------------------------------------
+
+Running ``pytest`` can result in six different exit codes:
+
+:Exit code 0: All tests were collected and passed successfully
+:Exit code 1: Tests were collected and run but some of the tests failed
+:Exit code 2: Test execution was interrupted by the user
+:Exit code 3: Internal error happened while executing tests
+:Exit code 4: pytest command line usage error
+:Exit code 5: No tests were collected
+
+Getting help on version, option names, environment variables
+--------------------------------------------------------------
+
+::
+
+ pytest --version # shows where pytest was imported from
+ pytest --fixtures # show available builtin function arguments
+ pytest -h | --help # show help on command line and config file options
+
+
+.. _maxfail:
+
+Stopping after the first (or N) failures
+---------------------------------------------------
+
+To stop the testing process after the first (N) failures::
+
+ pytest -x # stop after first failure
+ pytest --maxfail=2 # stop after two failures
+
+.. _select-tests:
+
+Specifying tests / selecting tests
+---------------------------------------------------
+
+Pytest supports several ways to run and select tests from the command-line.
+
+**Run tests in a module**
+
+::
+
+ pytest test_mod.py
+
+**Run tests in a directory**
+
+::
+
+ pytest testing/
+
+**Run tests by keyword expressions**
+
+::
+
+ pytest -k "MyClass and not method"
+
+This will run tests which contain names that match the given *string expression*, which can
+include Python operators that use filenames, class names and function names as variables.
+The example above will run ``TestMyClass.test_something`` but not ``TestMyClass.test_method_simple``.
+
+.. _nodeids:
+
+**Run tests by node ids**
+
+Each collected test is assigned a unique ``nodeid`` which consist of the module filename followed
+by specifiers like class names, function names and parameters from parametrization, separated by ``::`` characters.
+
+To run a specific test within a module::
+
+ pytest test_mod.py::test_func
+
+
+Another example specifying a test method in the command line::
+
+ pytest test_mod.py::TestClass::test_method
+
+**Run tests by marker expressions**
+
+::
+
+ pytest -m slow
+
+Will run all tests which are decorated with the ``@pytest.mark.slow`` decorator.
+
+For more information see :ref:`marks <mark>`.
+
+**Run tests from packages**
+
+::
+
+ pytest --pyargs pkg.testing
+
+This will import ``pkg.testing`` and use its filesystem location to find and run tests from.
+
+
+Modifying Python traceback printing
+----------------------------------------------
+
+Examples for modifying traceback printing::
+
+ pytest --showlocals # show local variables in tracebacks
+ pytest -l # show local variables (shortcut)
+
+ pytest --tb=auto # (default) 'long' tracebacks for the first and last
+ # entry, but 'short' style for the other entries
+ pytest --tb=long # exhaustive, informative traceback formatting
+ pytest --tb=short # shorter traceback format
+ pytest --tb=line # only one line per failure
+ pytest --tb=native # Python standard library formatting
+ pytest --tb=no # no traceback at all
+
+The ``--full-trace`` causes very long traces to be printed on error (longer
+than ``--tb=long``). It also ensures that a stack trace is printed on
+**KeyboardInterrupt** (Ctrl+C).
+This is very useful if the tests are taking too long and you interrupt them
+with Ctrl+C to find out where the tests are *hanging*. By default no output
+will be shown (because KeyboardInterrupt is caught by pytest). By using this
+option you make sure a trace is shown.
+
+
+.. _pdb-option:
+
+Dropping to PDB_ (Python Debugger) on failures
+-----------------------------------------------
+
+.. _PDB: http://docs.python.org/library/pdb.html
+
+Python comes with a builtin Python debugger called PDB_. ``pytest``
+allows one to drop into the PDB_ prompt via a command line option::
+
+ pytest --pdb
+
+This will invoke the Python debugger on every failure. Often you might
+only want to do this for the first failing test to understand a certain
+failure situation::
+
+ pytest -x --pdb # drop to PDB on first failure, then end test session
+ pytest --pdb --maxfail=3 # drop to PDB for first three failures
+
+Note that on any failure the exception information is stored on
+``sys.last_value``, ``sys.last_type`` and ``sys.last_traceback``. In
+interactive use, this allows one to drop into postmortem debugging with
+any debug tool. One can also manually access the exception information,
+for example::
+
+ >>> import sys
+ >>> sys.last_traceback.tb_lineno
+ 42
+ >>> sys.last_value
+ AssertionError('assert result == "ok"',)
+
+.. _breakpoints:
+
+Setting breakpoints
+-------------------
+
+.. versionadded: 2.4.0
+
+To set a breakpoint in your code use the native Python ``import pdb;pdb.set_trace()`` call
+in your code and pytest automatically disables its output capture for that test:
+
+* Output capture in other tests is not affected.
+* Any prior test output that has already been captured and will be processed as
+ such.
+* Any later output produced within the same test will not be captured and will
+ instead get sent directly to ``sys.stdout``. Note that this holds true even
+ for test output occurring after you exit the interactive PDB_ tracing session
+ and continue with the regular test run.
+
+.. _durations:
+
+Profiling test execution duration
+-------------------------------------
+
+.. versionadded: 2.2
+
+To get a list of the slowest 10 test durations::
+
+ pytest --durations=10
+
+
+Creating JUnitXML format files
+----------------------------------------------------
+
+To create result files which can be read by Jenkins_ or other Continuous
+integration servers, use this invocation::
+
+ pytest --junitxml=path
+
+to create an XML file at ``path``.
+
+.. versionadded:: 3.1
+
+To set the name of the root test suite xml item, you can configure the ``junit_suite_name`` option in your config file:
+
+.. code-block:: ini
+
+ [pytest]
+ junit_suite_name = my_suite
+
+record_xml_property
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 2.8
+
+If you want to log additional information for a test, you can use the
+``record_xml_property`` fixture:
+
+.. code-block:: python
+
+ def test_function(record_xml_property):
+ record_xml_property("example_key", 1)
+ assert 0
+
+This will add an extra property ``example_key="1"`` to the generated
+``testcase`` tag:
+
+.. code-block:: xml
+
+ <testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009">
+ <properties>
+ <property name="example_key" value="1" />
+ </properties>
+ </testcase>
+
+.. warning::
+
+ ``record_xml_property`` is an experimental feature, and its interface might be replaced
+ by something more powerful and general in future versions. The
+ functionality per-se will be kept, however.
+
+ Currently it does not work when used with the ``pytest-xdist`` plugin.
+
+ Also please note that using this feature will break any schema verification.
+ This might be a problem when used with some CI servers.
+
+LogXML: add_global_property
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 3.0
+
+If you want to add a properties node in the testsuite level, which may contains properties that are relevant
+to all testcases you can use ``LogXML.add_global_properties``
+
+.. code-block:: python
+
+ import pytest
+
+ @pytest.fixture(scope="session")
+ def log_global_env_facts(f):
+
+ if pytest.config.pluginmanager.hasplugin('junitxml'):
+ my_junit = getattr(pytest.config, '_xml', None)
+
+ my_junit.add_global_property('ARCH', 'PPC')
+ my_junit.add_global_property('STORAGE_TYPE', 'CEPH')
+
+ @pytest.mark.usefixtures(log_global_env_facts)
+ def start_and_prepare_env():
+ pass
+
+ class TestMe(object):
+ def test_foo(self):
+ assert True
+
+This will add a property node below the testsuite node to the generated xml:
+
+.. code-block:: xml
+
+ <testsuite errors="0" failures="0" name="pytest" skips="0" tests="1" time="0.006">
+ <properties>
+ <property name="ARCH" value="PPC"/>
+ <property name="STORAGE_TYPE" value="CEPH"/>
+ </properties>
+ <testcase classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/>
+ </testsuite>
+
+.. warning::
+
+ This is an experimental feature, and its interface might be replaced
+ by something more powerful and general in future versions. The
+ functionality per-se will be kept.
+
+Creating resultlog format files
+----------------------------------------------------
+
+.. deprecated:: 3.0
+
+ This option is rarely used and is scheduled for removal in 4.0.
+
+ An alternative for users which still need similar functionality is to use the
+ `pytest-tap <https://pypi.python.org/pypi/pytest-tap>`_ plugin which provides
+ a stream of test data.
+
+ If you have any concerns, please don't hesitate to
+ `open an issue <https://github.com/pytest-dev/pytest/issues>`_.
+
+To create plain-text machine-readable result files you can issue::
+
+ pytest --resultlog=path
+
+and look at the content at the ``path`` location. Such files are used e.g.
+by the `PyPy-test`_ web page to show test results over several revisions.
+
+.. _`PyPy-test`: http://buildbot.pypy.org/summary
+
+
+Sending test report to online pastebin service
+-----------------------------------------------------
+
+**Creating a URL for each test failure**::
+
+ pytest --pastebin=failed
+
+This will submit test run information to a remote Paste service and
+provide a URL for each failure. You may select tests as usual or add
+for example ``-x`` if you only want to send one particular failure.
+
+**Creating a URL for a whole test session log**::
+
+ pytest --pastebin=all
+
+Currently only pasting to the http://bpaste.net service is implemented.
+
+Disabling plugins
+-----------------
+
+To disable loading specific plugins at invocation time, use the ``-p`` option
+together with the prefix ``no:``.
+
+Example: to disable loading the plugin ``doctest``, which is responsible for
+executing doctest tests from text files, invoke pytest like this::
+
+ pytest -p no:doctest
+
+.. _`pytest.main-usage`:
+
+Calling pytest from Python code
+----------------------------------------------------
+
+.. versionadded:: 2.0
+
+You can invoke ``pytest`` from Python code directly::
+
+ pytest.main()
+
+this acts as if you would call "pytest" from the command line.
+It will not raise ``SystemExit`` but return the exitcode instead.
+You can pass in options and arguments::
+
+ pytest.main(['-x', 'mytestdir'])
+
+You can specify additional plugins to ``pytest.main``::
+
+ # content of myinvoke.py
+ import pytest
+ class MyPlugin(object):
+ def pytest_sessionfinish(self):
+ print("*** test run reporting finishing")
+
+ pytest.main(["-qq"], plugins=[MyPlugin()])
+
+Running it will show that ``MyPlugin`` was added and its
+hook was invoked::
+
+ $ python myinvoke.py
+ *** test run reporting finishing
+
+
+.. include:: links.inc
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/warnings.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/warnings.rst
new file mode 100644
index 00000000000..f249d7e3b2f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/warnings.rst
@@ -0,0 +1,299 @@
+.. _`warnings`:
+
+Warnings Capture
+================
+
+.. versionadded:: 3.1
+
+Starting from version ``3.1``, pytest now automatically catches warnings during test execution
+and displays them at the end of the session::
+
+ # content of test_show_warnings.py
+ import warnings
+
+ def api_v1():
+ warnings.warn(UserWarning("api v1, should use functions from v2"))
+ return 1
+
+ def test_one():
+ assert api_v1() == 1
+
+Running pytest now produces this output::
+
+ $ pytest test_show_warnings.py
+ =========================== test session starts ============================
+ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
+ rootdir: $REGENDOC_TMPDIR, inifile:
+ collected 1 item
+
+ test_show_warnings.py . [100%]
+
+ ============================= warnings summary =============================
+ test_show_warnings.py::test_one
+ $REGENDOC_TMPDIR/test_show_warnings.py:4: UserWarning: api v1, should use functions from v2
+ warnings.warn(UserWarning("api v1, should use functions from v2"))
+
+ -- Docs: http://doc.pytest.org/en/latest/warnings.html
+ =================== 1 passed, 1 warnings in 0.12 seconds ===================
+
+Pytest by default catches all warnings except for ``DeprecationWarning`` and ``PendingDeprecationWarning``.
+
+The ``-W`` flag can be passed to control which warnings will be displayed or even turn
+them into errors::
+
+ $ pytest -q test_show_warnings.py -W error::UserWarning
+ F [100%]
+ ================================= FAILURES =================================
+ _________________________________ test_one _________________________________
+
+ def test_one():
+ > assert api_v1() == 1
+
+ test_show_warnings.py:8:
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+
+ def api_v1():
+ > warnings.warn(UserWarning("api v1, should use functions from v2"))
+ E UserWarning: api v1, should use functions from v2
+
+ test_show_warnings.py:4: UserWarning
+ 1 failed in 0.12 seconds
+
+The same option can be set in the ``pytest.ini`` file using the ``filterwarnings`` ini option.
+For example, the configuration below will ignore all user warnings, but will transform
+all other warnings into errors.
+
+.. code-block:: ini
+
+ [pytest]
+ filterwarnings =
+ error
+ ignore::UserWarning
+
+
+When a warning matches more than one option in the list, the action for the last matching option
+is performed.
+
+Both ``-W`` command-line option and ``filterwarnings`` ini option are based on Python's own
+`-W option`_ and `warnings.simplefilter`_, so please refer to those sections in the Python
+documentation for other examples and advanced usage.
+
+``@pytest.mark.filterwarnings``
+-------------------------------
+
+.. versionadded:: 3.2
+
+You can use the ``@pytest.mark.filterwarnings`` to add warning filters to specific test items,
+allowing you to have finer control of which warnings should be captured at test, class or
+even module level:
+
+.. code-block:: python
+
+ import warnings
+
+ def api_v1():
+ warnings.warn(UserWarning("api v1, should use functions from v2"))
+ return 1
+
+ @pytest.mark.filterwarnings('ignore:api v1')
+ def test_one():
+ assert api_v1() == 1
+
+
+Filters applied using a mark take precedence over filters passed on the command line or configured
+by the ``filterwarnings`` ini option.
+
+You may apply a filter to all tests of a class by using the ``filterwarnings`` mark as a class
+decorator or to all tests in a module by setting the ``pytestmark`` variable:
+
+.. code-block:: python
+
+ # turns all warnings into errors for this module
+ pytestmark = pytest.mark.filterwarnings('error')
+
+
+.. note::
+
+ ``DeprecationWarning`` and ``PendingDeprecationWarning`` are hidden by the standard library
+ by default so you have to explicitly configure them to be displayed in your ``pytest.ini``:
+
+ .. code-block:: ini
+
+ [pytest]
+ filterwarnings =
+ once::DeprecationWarning
+ once::PendingDeprecationWarning
+
+
+*Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_
+*plugin.*
+
+.. _`-W option`: https://docs.python.org/3/using/cmdline.html?highlight=#cmdoption-W
+.. _warnings.simplefilter: https://docs.python.org/3/library/warnings.html#warnings.simplefilter
+.. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings
+
+
+Disabling warning capture
+-------------------------
+
+This feature is enabled by default but can be disabled entirely in your ``pytest.ini`` file with:
+
+ .. code-block:: ini
+
+ [pytest]
+ addopts = -p no:warnings
+
+Or passing ``-p no:warnings`` in the command-line.
+
+.. _`asserting warnings`:
+
+.. _assertwarnings:
+
+.. _`asserting warnings with the warns function`:
+
+.. _warns:
+
+Asserting warnings with the warns function
+-----------------------------------------------
+
+.. versionadded:: 2.8
+
+You can check that code raises a particular warning using ``pytest.warns``,
+which works in a similar manner to :ref:`raises <assertraises>`::
+
+ import warnings
+ import pytest
+
+ def test_warning():
+ with pytest.warns(UserWarning):
+ warnings.warn("my warning", UserWarning)
+
+The test will fail if the warning in question is not raised. The keyword
+argument ``match`` to assert that the exception matches a text or regex::
+
+ >>> with warns(UserWarning, match='must be 0 or None'):
+ ... warnings.warn("value must be 0 or None", UserWarning)
+
+ >>> with warns(UserWarning, match=r'must be \d+$'):
+ ... warnings.warn("value must be 42", UserWarning)
+
+ >>> with warns(UserWarning, match=r'must be \d+$'):
+ ... warnings.warn("this is not here", UserWarning)
+ Traceback (most recent call last):
+ ...
+ Failed: DID NOT WARN. No warnings of type ...UserWarning... was emitted...
+
+You can also call ``pytest.warns`` on a function or code string::
+
+ pytest.warns(expected_warning, func, *args, **kwargs)
+ pytest.warns(expected_warning, "func(*args, **kwargs)")
+
+The function also returns a list of all raised warnings (as
+``warnings.WarningMessage`` objects), which you can query for
+additional information::
+
+ with pytest.warns(RuntimeWarning) as record:
+ warnings.warn("another warning", RuntimeWarning)
+
+ # check that only one warning was raised
+ assert len(record) == 1
+ # check that the message matches
+ assert record[0].message.args[0] == "another warning"
+
+Alternatively, you can examine raised warnings in detail using the
+:ref:`recwarn <recwarn>` fixture (see below).
+
+.. note::
+ ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
+ differently; see :ref:`ensuring_function_triggers`.
+
+.. _`recording warnings`:
+
+.. _recwarn:
+
+Recording warnings
+------------------------
+
+You can record raised warnings either using ``pytest.warns`` or with
+the ``recwarn`` fixture.
+
+To record with ``pytest.warns`` without asserting anything about the warnings,
+pass ``None`` as the expected warning type::
+
+ with pytest.warns(None) as record:
+ warnings.warn("user", UserWarning)
+ warnings.warn("runtime", RuntimeWarning)
+
+ assert len(record) == 2
+ assert str(record[0].message) == "user"
+ assert str(record[1].message) == "runtime"
+
+The ``recwarn`` fixture will record warnings for the whole function::
+
+ import warnings
+
+ def test_hello(recwarn):
+ warnings.warn("hello", UserWarning)
+ assert len(recwarn) == 1
+ w = recwarn.pop(UserWarning)
+ assert issubclass(w.category, UserWarning)
+ assert str(w.message) == "hello"
+ assert w.filename
+ assert w.lineno
+
+Both ``recwarn`` and ``pytest.warns`` return the same interface for recorded
+warnings: a WarningsRecorder instance. To view the recorded warnings, you can
+iterate over this instance, call ``len`` on it to get the number of recorded
+warnings, or index into it to get a particular recorded warning. It also
+provides these methods:
+
+.. autoclass:: _pytest.recwarn.WarningsRecorder()
+ :members:
+
+Each recorded warning has the attributes ``message``, ``category``,
+``filename``, ``lineno``, ``file``, and ``line``. The ``category`` is the
+class of the warning. The ``message`` is the warning itself; calling
+``str(message)`` will return the actual message of the warning.
+
+.. note::
+ :class:`RecordedWarning` was changed from a plain class to a namedtuple in pytest 3.1
+
+.. note::
+ ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
+ differently; see :ref:`ensuring_function_triggers`.
+
+.. _`ensuring a function triggers a deprecation warning`:
+
+.. _ensuring_function_triggers:
+
+Ensuring a function triggers a deprecation warning
+-------------------------------------------------------
+
+You can also call a global helper for checking
+that a certain function call triggers a ``DeprecationWarning`` or
+``PendingDeprecationWarning``::
+
+ import pytest
+
+ def test_global():
+ pytest.deprecated_call(myfunction, 17)
+
+By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be
+caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide
+them. If you wish to record them in your own code, use the
+command ``warnings.simplefilter('always')``::
+
+ import warnings
+ import pytest
+
+ def test_deprecation(recwarn):
+ warnings.simplefilter('always')
+ warnings.warn("deprecated", DeprecationWarning)
+ assert len(recwarn) == 1
+ assert recwarn.pop(DeprecationWarning)
+
+You can also use it as a contextmanager::
+
+ def test_global():
+ with pytest.deprecated_call():
+ myobject.deprecated_method()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/writing_plugins.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/writing_plugins.rst
new file mode 100644
index 00000000000..eb525583000
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/writing_plugins.rst
@@ -0,0 +1,739 @@
+.. _plugins:
+.. _`writing-plugins`:
+
+Writing plugins
+===============
+
+It is easy to implement `local conftest plugins`_ for your own project
+or `pip-installable plugins`_ that can be used throughout many projects,
+including third party projects. Please refer to :ref:`using plugins` if you
+only want to use but not write plugins.
+
+A plugin contains one or multiple hook functions. :ref:`Writing hooks <writinghooks>`
+explains the basics and details of how you can write a hook function yourself.
+``pytest`` implements all aspects of configuration, collection, running and
+reporting by calling `well specified hooks`_ of the following plugins:
+
+* :ref:`builtin plugins`: loaded from pytest's internal ``_pytest`` directory.
+
+* :ref:`external plugins <extplugins>`: modules discovered through
+ `setuptools entry points`_
+
+* `conftest.py plugins`_: modules auto-discovered in test directories
+
+In principle, each hook call is a ``1:N`` Python function call where ``N`` is the
+number of registered implementation functions for a given specification.
+All specifications and implementations follow the ``pytest_`` prefix
+naming convention, making them easy to distinguish and find.
+
+.. _`pluginorder`:
+
+Plugin discovery order at tool startup
+--------------------------------------
+
+``pytest`` loads plugin modules at tool startup in the following way:
+
+* by loading all builtin plugins
+
+* by loading all plugins registered through `setuptools entry points`_.
+
+* by pre-scanning the command line for the ``-p name`` option
+ and loading the specified plugin before actual command line parsing.
+
+* by loading all :file:`conftest.py` files as inferred by the command line
+ invocation:
+
+ - if no test paths are specified use current dir as a test path
+ - if exists, load ``conftest.py`` and ``test*/conftest.py`` relative
+ to the directory part of the first test path.
+
+ Note that pytest does not find ``conftest.py`` files in deeper nested
+ sub directories at tool startup. It is usually a good idea to keep
+ your ``conftest.py`` file in the top level test or project root directory.
+
+* by recursively loading all plugins specified by the
+ ``pytest_plugins`` variable in ``conftest.py`` files
+
+
+.. _`pytest/plugin`: http://bitbucket.org/pytest-dev/pytest/src/tip/pytest/plugin/
+.. _`conftest.py plugins`:
+.. _`localplugin`:
+.. _`local conftest plugins`:
+
+conftest.py: local per-directory plugins
+----------------------------------------
+
+Local ``conftest.py`` plugins contain directory-specific hook
+implementations. Hook Session and test running activities will
+invoke all hooks defined in ``conftest.py`` files closer to the
+root of the filesystem. Example of implementing the
+``pytest_runtest_setup`` hook so that is called for tests in the ``a``
+sub directory but not for other directories::
+
+ a/conftest.py:
+ def pytest_runtest_setup(item):
+ # called for running each test in 'a' directory
+ print ("setting up", item)
+
+ a/test_sub.py:
+ def test_sub():
+ pass
+
+ test_flat.py:
+ def test_flat():
+ pass
+
+Here is how you might run it::
+
+ pytest test_flat.py # will not show "setting up"
+ pytest a/test_sub.py # will show "setting up"
+
+.. note::
+ If you have ``conftest.py`` files which do not reside in a
+ python package directory (i.e. one containing an ``__init__.py``) then
+ "import conftest" can be ambiguous because there might be other
+ ``conftest.py`` files as well on your ``PYTHONPATH`` or ``sys.path``.
+ It is thus good practice for projects to either put ``conftest.py``
+ under a package scope or to never import anything from a
+ ``conftest.py`` file.
+
+ See also: :ref:`pythonpath`.
+
+
+Writing your own plugin
+-----------------------
+
+.. _`setuptools`: http://pypi.python.org/pypi/setuptools
+
+If you want to write a plugin, there are many real-life examples
+you can copy from:
+
+* a custom collection example plugin: :ref:`yaml plugin`
+* around 20 :ref:`builtin plugins` which provide pytest's own functionality
+* many `external plugins <http://plugincompat.herokuapp.com>`_ providing additional features
+
+All of these plugins implement the documented `well specified hooks`_
+to extend and add functionality.
+
+.. note::
+ Make sure to check out the excellent
+ `cookiecutter-pytest-plugin <https://github.com/pytest-dev/cookiecutter-pytest-plugin>`_
+ project, which is a `cookiecutter template <https://github.com/audreyr/cookiecutter>`_
+ for authoring plugins.
+
+ The template provides an excellent starting point with a working plugin,
+ tests running with tox, a comprehensive README file as well as a
+ pre-configured entry-point.
+
+Also consider :ref:`contributing your plugin to pytest-dev<submitplugin>`
+once it has some happy users other than yourself.
+
+
+.. _`setuptools entry points`:
+.. _`pip-installable plugins`:
+
+Making your plugin installable by others
+----------------------------------------
+
+If you want to make your plugin externally available, you
+may define a so-called entry point for your distribution so
+that ``pytest`` finds your plugin module. Entry points are
+a feature that is provided by `setuptools`_. pytest looks up
+the ``pytest11`` entrypoint to discover its
+plugins and you can thus make your plugin available by defining
+it in your setuptools-invocation:
+
+.. sourcecode:: python
+
+ # sample ./setup.py file
+ from setuptools import setup
+
+ setup(
+ name="myproject",
+ packages = ['myproject']
+
+ # the following makes a plugin available to pytest
+ entry_points = {
+ 'pytest11': [
+ 'name_of_plugin = myproject.pluginmodule',
+ ]
+ },
+
+ # custom PyPI classifier for pytest plugins
+ classifiers=[
+ "Framework :: Pytest",
+ ],
+ )
+
+If a package is installed this way, ``pytest`` will load
+``myproject.pluginmodule`` as a plugin which can define
+`well specified hooks`_.
+
+.. note::
+
+ Make sure to include ``Framework :: Pytest`` in your list of
+ `PyPI classifiers <https://python-packaging-user-guide.readthedocs.io/distributing/#classifiers>`_
+ to make it easy for users to find your plugin.
+
+
+Assertion Rewriting
+-------------------
+
+One of the main features of ``pytest`` is the use of plain assert
+statements and the detailed introspection of expressions upon
+assertion failures. This is provided by "assertion rewriting" which
+modifies the parsed AST before it gets compiled to bytecode. This is
+done via a :pep:`302` import hook which gets installed early on when
+``pytest`` starts up and will perform this rewriting when modules get
+imported. However since we do not want to test different bytecode
+then you will run in production this hook only rewrites test modules
+themselves as well as any modules which are part of plugins. Any
+other imported module will not be rewritten and normal assertion
+behaviour will happen.
+
+If you have assertion helpers in other modules where you would need
+assertion rewriting to be enabled you need to ask ``pytest``
+explicitly to rewrite this module before it gets imported.
+
+.. autofunction:: pytest.register_assert_rewrite
+
+This is especially important when you write a pytest plugin which is
+created using a package. The import hook only treats ``conftest.py``
+files and any modules which are listed in the ``pytest11`` entrypoint
+as plugins. As an example consider the following package::
+
+ pytest_foo/__init__.py
+ pytest_foo/plugin.py
+ pytest_foo/helper.py
+
+With the following typical ``setup.py`` extract:
+
+.. code-block:: python
+
+ setup(
+ ...
+ entry_points={'pytest11': ['foo = pytest_foo.plugin']},
+ ...
+ )
+
+In this case only ``pytest_foo/plugin.py`` will be rewritten. If the
+helper module also contains assert statements which need to be
+rewritten it needs to be marked as such, before it gets imported.
+This is easiest by marking it for rewriting inside the
+``__init__.py`` module, which will always be imported first when a
+module inside a package is imported. This way ``plugin.py`` can still
+import ``helper.py`` normally. The contents of
+``pytest_foo/__init__.py`` will then need to look like this:
+
+.. code-block:: python
+
+ import pytest
+
+ pytest.register_assert_rewrite('pytest_foo.helper')
+
+
+
+Requiring/Loading plugins in a test module or conftest file
+-----------------------------------------------------------
+
+You can require plugins in a test module or a ``conftest.py`` file like this:
+
+.. code-block:: python
+
+ pytest_plugins = ["name1", "name2"]
+
+When the test module or conftest plugin is loaded the specified plugins
+will be loaded as well. Any module can be blessed as a plugin, including internal
+application modules:
+
+.. code-block:: python
+
+ pytest_plugins = "myapp.testsupport.myplugin"
+
+``pytest_plugins`` variables are processed recursively, so note that in the example above
+if ``myapp.testsupport.myplugin`` also declares ``pytest_plugins``, the contents
+of the variable will also be loaded as plugins, and so on.
+
+This mechanism makes it easy to share fixtures within applications or even
+external applications without the need to create external plugins using
+the ``setuptools``'s entry point technique.
+
+Plugins imported by ``pytest_plugins`` will also automatically be marked
+for assertion rewriting (see :func:`pytest.register_assert_rewrite`).
+However for this to have any effect the module must not be
+imported already; if it was already imported at the time the
+``pytest_plugins`` statement is processed, a warning will result and
+assertions inside the plugin will not be rewritten. To fix this you
+can either call :func:`pytest.register_assert_rewrite` yourself before
+the module is imported, or you can arrange the code to delay the
+importing until after the plugin is registered.
+
+
+Accessing another plugin by name
+--------------------------------
+
+If a plugin wants to collaborate with code from
+another plugin it can obtain a reference through
+the plugin manager like this:
+
+.. sourcecode:: python
+
+ plugin = config.pluginmanager.getplugin("name_of_plugin")
+
+If you want to look at the names of existing plugins, use
+the ``--trace-config`` option.
+
+Testing plugins
+---------------
+
+pytest comes with a plugin named ``pytester`` that helps you write tests for
+your plugin code. The plugin is disabled by default, so you will have to enable
+it before you can use it.
+
+You can do so by adding the following line to a ``conftest.py`` file in your
+testing directory:
+
+.. code-block:: python
+
+ # content of conftest.py
+
+ pytest_plugins = ["pytester"]
+
+Alternatively you can invoke pytest with the ``-p pytester`` command line
+option.
+
+This will allow you to use the :py:class:`testdir <_pytest.pytester.Testdir>`
+fixture for testing your plugin code.
+
+Let's demonstrate what you can do with the plugin with an example. Imagine we
+developed a plugin that provides a fixture ``hello`` which yields a function
+and we can invoke this function with one optional parameter. It will return a
+string value of ``Hello World!`` if we do not supply a value or ``Hello
+{value}!`` if we do supply a string value.
+
+.. code-block:: python
+
+ # -*- coding: utf-8 -*-
+
+ import pytest
+
+ def pytest_addoption(parser):
+ group = parser.getgroup('helloworld')
+ group.addoption(
+ '--name',
+ action='store',
+ dest='name',
+ default='World',
+ help='Default "name" for hello().'
+ )
+
+ @pytest.fixture
+ def hello(request):
+ name = request.config.getoption('name')
+
+ def _hello(name=None):
+ if not name:
+ name = request.config.getoption('name')
+ return "Hello {name}!".format(name=name)
+
+ return _hello
+
+
+Now the ``testdir`` fixture provides a convenient API for creating temporary
+``conftest.py`` files and test files. It also allows us to run the tests and
+return a result object, with which we can assert the tests' outcomes.
+
+.. code-block:: python
+
+ def test_hello(testdir):
+ """Make sure that our plugin works."""
+
+ # create a temporary conftest.py file
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture(params=[
+ "Brianna",
+ "Andreas",
+ "Floris",
+ ])
+ def name(request):
+ return request.param
+ """)
+
+ # create a temporary pytest test file
+ testdir.makepyfile("""
+ def test_hello_default(hello):
+ assert hello() == "Hello World!"
+
+ def test_hello_name(hello, name):
+ assert hello(name) == "Hello {0}!".format(name)
+ """)
+
+ # run all tests with pytest
+ result = testdir.runpytest()
+
+ # check that all 4 tests passed
+ result.assert_outcomes(passed=4)
+
+
+For more information about the result object that ``runpytest()`` returns, and
+the methods that it provides please check out the :py:class:`RunResult
+<_pytest.pytester.RunResult>` documentation.
+
+
+.. _`writinghooks`:
+
+Writing hook functions
+======================
+
+
+.. _validation:
+
+hook function validation and execution
+--------------------------------------
+
+pytest calls hook functions from registered plugins for any
+given hook specification. Let's look at a typical hook function
+for the ``pytest_collection_modifyitems(session, config,
+items)`` hook which pytest calls after collection of all test items is
+completed.
+
+When we implement a ``pytest_collection_modifyitems`` function in our plugin
+pytest will during registration verify that you use argument
+names which match the specification and bail out if not.
+
+Let's look at a possible implementation:
+
+.. code-block:: python
+
+ def pytest_collection_modifyitems(config, items):
+ # called after collection is completed
+ # you can modify the ``items`` list
+
+Here, ``pytest`` will pass in ``config`` (the pytest config object)
+and ``items`` (the list of collected test items) but will not pass
+in the ``session`` argument because we didn't list it in the function
+signature. This dynamic "pruning" of arguments allows ``pytest`` to
+be "future-compatible": we can introduce new hook named parameters without
+breaking the signatures of existing hook implementations. It is one of
+the reasons for the general long-lived compatibility of pytest plugins.
+
+Note that hook functions other than ``pytest_runtest_*`` are not
+allowed to raise exceptions. Doing so will break the pytest run.
+
+
+
+.. _firstresult:
+
+firstresult: stop at first non-None result
+-------------------------------------------
+
+Most calls to ``pytest`` hooks result in a **list of results** which contains
+all non-None results of the called hook functions.
+
+Some hook specifications use the ``firstresult=True`` option so that the hook
+call only executes until the first of N registered functions returns a
+non-None result which is then taken as result of the overall hook call.
+The remaining hook functions will not be called in this case.
+
+
+hookwrapper: executing around other hooks
+-------------------------------------------------
+
+.. currentmodule:: _pytest.core
+
+.. versionadded:: 2.7
+
+pytest plugins can implement hook wrappers which wrap the execution
+of other hook implementations. A hook wrapper is a generator function
+which yields exactly once. When pytest invokes hooks it first executes
+hook wrappers and passes the same arguments as to the regular hooks.
+
+At the yield point of the hook wrapper pytest will execute the next hook
+implementations and return their result to the yield point in the form of
+a :py:class:`Result <pluggy._Result>` instance which encapsulates a result or
+exception info. The yield point itself will thus typically not raise
+exceptions (unless there are bugs).
+
+Here is an example definition of a hook wrapper::
+
+ import pytest
+
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_pyfunc_call(pyfuncitem):
+ # do whatever you want before the next hook executes
+
+ outcome = yield
+ # outcome.excinfo may be None or a (cls, val, tb) tuple
+
+ res = outcome.get_result() # will raise if outcome was exception
+ # postprocess result
+
+Note that hook wrappers don't return results themselves, they merely
+perform tracing or other side effects around the actual hook implementations.
+If the result of the underlying hook is a mutable object, they may modify
+that result but it's probably better to avoid it.
+
+
+Hook function ordering / call example
+-------------------------------------
+
+For any given hook specification there may be more than one
+implementation and we thus generally view ``hook`` execution as a
+``1:N`` function call where ``N`` is the number of registered functions.
+There are ways to influence if a hook implementation comes before or
+after others, i.e. the position in the ``N``-sized list of functions:
+
+.. code-block:: python
+
+ # Plugin 1
+ @pytest.hookimpl(tryfirst=True)
+ def pytest_collection_modifyitems(items):
+ # will execute as early as possible
+
+ # Plugin 2
+ @pytest.hookimpl(trylast=True)
+ def pytest_collection_modifyitems(items):
+ # will execute as late as possible
+
+ # Plugin 3
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_collection_modifyitems(items):
+ # will execute even before the tryfirst one above!
+ outcome = yield
+ # will execute after all non-hookwrappers executed
+
+Here is the order of execution:
+
+1. Plugin3's pytest_collection_modifyitems called until the yield point
+ because it is a hook wrapper.
+
+2. Plugin1's pytest_collection_modifyitems is called because it is marked
+ with ``tryfirst=True``.
+
+3. Plugin2's pytest_collection_modifyitems is called because it is marked
+ with ``trylast=True`` (but even without this mark it would come after
+ Plugin1).
+
+4. Plugin3's pytest_collection_modifyitems then executing the code after the yield
+ point. The yield receives a :py:class:`Result <pluggy._Result>` instance which encapsulates
+ the result from calling the non-wrappers. Wrappers shall not modify the result.
+
+It's possible to use ``tryfirst`` and ``trylast`` also in conjunction with
+``hookwrapper=True`` in which case it will influence the ordering of hookwrappers
+among each other.
+
+
+Declaring new hooks
+------------------------
+
+.. currentmodule:: _pytest.hookspec
+
+Plugins and ``conftest.py`` files may declare new hooks that can then be
+implemented by other plugins in order to alter behaviour or interact with
+the new plugin:
+
+.. autofunction:: pytest_addhooks
+
+Hooks are usually declared as do-nothing functions that contain only
+documentation describing when the hook will be called and what return values
+are expected.
+
+For an example, see `newhooks.py`_ from `xdist <https://github.com/pytest-dev/pytest-xdist>`_.
+
+.. _`newhooks.py`: https://github.com/pytest-dev/pytest-xdist/blob/974bd566c599dc6a9ea291838c6f226197208b46/xdist/newhooks.py
+
+
+Optionally using hooks from 3rd party plugins
+---------------------------------------------
+
+Using new hooks from plugins as explained above might be a little tricky
+because of the standard :ref:`validation mechanism <validation>`:
+if you depend on a plugin that is not installed, validation will fail and
+the error message will not make much sense to your users.
+
+One approach is to defer the hook implementation to a new plugin instead of
+declaring the hook functions directly in your plugin module, for example::
+
+ # contents of myplugin.py
+
+ class DeferPlugin(object):
+ """Simple plugin to defer pytest-xdist hook functions."""
+
+ def pytest_testnodedown(self, node, error):
+ """standard xdist hook function.
+ """
+
+ def pytest_configure(config):
+ if config.pluginmanager.hasplugin('xdist'):
+ config.pluginmanager.register(DeferPlugin())
+
+This has the added benefit of allowing you to conditionally install hooks
+depending on which plugins are installed.
+
+.. _`well specified hooks`:
+
+.. currentmodule:: _pytest.hookspec
+
+pytest hook reference
+=====================
+
+
+Initialization, command line and configuration hooks
+----------------------------------------------------
+
+.. autofunction:: pytest_load_initial_conftests
+.. autofunction:: pytest_cmdline_preparse
+.. autofunction:: pytest_cmdline_parse
+.. autofunction:: pytest_addoption
+.. autofunction:: pytest_cmdline_main
+.. autofunction:: pytest_configure
+.. autofunction:: pytest_unconfigure
+
+Generic "runtest" hooks
+-----------------------
+
+All runtest related hooks receive a :py:class:`pytest.Item <_pytest.main.Item>` object.
+
+.. autofunction:: pytest_runtest_protocol
+.. autofunction:: pytest_runtest_setup
+.. autofunction:: pytest_runtest_call
+.. autofunction:: pytest_runtest_teardown
+.. autofunction:: pytest_runtest_makereport
+
+For deeper understanding you may look at the default implementation of
+these hooks in :py:mod:`_pytest.runner` and maybe also
+in :py:mod:`_pytest.pdb` which interacts with :py:mod:`_pytest.capture`
+and its input/output capturing in order to immediately drop
+into interactive debugging when a test failure occurs.
+
+The :py:mod:`_pytest.terminal` reported specifically uses
+the reporting hook to print information about a test run.
+
+Collection hooks
+----------------
+
+``pytest`` calls the following hooks for collecting files and directories:
+
+.. autofunction:: pytest_ignore_collect
+.. autofunction:: pytest_collect_directory
+.. autofunction:: pytest_collect_file
+
+For influencing the collection of objects in Python modules
+you can use the following hook:
+
+.. autofunction:: pytest_pycollect_makeitem
+.. autofunction:: pytest_generate_tests
+.. autofunction:: pytest_make_parametrize_id
+
+After collection is complete, you can modify the order of
+items, delete or otherwise amend the test items:
+
+.. autofunction:: pytest_collection_modifyitems
+
+Reporting hooks
+---------------
+
+Session related reporting hooks:
+
+.. autofunction:: pytest_collectstart
+.. autofunction:: pytest_itemcollected
+.. autofunction:: pytest_collectreport
+.. autofunction:: pytest_deselected
+.. autofunction:: pytest_report_header
+.. autofunction:: pytest_report_collectionfinish
+.. autofunction:: pytest_report_teststatus
+.. autofunction:: pytest_terminal_summary
+.. autofunction:: pytest_fixture_setup
+.. autofunction:: pytest_fixture_post_finalizer
+
+And here is the central hook for reporting about
+test execution:
+
+.. autofunction:: pytest_runtest_logreport
+
+You can also use this hook to customize assertion representation for some
+types:
+
+.. autofunction:: pytest_assertrepr_compare
+
+
+Debugging/Interaction hooks
+---------------------------
+
+There are few hooks which can be used for special
+reporting or interaction with exceptions:
+
+.. autofunction:: pytest_internalerror
+.. autofunction:: pytest_keyboard_interrupt
+.. autofunction:: pytest_exception_interact
+.. autofunction:: pytest_enter_pdb
+
+
+Reference of objects involved in hooks
+======================================
+
+.. autoclass:: _pytest.config.Config()
+ :members:
+
+.. autoclass:: _pytest.config.Parser()
+ :members:
+
+.. autoclass:: _pytest.main.Node()
+ :members:
+
+.. autoclass:: _pytest.main.Collector()
+ :members:
+ :show-inheritance:
+
+.. autoclass:: _pytest.main.Item()
+ :members:
+ :show-inheritance:
+
+.. autoclass:: _pytest.python.Module()
+ :members:
+ :show-inheritance:
+
+.. autoclass:: _pytest.python.Class()
+ :members:
+ :show-inheritance:
+
+.. autoclass:: _pytest.python.Function()
+ :members:
+ :show-inheritance:
+
+.. autoclass:: _pytest.fixtures.FixtureDef()
+ :members:
+ :show-inheritance:
+
+.. autoclass:: _pytest.runner.CallInfo()
+ :members:
+
+.. autoclass:: _pytest.runner.TestReport()
+ :members:
+ :inherited-members:
+
+.. autoclass:: pluggy._Result
+ :members:
+
+.. autofunction:: _pytest.config.get_plugin_manager()
+
+.. autoclass:: _pytest.config.PytestPluginManager()
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+.. autoclass:: pluggy.PluginManager()
+ :members:
+
+.. currentmodule:: _pytest.pytester
+
+.. autoclass:: Testdir()
+ :members: runpytest,runpytest_subprocess,runpytest_inprocess,makeconftest,makepyfile
+
+.. autoclass:: RunResult()
+ :members:
+
+.. autoclass:: LineMatcher()
+ :members:
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/xunit_setup.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/xunit_setup.rst
new file mode 100644
index 00000000000..148fb1209ea
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/xunit_setup.rst
@@ -0,0 +1,99 @@
+
+.. _`classic xunit`:
+.. _xunitsetup:
+
+classic xunit-style setup
+========================================
+
+This section describes a classic and popular way how you can implement
+fixtures (setup and teardown test state) on a per-module/class/function basis.
+
+
+.. note::
+
+ While these setup/teardown methods are simple and familiar to those
+ coming from a ``unittest`` or nose ``background``, you may also consider
+ using pytest's more powerful :ref:`fixture mechanism
+ <fixture>` which leverages the concept of dependency injection, allowing
+ for a more modular and more scalable approach for managing test state,
+ especially for larger projects and for functional testing. You can
+ mix both fixture mechanisms in the same file but
+ test methods of ``unittest.TestCase`` subclasses
+ cannot receive fixture arguments.
+
+
+Module level setup/teardown
+--------------------------------------
+
+If you have multiple test functions and test classes in a single
+module you can optionally implement the following fixture methods
+which will usually be called once for all the functions::
+
+ def setup_module(module):
+ """ setup any state specific to the execution of the given module."""
+
+ def teardown_module(module):
+ """ teardown any state that was previously setup with a setup_module
+ method.
+ """
+
+As of pytest-3.0, the ``module`` parameter is optional.
+
+Class level setup/teardown
+----------------------------------
+
+Similarly, the following methods are called at class level before
+and after all test methods of the class are called::
+
+ @classmethod
+ def setup_class(cls):
+ """ setup any state specific to the execution of the given class (which
+ usually contains tests).
+ """
+
+ @classmethod
+ def teardown_class(cls):
+ """ teardown any state that was previously setup with a call to
+ setup_class.
+ """
+
+Method and function level setup/teardown
+-----------------------------------------------
+
+Similarly, the following methods are called around each method invocation::
+
+ def setup_method(self, method):
+ """ setup any state tied to the execution of the given method in a
+ class. setup_method is invoked for every test method of a class.
+ """
+
+ def teardown_method(self, method):
+ """ teardown any state that was previously setup with a setup_method
+ call.
+ """
+
+As of pytest-3.0, the ``method`` parameter is optional.
+
+If you would rather define test functions directly at module level
+you can also use the following functions to implement fixtures::
+
+ def setup_function(function):
+ """ setup any state tied to the execution of the given function.
+ Invoked for every test function in the module.
+ """
+
+ def teardown_function(function):
+ """ teardown any state that was previously setup with a setup_function
+ call.
+ """
+
+As of pytest-3.0, the ``function`` parameter is optional.
+
+Remarks:
+
+* It is possible for setup/teardown pairs to be invoked multiple times
+ per testing process.
+* teardown functions are not called if the corresponding setup function existed
+ and failed/was skipped.
+
+.. _`unittest.py module`: http://docs.python.org/library/unittest.html
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/yieldfixture.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/yieldfixture.rst
new file mode 100644
index 00000000000..6fd1edac2f5
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/doc/en/yieldfixture.rst
@@ -0,0 +1,18 @@
+:orphan:
+
+.. _yieldfixture:
+
+"yield_fixture" functions
+---------------------------------------------------------------
+
+.. deprecated:: 3.0
+
+.. versionadded:: 2.4
+
+.. important::
+ Since pytest-3.0, fixtures using the normal ``fixture`` decorator can use a ``yield``
+ statement to provide fixture values and execute teardown code, exactly like ``yield_fixture``
+ in previous versions.
+
+ Marking functions as ``yield_fixture`` is still supported, but deprecated and should not
+ be used in new code.
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/extra/get_issues.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/extra/get_issues.py
new file mode 100644
index 00000000000..2a8f8c31606
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/extra/get_issues.py
@@ -0,0 +1,84 @@
+import json
+import py
+import textwrap
+
+issues_url = "https://api.github.com/repos/pytest-dev/pytest/issues"
+
+import requests
+
+
+def get_issues():
+ issues = []
+ url = issues_url
+ while 1:
+ get_data = {"state": "all"}
+ r = requests.get(url, params=get_data)
+ data = r.json()
+ if r.status_code == 403:
+ # API request limit exceeded
+ print(data['message'])
+ exit(1)
+ issues.extend(data)
+
+ # Look for next page
+ links = requests.utils.parse_header_links(r.headers['Link'])
+ another_page = False
+ for link in links:
+ if link['rel'] == 'next':
+ url = link['url']
+ another_page = True
+ if not another_page:
+ return issues
+
+
+def main(args):
+ cachefile = py.path.local(args.cache)
+ if not cachefile.exists() or args.refresh:
+ issues = get_issues()
+ cachefile.write(json.dumps(issues))
+ else:
+ issues = json.loads(cachefile.read())
+
+ open_issues = [x for x in issues if x["state"] == "open"]
+
+ open_issues.sort(key=lambda x: x["number"])
+ report(open_issues)
+
+
+def _get_kind(issue):
+ labels = [l['name'] for l in issue['labels']]
+ for key in ('bug', 'enhancement', 'proposal'):
+ if key in labels:
+ return key
+ return 'issue'
+
+
+def report(issues):
+ for issue in issues:
+ title = issue["title"]
+ body = issue["body"]
+ kind = _get_kind(issue)
+ status = issue["state"]
+ number = issue["number"]
+ link = "https://github.com/pytest-dev/pytest/issues/%s/" % number
+ print("----")
+ print(status, kind, link)
+ print(title)
+ #print()
+ #lines = body.split("\n")
+ #print ("\n".join(lines[:3]))
+ #if len(lines) > 3 or len(body) > 240:
+ # print ("...")
+ print("\n\nFound %s open issues" % len(issues))
+
+
+if __name__ == "__main__":
+ import argparse
+ parser = argparse.ArgumentParser("process bitbucket issues")
+ parser.add_argument("--refresh", action="store_true",
+ help="invalidate cache, refresh issues")
+ parser.add_argument("--cache", action="store", default="issues.json",
+ help="cache file")
+ args = parser.parse_args()
+ main(args)
+
diff --git a/tests/wpt/web-platform-tests/tools/pytest/extra/setup-py.test/setup.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/extra/setup-py.test/setup.py
index d0560ce1f5f..d0560ce1f5f 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/extra/setup-py.test/setup.py
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/extra/setup-py.test/setup.py
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/pyproject.toml b/tests/wpt/web-platform-tests/tools/third_party/pytest/pyproject.toml
new file mode 100644
index 00000000000..88571e208b9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/pyproject.toml
@@ -0,0 +1,35 @@
+[tool.towncrier]
+package = "pytest"
+filename = "CHANGELOG.rst"
+directory = "changelog/"
+template = "changelog/_template.rst"
+
+ [[tool.towncrier.type]]
+ directory = "removal"
+ name = "Deprecations and Removals"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "feature"
+ name = "Features"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "bugfix"
+ name = "Bug Fixes"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "vendor"
+ name = "Vendored Libraries"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "doc"
+ name = "Improved Documentation"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "trivial"
+ name = "Trivial/Internal Changes"
+ showcontent = true
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/pytest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/pytest.py
new file mode 100644
index 00000000000..2b681b64b4c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/pytest.py
@@ -0,0 +1,77 @@
+# PYTHON_ARGCOMPLETE_OK
+"""
+pytest: unit and functional testing with Python.
+"""
+
+
+# else we are imported
+
+from _pytest.config import (
+ main, UsageError, cmdline,
+ hookspec, hookimpl
+)
+from _pytest.fixtures import fixture, yield_fixture
+from _pytest.assertion import register_assert_rewrite
+from _pytest.freeze_support import freeze_includes
+from _pytest import __version__
+from _pytest.debugging import pytestPDB as __pytestPDB
+from _pytest.recwarn import warns, deprecated_call
+from _pytest.outcomes import fail, skip, importorskip, exit, xfail
+from _pytest.mark import MARK_GEN as mark, param
+from _pytest.main import Item, Collector, File, Session
+from _pytest.fixtures import fillfixtures as _fillfuncargs
+from _pytest.python import (
+ Module, Class, Instance, Function, Generator,
+)
+
+from _pytest.python_api import approx, raises
+
+set_trace = __pytestPDB.set_trace
+
+__all__ = [
+ 'main',
+ 'UsageError',
+ 'cmdline',
+ 'hookspec',
+ 'hookimpl',
+ '__version__',
+ 'register_assert_rewrite',
+ 'freeze_includes',
+ 'set_trace',
+ 'warns',
+ 'deprecated_call',
+ 'fixture',
+ 'yield_fixture',
+ 'fail',
+ 'skip',
+ 'xfail',
+ 'importorskip',
+ 'exit',
+ 'mark',
+ 'param',
+ 'approx',
+ '_fillfuncargs',
+
+ 'Item',
+ 'File',
+ 'Collector',
+ 'Session',
+ 'Module',
+ 'Class',
+ 'Instance',
+ 'Function',
+ 'Generator',
+ 'raises',
+
+
+]
+
+if __name__ == '__main__':
+ # if run as a script or by 'python -m pytest'
+ # we trigger the below "else" condition by the following import
+ import pytest
+ raise SystemExit(pytest.main())
+else:
+
+ from _pytest.compat import _setup_collect_fakemodule
+ _setup_collect_fakemodule()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/call-tox.bat b/tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/call-tox.bat
new file mode 100644
index 00000000000..86fb25c1df1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/call-tox.bat
@@ -0,0 +1,8 @@
+REM skip "coveralls" run in PRs or forks
+if "%TOXENV%" == "coveralls" (
+ if not defined COVERALLS_REPO_TOKEN (
+ echo skipping coveralls run because COVERALLS_REPO_TOKEN is not defined
+ exit /b 0
+ )
+)
+C:\Python36\python -m tox
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/check-rst.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/check-rst.py
new file mode 100644
index 00000000000..57f717501af
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/check-rst.py
@@ -0,0 +1,11 @@
+
+from __future__ import print_function
+
+import subprocess
+import glob
+import sys
+
+sys.exit(subprocess.call([
+ 'rst-lint', '--encoding', 'utf-8',
+ 'CHANGELOG.rst', 'HOWTORELEASE.rst', 'README.rst',
+] + glob.glob('changelog/[0-9]*.*')))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/install-pypy.bat b/tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/install-pypy.bat
new file mode 100644
index 00000000000..8012ea46aca
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/scripts/install-pypy.bat
@@ -0,0 +1,6 @@
+REM install pypy using choco
+REM redirect to a file because choco install python.pypy is too noisy. If the command fails, write output to console
+choco install python.pypy > pypy-inst.log 2>&1 || (type pypy-inst.log & exit /b 1)
+set PATH=C:\tools\pypy\pypy;%PATH% # so tox can find pypy
+echo PyPy installed
+pypy --version
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/setup.cfg b/tests/wpt/web-platform-tests/tools/third_party/pytest/setup.cfg
new file mode 100644
index 00000000000..816539e2ec6
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/setup.cfg
@@ -0,0 +1,20 @@
+[build_sphinx]
+source-dir = doc/en/
+build-dir = doc/build
+all_files = 1
+
+[upload_sphinx]
+upload-dir = doc/en/build/html
+
+[bdist_wheel]
+universal = 1
+
+[check-manifest]
+ignore =
+ _pytest/_version.py
+
+[metadata]
+license_file = LICENSE
+
+[devpi:upload]
+formats = sdist.tgz,bdist_wheel
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/setup.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/setup.py
new file mode 100644
index 00000000000..3eb38efe655
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/setup.py
@@ -0,0 +1,114 @@
+import os
+import sys
+import setuptools
+import pkg_resources
+from setuptools import setup, Command
+
+classifiers = [
+ 'Development Status :: 6 - Mature',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: MIT License',
+ 'Operating System :: POSIX',
+ 'Operating System :: Microsoft :: Windows',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Topic :: Software Development :: Testing',
+ 'Topic :: Software Development :: Libraries',
+ 'Topic :: Utilities',
+] + [
+ ('Programming Language :: Python :: %s' % x)
+ for x in '2 2.7 3 3.4 3.5 3.6'.split()
+]
+
+with open('README.rst') as fd:
+ long_description = fd.read()
+
+
+def has_environment_marker_support():
+ """
+ Tests that setuptools has support for PEP-426 environment marker support.
+
+ The first known release to support it is 0.7 (and the earliest on PyPI seems to be 0.7.2
+ so we're using that), see: http://pythonhosted.org/setuptools/history.html#id142
+
+ References:
+
+ * https://wheel.readthedocs.io/en/latest/index.html#defining-conditional-dependencies
+ * https://www.python.org/dev/peps/pep-0426/#environment-markers
+ """
+ try:
+ return pkg_resources.parse_version(setuptools.__version__) >= pkg_resources.parse_version('0.7.2')
+ except Exception as exc:
+ sys.stderr.write("Could not test setuptool's version: %s\n" % exc)
+ return False
+
+
+def main():
+ extras_require = {}
+ install_requires = [
+ 'py>=1.5.0',
+ 'six>=1.10.0',
+ 'setuptools',
+ 'attrs>=17.2.0',
+ ]
+ # if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy;
+ # used by tox.ini to test with pluggy master
+ if '_PYTEST_SETUP_SKIP_PLUGGY_DEP' not in os.environ:
+ install_requires.append('pluggy>=0.5,<0.7')
+ if has_environment_marker_support():
+ extras_require[':python_version<"3.0"'] = ['funcsigs']
+ extras_require[':sys_platform=="win32"'] = ['colorama']
+ else:
+ if sys.platform == 'win32':
+ install_requires.append('colorama')
+ if sys.version_info < (3, 0):
+ install_requires.append('funcsigs')
+
+ setup(
+ name='pytest',
+ description='pytest: simple powerful testing with Python',
+ long_description=long_description,
+ use_scm_version={
+ 'write_to': '_pytest/_version.py',
+ },
+ url='http://pytest.org',
+ license='MIT license',
+ platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
+ author=(
+ 'Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, '
+ 'Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others'),
+ entry_points={'console_scripts': [
+ 'pytest=pytest:main', 'py.test=pytest:main']},
+ classifiers=classifiers,
+ keywords="test unittest",
+ cmdclass={'test': PyTest},
+ # the following should be enabled for release
+ setup_requires=['setuptools-scm'],
+ python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
+ install_requires=install_requires,
+ extras_require=extras_require,
+ packages=['_pytest', '_pytest.assertion', '_pytest._code'],
+ py_modules=['pytest'],
+ zip_safe=False,
+ )
+
+
+class PyTest(Command):
+ user_options = []
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ import subprocess
+ PPATH = [x for x in os.environ.get('PYTHONPATH', '').split(':') if x]
+ PPATH.insert(0, os.getcwd())
+ os.environ['PYTHONPATH'] = ':'.join(PPATH)
+ errno = subprocess.call([sys.executable, 'pytest.py', '--ignore=doc'])
+ raise SystemExit(errno)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/__init__.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/__init__.py
new file mode 100644
index 00000000000..8ea038f0af0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/__init__.py
@@ -0,0 +1,12 @@
+"""
+Invoke tasks to help with pytest development and release process.
+"""
+
+import invoke
+
+from . import generate
+
+
+ns = invoke.Collection(
+ generate,
+)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/generate.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/generate.py
new file mode 100644
index 00000000000..fa8ee6557df
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/generate.py
@@ -0,0 +1,162 @@
+import os
+from pathlib import Path
+from subprocess import check_output, check_call
+
+import invoke
+
+
+@invoke.task(help={
+ 'version': 'version being released',
+})
+def announce(ctx, version):
+ """Generates a new release announcement entry in the docs."""
+ # Get our list of authors
+ stdout = check_output(["git", "describe", "--abbrev=0", '--tags'])
+ stdout = stdout.decode('utf-8')
+ last_version = stdout.strip()
+
+ stdout = check_output(["git", "log", "{}..HEAD".format(last_version), "--format=%aN"])
+ stdout = stdout.decode('utf-8')
+
+ contributors = set(stdout.splitlines())
+
+ template_name = 'release.minor.rst' if version.endswith('.0') else 'release.patch.rst'
+ template_text = Path(__file__).parent.joinpath(template_name).read_text(encoding='UTF-8')
+
+ contributors_text = '\n'.join('* {}'.format(name) for name in sorted(contributors)) + '\n'
+ text = template_text.format(version=version, contributors=contributors_text)
+
+ target = Path(__file__).parent.joinpath('../doc/en/announce/release-{}.rst'.format(version))
+ target.write_text(text, encoding='UTF-8')
+ print("[generate.announce] Generated {}".format(target.name))
+
+ # Update index with the new release entry
+ index_path = Path(__file__).parent.joinpath('../doc/en/announce/index.rst')
+ lines = index_path.read_text(encoding='UTF-8').splitlines()
+ indent = ' '
+ for index, line in enumerate(lines):
+ if line.startswith('{}release-'.format(indent)):
+ new_line = indent + target.stem
+ if line != new_line:
+ lines.insert(index, new_line)
+ index_path.write_text('\n'.join(lines) + '\n', encoding='UTF-8')
+ print("[generate.announce] Updated {}".format(index_path.name))
+ else:
+ print("[generate.announce] Skip {} (already contains release)".format(index_path.name))
+ break
+
+ check_call(['git', 'add', str(target)])
+
+
+@invoke.task()
+def regen(ctx):
+ """Call regendoc tool to update examples and pytest output in the docs."""
+ print("[generate.regen] Updating docs")
+ check_call(['tox', '-e', 'regen'])
+
+
+@invoke.task()
+def make_tag(ctx, version):
+ """Create a new (local) tag for the release, only if the repository is clean."""
+ from git import Repo
+
+ repo = Repo('.')
+ if repo.is_dirty():
+ print('Current repository is dirty. Please commit any changes and try again.')
+ raise invoke.Exit(code=2)
+
+ tag_names = [x.name for x in repo.tags]
+ if version in tag_names:
+ print("[generate.make_tag] Delete existing tag {}".format(version))
+ repo.delete_tag(version)
+
+ print("[generate.make_tag] Create tag {}".format(version))
+ repo.create_tag(version)
+
+
+@invoke.task()
+def devpi_upload(ctx, version, user, password=None):
+ """Creates and uploads a package to devpi for testing."""
+ if password:
+ print("[generate.devpi_upload] devpi login {}".format(user))
+ check_call(['devpi', 'login', user, '--password', password])
+
+ check_call(['devpi', 'use', 'https://devpi.net/{}/dev'.format(user)])
+
+ env = os.environ.copy()
+ env['SETUPTOOLS_SCM_PRETEND_VERSION'] = version
+ check_call(['devpi', 'upload', '--formats', 'sdist,bdist_wheel'], env=env)
+ print("[generate.devpi_upload] package uploaded")
+
+
+@invoke.task(help={
+ 'version': 'version being released',
+ 'user': 'name of the user on devpi to stage the generated package',
+ 'password': 'user password on devpi to stage the generated package '
+ '(if not given assumed logged in)',
+})
+def pre_release(ctx, version, user, password=None):
+ """Generates new docs, release announcements and uploads a new release to devpi for testing."""
+ announce(ctx, version)
+ regen(ctx)
+ changelog(ctx, version, write_out=True)
+
+ msg = 'Preparing release version {}'.format(version)
+ check_call(['git', 'commit', '-a', '-m', msg])
+
+ make_tag(ctx, version)
+
+ devpi_upload(ctx, version=version, user=user, password=password)
+
+ print()
+ print('[generate.pre_release] Please push your branch and open a PR.')
+
+
+@invoke.task(help={
+ 'version': 'version being released',
+ 'user': 'name of the user on devpi to stage the generated package',
+ 'pypi_name': 'name of the pypi configuration section in your ~/.pypirc',
+})
+def publish_release(ctx, version, user, pypi_name):
+ """Publishes a package previously created by the 'pre_release' command."""
+ from git import Repo
+ repo = Repo('.')
+ tag_names = [x.name for x in repo.tags]
+ if version not in tag_names:
+ print('Could not find tag for version {}, exiting...'.format(version))
+ raise invoke.Exit(code=2)
+
+ check_call(['devpi', 'use', 'https://devpi.net/{}/dev'.format(user)])
+ check_call(['devpi', 'push', 'pytest=={}'.format(version), 'pypi:{}'.format(pypi_name)])
+ check_call(['git', 'push', 'git@github.com:pytest-dev/pytest.git', version])
+
+ emails = [
+ 'pytest-dev@python.org',
+ 'python-announce-list@python.org'
+ ]
+ if version.endswith('.0'):
+ emails.append('testing-in-python@lists.idyll.org')
+ print('Version {} has been published to PyPI!'.format(version))
+ print()
+ print('Please send an email announcement with the contents from:')
+ print()
+ print(' doc/en/announce/release-{}.rst'.format(version))
+ print()
+ print('To the following mail lists:')
+ print()
+ print(' ', ','.join(emails))
+ print()
+ print('And announce it on twitter adding the #pytest hash tag.')
+
+
+@invoke.task(help={
+ 'version': 'version being released',
+ 'write_out': 'write changes to the actial changelog'
+})
+def changelog(ctx, version, write_out=False):
+ if write_out:
+ addopts = []
+ else:
+ addopts = ['--draft']
+ check_call(['towncrier', '--version', version] + addopts)
+
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/release.minor.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/release.minor.rst
new file mode 100644
index 00000000000..3c0b7d718a1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/release.minor.rst
@@ -0,0 +1,27 @@
+pytest-{version}
+=======================================
+
+The pytest team is proud to announce the {version} release!
+
+pytest is a mature Python testing tool with more than a 1600 tests
+against itself, passing on many different interpreters and platforms.
+
+This release contains a number of bugs fixes and improvements, so users are encouraged
+to take a look at the CHANGELOG:
+
+ http://doc.pytest.org/en/latest/changelog.html
+
+For complete documentation, please visit:
+
+ http://docs.pytest.org
+
+As usual, you can upgrade from pypi via:
+
+ pip install -U pytest
+
+Thanks to all who contributed to this release, among them:
+
+{contributors}
+
+Happy testing,
+The Pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/release.patch.rst b/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/release.patch.rst
new file mode 100644
index 00000000000..56764b91307
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/release.patch.rst
@@ -0,0 +1,17 @@
+pytest-{version}
+=======================================
+
+pytest {version} has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
+
+Thanks to all who contributed to this release, among them:
+
+{contributors}
+
+Happy testing,
+The pytest Development Team
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/requirements.txt b/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/requirements.txt
new file mode 100644
index 00000000000..6392de0cc0a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/tasks/requirements.txt
@@ -0,0 +1,5 @@
+invoke
+tox
+gitpython
+towncrier
+wheel
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/acceptance_test.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/acceptance_test.py
new file mode 100644
index 00000000000..a7838545b07
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/acceptance_test.py
@@ -0,0 +1,850 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+import os
+import sys
+
+import _pytest._code
+import py
+import pytest
+from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
+
+
+class TestGeneralUsage(object):
+ def test_config_error(self, testdir):
+ testdir.makeconftest("""
+ def pytest_configure(config):
+ import pytest
+ raise pytest.UsageError("hello")
+ """)
+ result = testdir.runpytest(testdir.tmpdir)
+ assert result.ret != 0
+ result.stderr.fnmatch_lines([
+ '*ERROR: hello'
+ ])
+
+ def test_root_conftest_syntax_error(self, testdir):
+ testdir.makepyfile(conftest="raise SyntaxError\n")
+ result = testdir.runpytest()
+ result.stderr.fnmatch_lines(["*raise SyntaxError*"])
+ assert result.ret != 0
+
+ def test_early_hook_error_issue38_1(self, testdir):
+ testdir.makeconftest("""
+ def pytest_sessionstart():
+ 0 / 0
+ """)
+ result = testdir.runpytest(testdir.tmpdir)
+ assert result.ret != 0
+ # tracestyle is native by default for hook failures
+ result.stdout.fnmatch_lines([
+ '*INTERNALERROR*File*conftest.py*line 2*',
+ '*0 / 0*',
+ ])
+ result = testdir.runpytest(testdir.tmpdir, "--fulltrace")
+ assert result.ret != 0
+ # tracestyle is native by default for hook failures
+ result.stdout.fnmatch_lines([
+ '*INTERNALERROR*def pytest_sessionstart():*',
+ '*INTERNALERROR*0 / 0*',
+ ])
+
+ def test_early_hook_configure_error_issue38(self, testdir):
+ testdir.makeconftest("""
+ def pytest_configure():
+ 0 / 0
+ """)
+ result = testdir.runpytest(testdir.tmpdir)
+ assert result.ret != 0
+ # here we get it on stderr
+ result.stderr.fnmatch_lines([
+ '*INTERNALERROR*File*conftest.py*line 2*',
+ '*0 / 0*',
+ ])
+
+ def test_file_not_found(self, testdir):
+ result = testdir.runpytest("asd")
+ assert result.ret != 0
+ result.stderr.fnmatch_lines(["ERROR: file not found*asd"])
+
+ def test_file_not_found_unconfigure_issue143(self, testdir):
+ testdir.makeconftest("""
+ def pytest_configure():
+ print("---configure")
+ def pytest_unconfigure():
+ print("---unconfigure")
+ """)
+ result = testdir.runpytest("-s", "asd")
+ assert result.ret == 4 # EXIT_USAGEERROR
+ result.stderr.fnmatch_lines(["ERROR: file not found*asd"])
+ result.stdout.fnmatch_lines([
+ "*---configure",
+ "*---unconfigure",
+ ])
+
+ def test_config_preparse_plugin_option(self, testdir):
+ testdir.makepyfile(pytest_xyz="""
+ def pytest_addoption(parser):
+ parser.addoption("--xyz", dest="xyz", action="store")
+ """)
+ testdir.makepyfile(test_one="""
+ def test_option(pytestconfig):
+ assert pytestconfig.option.xyz == "123"
+ """)
+ result = testdir.runpytest("-p", "pytest_xyz", "--xyz=123", syspathinsert=True)
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ '*1 passed*',
+ ])
+
+ def test_assertion_magic(self, testdir):
+ p = testdir.makepyfile("""
+ def test_this():
+ x = 0
+ assert x
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "> assert x",
+ "E assert 0",
+ ])
+ assert result.ret == 1
+
+ def test_nested_import_error(self, testdir):
+ p = testdir.makepyfile("""
+ import import_fails
+ def test_this():
+ assert import_fails.a == 1
+ """)
+ testdir.makepyfile(import_fails="import does_not_work")
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ # XXX on jython this fails: "> import import_fails",
+ "ImportError while importing test module*",
+ "*No module named *does_not_work*",
+ ])
+ assert result.ret == 2
+
+ def test_not_collectable_arguments(self, testdir):
+ p1 = testdir.makepyfile("")
+ p2 = testdir.makefile(".pyc", "123")
+ result = testdir.runpytest(p1, p2)
+ assert result.ret
+ result.stderr.fnmatch_lines([
+ "*ERROR: not found:*%s" % (p2.basename,)
+ ])
+
+ def test_issue486_better_reporting_on_conftest_load_failure(self, testdir):
+ testdir.makepyfile("")
+ testdir.makeconftest("import qwerty")
+ result = testdir.runpytest("--help")
+ result.stdout.fnmatch_lines("""
+ *--version*
+ *warning*conftest.py*
+ """)
+ result = testdir.runpytest()
+ result.stderr.fnmatch_lines("""
+ *ERROR*could not load*conftest.py*
+ """)
+
+ def test_early_skip(self, testdir):
+ testdir.mkdir("xyz")
+ testdir.makeconftest("""
+ import pytest
+ def pytest_collect_directory():
+ pytest.skip("early")
+ """)
+ result = testdir.runpytest()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ result.stdout.fnmatch_lines([
+ "*1 skip*"
+ ])
+
+ def test_issue88_initial_file_multinodes(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ class MyFile(pytest.File):
+ def collect(self):
+ return [MyItem("hello", parent=self)]
+ def pytest_collect_file(path, parent):
+ return MyFile(path, parent)
+ class MyItem(pytest.Item):
+ pass
+ """)
+ p = testdir.makepyfile("def test_hello(): pass")
+ result = testdir.runpytest(p, "--collect-only")
+ result.stdout.fnmatch_lines([
+ "*MyFile*test_issue88*",
+ "*Module*test_issue88*",
+ ])
+
+ def test_issue93_initialnode_importing_capturing(self, testdir):
+ testdir.makeconftest("""
+ import sys
+ print ("should not be seen")
+ sys.stderr.write("stder42\\n")
+ """)
+ result = testdir.runpytest()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ assert "should not be seen" not in result.stdout.str()
+ assert "stderr42" not in result.stderr.str()
+
+ def test_conftest_printing_shows_if_error(self, testdir):
+ testdir.makeconftest("""
+ print ("should be seen")
+ assert 0
+ """)
+ result = testdir.runpytest()
+ assert result.ret != 0
+ assert "should be seen" in result.stdout.str()
+
+ @pytest.mark.skipif(not hasattr(py.path.local, 'mksymlinkto'),
+ reason="symlink not available on this platform")
+ def test_chdir(self, testdir):
+ testdir.tmpdir.join("py").mksymlinkto(py._pydir)
+ p = testdir.tmpdir.join("main.py")
+ p.write(_pytest._code.Source("""
+ import sys, os
+ sys.path.insert(0, '')
+ import py
+ print (py.__file__)
+ print (py.__path__)
+ os.chdir(os.path.dirname(os.getcwd()))
+ print (py.log)
+ """))
+ result = testdir.runpython(p)
+ assert not result.ret
+
+ def test_issue109_sibling_conftests_not_loaded(self, testdir):
+ sub1 = testdir.tmpdir.mkdir("sub1")
+ sub2 = testdir.tmpdir.mkdir("sub2")
+ sub1.join("conftest.py").write("assert 0")
+ result = testdir.runpytest(sub2)
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ sub2.ensure("__init__.py")
+ p = sub2.ensure("test_hello.py")
+ result = testdir.runpytest(p)
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ result = testdir.runpytest(sub1)
+ assert result.ret == EXIT_USAGEERROR
+
+ def test_directory_skipped(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ def pytest_ignore_collect():
+ pytest.skip("intentional")
+ """)
+ testdir.makepyfile("def test_hello(): pass")
+ result = testdir.runpytest()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ result.stdout.fnmatch_lines([
+ "*1 skipped*"
+ ])
+
+ def test_multiple_items_per_collector_byid(self, testdir):
+ c = testdir.makeconftest("""
+ import pytest
+ class MyItem(pytest.Item):
+ def runtest(self):
+ pass
+ class MyCollector(pytest.File):
+ def collect(self):
+ return [MyItem(name="xyz", parent=self)]
+ def pytest_collect_file(path, parent):
+ if path.basename.startswith("conftest"):
+ return MyCollector(path, parent)
+ """)
+ result = testdir.runpytest(c.basename + "::" + "xyz")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*1 pass*",
+ ])
+
+ def test_skip_on_generated_funcarg_id(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ def pytest_generate_tests(metafunc):
+ metafunc.addcall({'x': 3}, id='hello-123')
+ def pytest_runtest_setup(item):
+ print (item.keywords)
+ if 'hello-123' in item.keywords:
+ pytest.skip("hello")
+ assert 0
+ """)
+ p = testdir.makepyfile("""def test_func(x): pass""")
+ res = testdir.runpytest(p)
+ assert res.ret == 0
+ res.stdout.fnmatch_lines(["*1 skipped*"])
+
+ def test_direct_addressing_selects(self, testdir):
+ p = testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ metafunc.addcall({'i': 1}, id="1")
+ metafunc.addcall({'i': 2}, id="2")
+ def test_func(i):
+ pass
+ """)
+ res = testdir.runpytest(p.basename + "::" + "test_func[1]")
+ assert res.ret == 0
+ res.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_direct_addressing_notfound(self, testdir):
+ p = testdir.makepyfile("""
+ def test_func():
+ pass
+ """)
+ res = testdir.runpytest(p.basename + "::" + "test_notfound")
+ assert res.ret
+ res.stderr.fnmatch_lines(["*ERROR*not found*"])
+
+ def test_docstring_on_hookspec(self):
+ from _pytest import hookspec
+ for name, value in vars(hookspec).items():
+ if name.startswith("pytest_"):
+ assert value.__doc__, "no docstring for %s" % name
+
+ def test_initialization_error_issue49(self, testdir):
+ testdir.makeconftest("""
+ def pytest_configure():
+ x
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 3 # internal error
+ result.stderr.fnmatch_lines([
+ "INTERNAL*pytest_configure*",
+ "INTERNAL*x*",
+ ])
+ assert 'sessionstarttime' not in result.stderr.str()
+
+ @pytest.mark.parametrize('lookfor', ['test_fun.py::test_a'])
+ def test_issue134_report_error_when_collecting_member(self, testdir, lookfor):
+ testdir.makepyfile(test_fun="""
+ def test_a():
+ pass
+ def""")
+ result = testdir.runpytest(lookfor)
+ result.stdout.fnmatch_lines(['*SyntaxError*'])
+ if '::' in lookfor:
+ result.stderr.fnmatch_lines([
+ '*ERROR*',
+ ])
+ assert result.ret == 4 # usage error only if item not found
+
+ def test_report_all_failed_collections_initargs(self, testdir):
+ testdir.makepyfile(test_a="def", test_b="def")
+ result = testdir.runpytest("test_a.py::a", "test_b.py::b")
+ result.stderr.fnmatch_lines([
+ "*ERROR*test_a.py::a*",
+ "*ERROR*test_b.py::b*",
+ ])
+
+ @pytest.mark.usefixtures('recwarn')
+ def test_namespace_import_doesnt_confuse_import_hook(self, testdir):
+ """
+ Ref #383. Python 3.3's namespace package messed with our import hooks
+ Importing a module that didn't exist, even if the ImportError was
+ gracefully handled, would make our test crash.
+
+ Use recwarn here to silence this warning in Python 2.7:
+ ImportWarning: Not importing directory '...\not_a_package': missing __init__.py
+ """
+ testdir.mkdir('not_a_package')
+ p = testdir.makepyfile("""
+ try:
+ from not_a_package import doesnt_exist
+ except ImportError:
+ # We handle the import error gracefully here
+ pass
+
+ def test_whatever():
+ pass
+ """)
+ res = testdir.runpytest(p.basename)
+ assert res.ret == 0
+
+ def test_unknown_option(self, testdir):
+ result = testdir.runpytest("--qwlkej")
+ result.stderr.fnmatch_lines("""
+ *unrecognized*
+ """)
+
+ def test_getsourcelines_error_issue553(self, testdir, monkeypatch):
+ monkeypatch.setattr("inspect.getsourcelines", None)
+ p = testdir.makepyfile("""
+ def raise_error(obj):
+ raise IOError('source code not available')
+
+ import inspect
+ inspect.getsourcelines = raise_error
+
+ def test_foo(invalid_fixture):
+ pass
+ """)
+ res = testdir.runpytest(p)
+ res.stdout.fnmatch_lines([
+ "*source code not available*",
+ "E*fixture 'invalid_fixture' not found",
+ ])
+
+ def test_plugins_given_as_strings(self, tmpdir, monkeypatch):
+ """test that str values passed to main() as `plugins` arg
+ are interpreted as module names to be imported and registered.
+ #855.
+ """
+ with pytest.raises(ImportError) as excinfo:
+ pytest.main([str(tmpdir)], plugins=['invalid.module'])
+ assert 'invalid' in str(excinfo.value)
+
+ p = tmpdir.join('test_test_plugins_given_as_strings.py')
+ p.write('def test_foo(): pass')
+ mod = py.std.types.ModuleType("myplugin")
+ monkeypatch.setitem(sys.modules, 'myplugin', mod)
+ assert pytest.main(args=[str(tmpdir)], plugins=['myplugin']) == 0
+
+ def test_parametrized_with_bytes_regex(self, testdir):
+ p = testdir.makepyfile("""
+ import re
+ import pytest
+ @pytest.mark.parametrize('r', [re.compile(b'foo')])
+ def test_stuff(r):
+ pass
+ """
+ )
+ res = testdir.runpytest(p)
+ res.stdout.fnmatch_lines([
+ '*1 passed*'
+ ])
+
+ def test_parametrized_with_null_bytes(self, testdir):
+ """Test parametrization with values that contain null bytes and unicode characters (#2644, #2957)"""
+ p = testdir.makepyfile(u"""
+ # encoding: UTF-8
+ import pytest
+
+ @pytest.mark.parametrize("data", [b"\\x00", "\\x00", u'ação'])
+ def test_foo(data):
+ assert data
+ """)
+ res = testdir.runpytest(p)
+ res.assert_outcomes(passed=3)
+
+
+class TestInvocationVariants(object):
+ def test_earlyinit(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ assert hasattr(pytest, 'mark')
+ """)
+ result = testdir.runpython(p)
+ assert result.ret == 0
+
+ @pytest.mark.xfail("sys.platform.startswith('java')")
+ def test_pydoc(self, testdir):
+ for name in ('py.test', 'pytest'):
+ result = testdir.runpython_c("import %s;help(%s)" % (name, name))
+ assert result.ret == 0
+ s = result.stdout.str()
+ assert 'MarkGenerator' in s
+
+ def test_import_star_py_dot_test(self, testdir):
+ p = testdir.makepyfile("""
+ from py.test import *
+ #collect
+ #cmdline
+ #Item
+ # assert collect.Item is Item
+ # assert collect.Collector is Collector
+ main
+ skip
+ xfail
+ """)
+ result = testdir.runpython(p)
+ assert result.ret == 0
+
+ def test_import_star_pytest(self, testdir):
+ p = testdir.makepyfile("""
+ from pytest import *
+ #Item
+ #File
+ main
+ skip
+ xfail
+ """)
+ result = testdir.runpython(p)
+ assert result.ret == 0
+
+ def test_double_pytestcmdline(self, testdir):
+ p = testdir.makepyfile(run="""
+ import pytest
+ pytest.main()
+ pytest.main()
+ """)
+ testdir.makepyfile("""
+ def test_hello():
+ pass
+ """)
+ result = testdir.runpython(p)
+ result.stdout.fnmatch_lines([
+ "*1 passed*",
+ "*1 passed*",
+ ])
+
+ def test_python_minus_m_invocation_ok(self, testdir):
+ p1 = testdir.makepyfile("def test_hello(): pass")
+ res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1))
+ assert res.ret == 0
+
+ def test_python_minus_m_invocation_fail(self, testdir):
+ p1 = testdir.makepyfile("def test_fail(): 0/0")
+ res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1))
+ assert res.ret == 1
+
+ def test_python_pytest_package(self, testdir):
+ p1 = testdir.makepyfile("def test_pass(): pass")
+ res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1))
+ assert res.ret == 0
+ res.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_equivalence_pytest_pytest(self):
+ assert pytest.main == py.test.cmdline.main
+
+ def test_invoke_with_string(self, capsys):
+ retcode = pytest.main("-h")
+ assert not retcode
+ out, err = capsys.readouterr()
+ assert "--help" in out
+ pytest.raises(ValueError, lambda: pytest.main(0))
+
+ def test_invoke_with_path(self, tmpdir, capsys):
+ retcode = pytest.main(tmpdir)
+ assert retcode == EXIT_NOTESTSCOLLECTED
+ out, err = capsys.readouterr()
+
+ def test_invoke_plugin_api(self, testdir, capsys):
+ class MyPlugin(object):
+ def pytest_addoption(self, parser):
+ parser.addoption("--myopt")
+
+ pytest.main(["-h"], plugins=[MyPlugin()])
+ out, err = capsys.readouterr()
+ assert "--myopt" in out
+
+ def test_pyargs_importerror(self, testdir, monkeypatch):
+ monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', False)
+ path = testdir.mkpydir("tpkg")
+ path.join("test_hello.py").write('raise ImportError')
+
+ result = testdir.runpytest_subprocess("--pyargs", "tpkg.test_hello")
+ assert result.ret != 0
+
+ result.stdout.fnmatch_lines([
+ "collected*0*items*/*1*errors"
+ ])
+
+ def test_cmdline_python_package(self, testdir, monkeypatch):
+ import warnings
+ monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', False)
+ path = testdir.mkpydir("tpkg")
+ path.join("test_hello.py").write("def test_hello(): pass")
+ path.join("test_world.py").write("def test_world(): pass")
+ result = testdir.runpytest("--pyargs", "tpkg")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*2 passed*"
+ ])
+ result = testdir.runpytest("--pyargs", "tpkg.test_hello")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*1 passed*"
+ ])
+
+ def join_pythonpath(what):
+ cur = py.std.os.environ.get('PYTHONPATH')
+ if cur:
+ return str(what) + os.pathsep + cur
+ return what
+ empty_package = testdir.mkpydir("empty_package")
+ monkeypatch.setenv('PYTHONPATH', join_pythonpath(empty_package))
+ # the path which is not a package raises a warning on pypy;
+ # no idea why only pypy and not normal python warn about it here
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', ImportWarning)
+ result = testdir.runpytest("--pyargs", ".")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*2 passed*"
+ ])
+
+ monkeypatch.setenv('PYTHONPATH', join_pythonpath(testdir))
+ result = testdir.runpytest("--pyargs", "tpkg.test_missing")
+ assert result.ret != 0
+ result.stderr.fnmatch_lines([
+ "*not*found*test_missing*",
+ ])
+
+ def test_cmdline_python_namespace_package(self, testdir, monkeypatch):
+ """
+ test --pyargs option with namespace packages (#1567)
+ """
+ monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', raising=False)
+
+ search_path = []
+ for dirname in "hello", "world":
+ d = testdir.mkdir(dirname)
+ search_path.append(d)
+ ns = d.mkdir("ns_pkg")
+ ns.join("__init__.py").write(
+ "__import__('pkg_resources').declare_namespace(__name__)")
+ lib = ns.mkdir(dirname)
+ lib.ensure("__init__.py")
+ lib.join("test_{0}.py".format(dirname)). \
+ write("def test_{0}(): pass\n"
+ "def test_other():pass".format(dirname))
+
+ # The structure of the test directory is now:
+ # .
+ # ├── hello
+ # │   └── ns_pkg
+ # │   ├── __init__.py
+ # │   └── hello
+ # │   ├── __init__.py
+ # │   └── test_hello.py
+ # └── world
+ # └── ns_pkg
+ # ├── __init__.py
+ # └── world
+ # ├── __init__.py
+ # └── test_world.py
+
+ def join_pythonpath(*dirs):
+ cur = py.std.os.environ.get('PYTHONPATH')
+ if cur:
+ dirs += (cur,)
+ return os.pathsep.join(str(p) for p in dirs)
+ monkeypatch.setenv('PYTHONPATH', join_pythonpath(*search_path))
+ for p in search_path:
+ monkeypatch.syspath_prepend(p)
+
+ os.chdir('world')
+ # mixed module and filenames:
+ result = testdir.runpytest("--pyargs", "-v", "ns_pkg.hello", "ns_pkg/world")
+ testdir.chdir()
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*test_hello.py::test_hello*PASSED*",
+ "*test_hello.py::test_other*PASSED*",
+ "*test_world.py::test_world*PASSED*",
+ "*test_world.py::test_other*PASSED*",
+ "*4 passed*"
+ ])
+
+ # specify tests within a module
+ result = testdir.runpytest("--pyargs", "-v", "ns_pkg.world.test_world::test_other")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*test_world.py::test_other*PASSED*",
+ "*1 passed*"
+ ])
+
+ def test_cmdline_python_package_not_exists(self, testdir):
+ result = testdir.runpytest("--pyargs", "tpkgwhatv")
+ assert result.ret
+ result.stderr.fnmatch_lines([
+ "ERROR*file*or*package*not*found*",
+ ])
+
+ @pytest.mark.xfail(reason="decide: feature or bug")
+ def test_noclass_discovery_if_not_testcase(self, testdir):
+ testpath = testdir.makepyfile("""
+ import unittest
+ class TestHello(object):
+ def test_hello(self):
+ assert self.attr
+
+ class RealTest(unittest.TestCase, TestHello):
+ attr = 42
+ """)
+ reprec = testdir.inline_run(testpath)
+ reprec.assertoutcome(passed=1)
+
+ def test_doctest_id(self, testdir):
+ testdir.makefile('.txt', """
+ >>> x=3
+ >>> x
+ 4
+ """)
+ result = testdir.runpytest("-rf")
+ lines = result.stdout.str().splitlines()
+ for line in lines:
+ if line.startswith("FAIL "):
+ testid = line[5:].strip()
+ break
+ result = testdir.runpytest(testid, '-rf')
+ result.stdout.fnmatch_lines([
+ line,
+ "*1 failed*",
+ ])
+
+ def test_core_backward_compatibility(self):
+ """Test backward compatibility for get_plugin_manager function. See #787."""
+ import _pytest.config
+ assert type(_pytest.config.get_plugin_manager()) is _pytest.config.PytestPluginManager
+
+ def test_has_plugin(self, request):
+ """Test hasplugin function of the plugin manager (#932)."""
+ assert request.config.pluginmanager.hasplugin('python')
+
+
+class TestDurations(object):
+ source = """
+ import time
+ frag = 0.002
+ def test_something():
+ pass
+ def test_2():
+ time.sleep(frag*5)
+ def test_1():
+ time.sleep(frag)
+ def test_3():
+ time.sleep(frag*10)
+ """
+
+ def test_calls(self, testdir):
+ testdir.makepyfile(self.source)
+ result = testdir.runpytest("--durations=10")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines_random([
+ "*durations*",
+ "*call*test_3*",
+ "*call*test_2*",
+ "*call*test_1*",
+ ])
+
+ def test_calls_show_2(self, testdir):
+ testdir.makepyfile(self.source)
+ result = testdir.runpytest("--durations=2")
+ assert result.ret == 0
+ lines = result.stdout.get_lines_after("*slowest*durations*")
+ assert "4 passed" in lines[2]
+
+ def test_calls_showall(self, testdir):
+ testdir.makepyfile(self.source)
+ result = testdir.runpytest("--durations=0")
+ assert result.ret == 0
+ for x in "123":
+ for y in 'call', : # 'setup', 'call', 'teardown':
+ for line in result.stdout.lines:
+ if ("test_%s" % x) in line and y in line:
+ break
+ else:
+ raise AssertionError("not found %s %s" % (x, y))
+
+ def test_with_deselected(self, testdir):
+ testdir.makepyfile(self.source)
+ result = testdir.runpytest("--durations=2", "-k test_1")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*durations*",
+ "*call*test_1*",
+ ])
+
+ def test_with_failing_collection(self, testdir):
+ testdir.makepyfile(self.source)
+ testdir.makepyfile(test_collecterror="""xyz""")
+ result = testdir.runpytest("--durations=2", "-k test_1")
+ assert result.ret == 2
+ result.stdout.fnmatch_lines([
+ "*Interrupted: 1 errors during collection*",
+ ])
+ # Collection errors abort test execution, therefore no duration is
+ # output
+ assert "duration" not in result.stdout.str()
+
+ def test_with_not(self, testdir):
+ testdir.makepyfile(self.source)
+ result = testdir.runpytest("-k not 1")
+ assert result.ret == 0
+
+
+class TestDurationWithFixture(object):
+ source = """
+ import time
+ frag = 0.001
+ def setup_function(func):
+ time.sleep(frag * 3)
+ def test_1():
+ time.sleep(frag*2)
+ def test_2():
+ time.sleep(frag)
+ """
+
+ def test_setup_function(self, testdir):
+ testdir.makepyfile(self.source)
+ result = testdir.runpytest("--durations=10")
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines_random("""
+ *durations*
+ * setup *test_1*
+ * call *test_1*
+ """)
+
+
+def test_zipimport_hook(testdir, tmpdir):
+ """Test package loader is being used correctly (see #1837)."""
+ zipapp = pytest.importorskip('zipapp')
+ testdir.tmpdir.join('app').ensure(dir=1)
+ testdir.makepyfile(**{
+ 'app/foo.py': """
+ import pytest
+ def main():
+ pytest.main(['--pyarg', 'foo'])
+ """,
+ })
+ target = tmpdir.join('foo.zip')
+ zipapp.create_archive(str(testdir.tmpdir.join('app')), str(target), main='foo:main')
+ result = testdir.runpython(target)
+ assert result.ret == 0
+ result.stderr.fnmatch_lines(['*not found*foo*'])
+ assert 'INTERNALERROR>' not in result.stdout.str()
+
+
+def test_import_plugin_unicode_name(testdir):
+ testdir.makepyfile(
+ myplugin='',
+ )
+ testdir.makepyfile("""
+ def test(): pass
+ """)
+ testdir.makeconftest("""
+ pytest_plugins = [u'myplugin']
+ """)
+ r = testdir.runpytest()
+ assert r.ret == 0
+
+
+def test_deferred_hook_checking(testdir):
+ """
+ Check hooks as late as possible (#1821).
+ """
+ testdir.syspathinsert()
+ testdir.makepyfile(**{
+ 'plugin.py': """
+ class Hooks:
+ def pytest_my_hook(self, config):
+ pass
+
+ def pytest_configure(config):
+ config.pluginmanager.add_hookspecs(Hooks)
+ """,
+ 'conftest.py': """
+ pytest_plugins = ['plugin']
+ def pytest_my_hook(config):
+ return 40
+ """,
+ 'test_foo.py': """
+ def test(request):
+ assert request.config.hook.pytest_my_hook(config=request.config) == [40]
+ """
+ })
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['* 1 passed *'])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_code.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_code.py
new file mode 100644
index 00000000000..209a8ef19a0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_code.py
@@ -0,0 +1,196 @@
+# coding: utf-8
+from __future__ import absolute_import, division, print_function
+import sys
+
+import _pytest._code
+import py
+import pytest
+from test_excinfo import TWMock
+
+
+def test_ne():
+ code1 = _pytest._code.Code(compile('foo = "bar"', '', 'exec'))
+ assert code1 == code1
+ code2 = _pytest._code.Code(compile('foo = "baz"', '', 'exec'))
+ assert code2 != code1
+
+
+def test_code_gives_back_name_for_not_existing_file():
+ name = 'abc-123'
+ co_code = compile("pass\n", name, 'exec')
+ assert co_code.co_filename == name
+ code = _pytest._code.Code(co_code)
+ assert str(code.path) == name
+ assert code.fullsource is None
+
+
+def test_code_with_class():
+ class A(object):
+ pass
+ pytest.raises(TypeError, "_pytest._code.Code(A)")
+
+
+if True:
+ def x():
+ pass
+
+
+def test_code_fullsource():
+ code = _pytest._code.Code(x)
+ full = code.fullsource
+ assert 'test_code_fullsource()' in str(full)
+
+
+def test_code_source():
+ code = _pytest._code.Code(x)
+ src = code.source()
+ expected = """def x():
+ pass"""
+ assert str(src) == expected
+
+
+def test_frame_getsourcelineno_myself():
+ def func():
+ return sys._getframe(0)
+ f = func()
+ f = _pytest._code.Frame(f)
+ source, lineno = f.code.fullsource, f.lineno
+ assert source[lineno].startswith(" return sys._getframe(0)")
+
+
+def test_getstatement_empty_fullsource():
+ def func():
+ return sys._getframe(0)
+ f = func()
+ f = _pytest._code.Frame(f)
+ prop = f.code.__class__.fullsource
+ try:
+ f.code.__class__.fullsource = None
+ assert f.statement == _pytest._code.Source("")
+ finally:
+ f.code.__class__.fullsource = prop
+
+
+def test_code_from_func():
+ co = _pytest._code.Code(test_frame_getsourcelineno_myself)
+ assert co.firstlineno
+ assert co.path
+
+
+def test_unicode_handling():
+ value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
+
+ def f():
+ raise Exception(value)
+
+ excinfo = pytest.raises(Exception, f)
+ str(excinfo)
+ if sys.version_info[0] < 3:
+ unicode(excinfo)
+
+
+@pytest.mark.skipif(sys.version_info[0] >= 3, reason='python 2 only issue')
+def test_unicode_handling_syntax_error():
+ value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
+
+ def f():
+ raise SyntaxError('invalid syntax', (None, 1, 3, value))
+
+ excinfo = pytest.raises(Exception, f)
+ str(excinfo)
+ if sys.version_info[0] < 3:
+ unicode(excinfo)
+
+
+def test_code_getargs():
+ def f1(x):
+ pass
+ c1 = _pytest._code.Code(f1)
+ assert c1.getargs(var=True) == ('x',)
+
+ def f2(x, *y):
+ pass
+ c2 = _pytest._code.Code(f2)
+ assert c2.getargs(var=True) == ('x', 'y')
+
+ def f3(x, **z):
+ pass
+ c3 = _pytest._code.Code(f3)
+ assert c3.getargs(var=True) == ('x', 'z')
+
+ def f4(x, *y, **z):
+ pass
+ c4 = _pytest._code.Code(f4)
+ assert c4.getargs(var=True) == ('x', 'y', 'z')
+
+
+def test_frame_getargs():
+ def f1(x):
+ return sys._getframe(0)
+ fr1 = _pytest._code.Frame(f1('a'))
+ assert fr1.getargs(var=True) == [('x', 'a')]
+
+ def f2(x, *y):
+ return sys._getframe(0)
+ fr2 = _pytest._code.Frame(f2('a', 'b', 'c'))
+ assert fr2.getargs(var=True) == [('x', 'a'), ('y', ('b', 'c'))]
+
+ def f3(x, **z):
+ return sys._getframe(0)
+ fr3 = _pytest._code.Frame(f3('a', b='c'))
+ assert fr3.getargs(var=True) == [('x', 'a'), ('z', {'b': 'c'})]
+
+ def f4(x, *y, **z):
+ return sys._getframe(0)
+ fr4 = _pytest._code.Frame(f4('a', 'b', c='d'))
+ assert fr4.getargs(var=True) == [('x', 'a'), ('y', ('b',)),
+ ('z', {'c': 'd'})]
+
+
+class TestExceptionInfo(object):
+
+ def test_bad_getsource(self):
+ try:
+ if False:
+ pass
+ else:
+ assert False
+ except AssertionError:
+ exci = _pytest._code.ExceptionInfo()
+ assert exci.getrepr()
+
+
+class TestTracebackEntry(object):
+
+ def test_getsource(self):
+ try:
+ if False:
+ pass
+ else:
+ assert False
+ except AssertionError:
+ exci = _pytest._code.ExceptionInfo()
+ entry = exci.traceback[0]
+ source = entry.getsource()
+ assert len(source) == 6
+ assert 'assert False' in source[5]
+
+
+class TestReprFuncArgs(object):
+
+ def test_not_raise_exception_with_mixed_encoding(self):
+ from _pytest._code.code import ReprFuncArgs
+
+ tw = TWMock()
+
+ args = [
+ ('unicode_string', u"São Paulo"),
+ ('utf8_string', 'S\xc3\xa3o Paulo'),
+ ]
+
+ r = ReprFuncArgs(args)
+ r.toterminal(tw)
+ if sys.version_info[0] >= 3:
+ assert tw.lines[0] == 'unicode_string = São Paulo, utf8_string = São Paulo'
+ else:
+ assert tw.lines[0] == 'unicode_string = São Paulo, utf8_string = São Paulo'
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_excinfo.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_excinfo.py
new file mode 100644
index 00000000000..34db8ffa189
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_excinfo.py
@@ -0,0 +1,1245 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+
+import operator
+import _pytest
+import py
+import pytest
+from _pytest._code.code import (
+ ExceptionInfo,
+ FormattedExcinfo,
+ ReprExceptionInfo,
+ ExceptionChainRepr)
+
+from test_source import astonly
+
+try:
+ import importlib
+except ImportError:
+ invalidate_import_caches = None
+else:
+ invalidate_import_caches = getattr(importlib, "invalidate_caches", None)
+
+queue = py.builtin._tryimport('queue', 'Queue')
+
+failsonjython = pytest.mark.xfail("sys.platform.startswith('java')")
+
+pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
+
+
+class TWMock(object):
+ WRITE = object()
+
+ def __init__(self):
+ self.lines = []
+ self.is_writing = False
+
+ def sep(self, sep, line=None):
+ self.lines.append((sep, line))
+
+ def write(self, msg, **kw):
+ self.lines.append((TWMock.WRITE, msg))
+
+ def line(self, line, **kw):
+ self.lines.append(line)
+
+ def markup(self, text, **kw):
+ return text
+
+ def get_write_msg(self, idx):
+ flag, msg = self.lines[idx]
+ assert flag == TWMock.WRITE
+ return msg
+
+ fullwidth = 80
+
+
+def test_excinfo_simple():
+ try:
+ raise ValueError
+ except ValueError:
+ info = _pytest._code.ExceptionInfo()
+ assert info.type == ValueError
+
+
+def test_excinfo_getstatement():
+ def g():
+ raise ValueError
+
+ def f():
+ g()
+
+ try:
+ f()
+ except ValueError:
+ excinfo = _pytest._code.ExceptionInfo()
+ linenumbers = [_pytest._code.getrawcode(f).co_firstlineno - 1 + 4,
+ _pytest._code.getrawcode(f).co_firstlineno - 1 + 1,
+ _pytest._code.getrawcode(g).co_firstlineno - 1 + 1, ]
+ values = list(excinfo.traceback)
+ foundlinenumbers = [x.lineno for x in values]
+ assert foundlinenumbers == linenumbers
+ # for x in info:
+ # print "%s:%d %s" %(x.path.relto(root), x.lineno, x.statement)
+ # xxx
+
+# testchain for getentries test below
+
+
+def f():
+ #
+ raise ValueError
+ #
+
+
+def g():
+ #
+ __tracebackhide__ = True
+ f()
+ #
+
+
+def h():
+ #
+ g()
+ #
+
+
+class TestTraceback_f_g_h(object):
+ def setup_method(self, method):
+ try:
+ h()
+ except ValueError:
+ self.excinfo = _pytest._code.ExceptionInfo()
+
+ def test_traceback_entries(self):
+ tb = self.excinfo.traceback
+ entries = list(tb)
+ assert len(tb) == 4 # maybe fragile test
+ assert len(entries) == 4 # maybe fragile test
+ names = ['f', 'g', 'h']
+ for entry in entries:
+ try:
+ names.remove(entry.frame.code.name)
+ except ValueError:
+ pass
+ assert not names
+
+ def test_traceback_entry_getsource(self):
+ tb = self.excinfo.traceback
+ s = str(tb[-1].getsource())
+ assert s.startswith("def f():")
+ assert s.endswith("raise ValueError")
+
+ @astonly
+ @failsonjython
+ def test_traceback_entry_getsource_in_construct(self):
+ source = _pytest._code.Source("""\
+ def xyz():
+ try:
+ raise ValueError
+ except somenoname:
+ pass
+ xyz()
+ """)
+ try:
+ exec(source.compile())
+ except NameError:
+ tb = _pytest._code.ExceptionInfo().traceback
+ print(tb[-1].getsource())
+ s = str(tb[-1].getsource())
+ assert s.startswith("def xyz():\n try:")
+ assert s.strip().endswith("except somenoname:")
+
+ def test_traceback_cut(self):
+ co = _pytest._code.Code(f)
+ path, firstlineno = co.path, co.firstlineno
+ traceback = self.excinfo.traceback
+ newtraceback = traceback.cut(path=path, firstlineno=firstlineno)
+ assert len(newtraceback) == 1
+ newtraceback = traceback.cut(path=path, lineno=firstlineno + 2)
+ assert len(newtraceback) == 1
+
+ def test_traceback_cut_excludepath(self, testdir):
+ p = testdir.makepyfile("def f(): raise ValueError")
+ excinfo = pytest.raises(ValueError, "p.pyimport().f()")
+ basedir = py.path.local(pytest.__file__).dirpath()
+ newtraceback = excinfo.traceback.cut(excludepath=basedir)
+ for x in newtraceback:
+ if hasattr(x, 'path'):
+ assert not py.path.local(x.path).relto(basedir)
+ assert newtraceback[-1].frame.code.path == p
+
+ def test_traceback_filter(self):
+ traceback = self.excinfo.traceback
+ ntraceback = traceback.filter()
+ assert len(ntraceback) == len(traceback) - 1
+
+ @pytest.mark.parametrize('tracebackhide, matching', [
+ (lambda info: True, True),
+ (lambda info: False, False),
+ (operator.methodcaller('errisinstance', ValueError), True),
+ (operator.methodcaller('errisinstance', IndexError), False),
+ ])
+ def test_traceback_filter_selective(self, tracebackhide, matching):
+ def f():
+ #
+ raise ValueError
+ #
+
+ def g():
+ #
+ __tracebackhide__ = tracebackhide
+ f()
+ #
+
+ def h():
+ #
+ g()
+ #
+
+ excinfo = pytest.raises(ValueError, h)
+ traceback = excinfo.traceback
+ ntraceback = traceback.filter()
+ print('old: {0!r}'.format(traceback))
+ print('new: {0!r}'.format(ntraceback))
+
+ if matching:
+ assert len(ntraceback) == len(traceback) - 2
+ else:
+ # -1 because of the __tracebackhide__ in pytest.raises
+ assert len(ntraceback) == len(traceback) - 1
+
+ def test_traceback_recursion_index(self):
+ def f(n):
+ if n < 10:
+ n += 1
+ f(n)
+ excinfo = pytest.raises(RuntimeError, f, 8)
+ traceback = excinfo.traceback
+ recindex = traceback.recursionindex()
+ assert recindex == 3
+
+ def test_traceback_only_specific_recursion_errors(self, monkeypatch):
+ def f(n):
+ if n == 0:
+ raise RuntimeError("hello")
+ f(n - 1)
+
+ excinfo = pytest.raises(RuntimeError, f, 100)
+ monkeypatch.delattr(excinfo.traceback.__class__, "recursionindex")
+ repr = excinfo.getrepr()
+ assert "RuntimeError: hello" in str(repr.reprcrash)
+
+ def test_traceback_no_recursion_index(self):
+ def do_stuff():
+ raise RuntimeError
+
+ def reraise_me():
+ import sys
+ exc, val, tb = sys.exc_info()
+ py.builtin._reraise(exc, val, tb)
+
+ def f(n):
+ try:
+ do_stuff()
+ except: # noqa
+ reraise_me()
+
+ excinfo = pytest.raises(RuntimeError, f, 8)
+ traceback = excinfo.traceback
+ recindex = traceback.recursionindex()
+ assert recindex is None
+
+ def test_traceback_messy_recursion(self):
+ # XXX: simplified locally testable version
+ decorator = pytest.importorskip('decorator').decorator
+
+ def log(f, *k, **kw):
+ print('%s %s' % (k, kw))
+ f(*k, **kw)
+ log = decorator(log)
+
+ def fail():
+ raise ValueError('')
+
+ fail = log(log(fail))
+
+ excinfo = pytest.raises(ValueError, fail)
+ assert excinfo.traceback.recursionindex() is None
+
+ def test_traceback_getcrashentry(self):
+ def i():
+ __tracebackhide__ = True
+ raise ValueError
+
+ def h():
+ i()
+
+ def g():
+ __tracebackhide__ = True
+ h()
+
+ def f():
+ g()
+
+ excinfo = pytest.raises(ValueError, f)
+ tb = excinfo.traceback
+ entry = tb.getcrashentry()
+ co = _pytest._code.Code(h)
+ assert entry.frame.code.path == co.path
+ assert entry.lineno == co.firstlineno + 1
+ assert entry.frame.code.name == 'h'
+
+ def test_traceback_getcrashentry_empty(self):
+ def g():
+ __tracebackhide__ = True
+ raise ValueError
+
+ def f():
+ __tracebackhide__ = True
+ g()
+
+ excinfo = pytest.raises(ValueError, f)
+ tb = excinfo.traceback
+ entry = tb.getcrashentry()
+ co = _pytest._code.Code(g)
+ assert entry.frame.code.path == co.path
+ assert entry.lineno == co.firstlineno + 2
+ assert entry.frame.code.name == 'g'
+
+
+def test_excinfo_exconly():
+ excinfo = pytest.raises(ValueError, h)
+ assert excinfo.exconly().startswith('ValueError')
+ excinfo = pytest.raises(ValueError,
+ "raise ValueError('hello\\nworld')")
+ msg = excinfo.exconly(tryshort=True)
+ assert msg.startswith('ValueError')
+ assert msg.endswith("world")
+
+
+def test_excinfo_repr():
+ excinfo = pytest.raises(ValueError, h)
+ s = repr(excinfo)
+ assert s == "<ExceptionInfo ValueError tblen=4>"
+
+
+def test_excinfo_str():
+ excinfo = pytest.raises(ValueError, h)
+ s = str(excinfo)
+ assert s.startswith(__file__[:-9]) # pyc file and $py.class
+ assert s.endswith("ValueError")
+ assert len(s.split(":")) >= 3 # on windows it's 4
+
+
+def test_excinfo_errisinstance():
+ excinfo = pytest.raises(ValueError, h)
+ assert excinfo.errisinstance(ValueError)
+
+
+def test_excinfo_no_sourcecode():
+ try:
+ exec("raise ValueError()")
+ except ValueError:
+ excinfo = _pytest._code.ExceptionInfo()
+ s = str(excinfo.traceback[-1])
+ assert s == " File '<string>':1 in <module>\n ???\n"
+
+
+def test_excinfo_no_python_sourcecode(tmpdir):
+ # XXX: simplified locally testable version
+ tmpdir.join('test.txt').write("{{ h()}}:")
+
+ jinja2 = pytest.importorskip('jinja2')
+ loader = jinja2.FileSystemLoader(str(tmpdir))
+ env = jinja2.Environment(loader=loader)
+ template = env.get_template('test.txt')
+ excinfo = pytest.raises(ValueError,
+ template.render, h=h)
+ for item in excinfo.traceback:
+ print(item) # XXX: for some reason jinja.Template.render is printed in full
+ item.source # shouldnt fail
+ if item.path.basename == 'test.txt':
+ assert str(item.source) == '{{ h()}}:'
+
+
+def test_entrysource_Queue_example():
+ try:
+ queue.Queue().get(timeout=0.001)
+ except queue.Empty:
+ excinfo = _pytest._code.ExceptionInfo()
+ entry = excinfo.traceback[-1]
+ source = entry.getsource()
+ assert source is not None
+ s = str(source).strip()
+ assert s.startswith("def get")
+
+
+def test_codepath_Queue_example():
+ try:
+ queue.Queue().get(timeout=0.001)
+ except queue.Empty:
+ excinfo = _pytest._code.ExceptionInfo()
+ entry = excinfo.traceback[-1]
+ path = entry.path
+ assert isinstance(path, py.path.local)
+ assert path.basename.lower() == "queue.py"
+ assert path.check()
+
+
+def test_match_succeeds():
+ with pytest.raises(ZeroDivisionError) as excinfo:
+ 0 // 0
+ excinfo.match(r'.*zero.*')
+
+
+def test_match_raises_error(testdir):
+ testdir.makepyfile("""
+ import pytest
+ def test_division_zero():
+ with pytest.raises(ZeroDivisionError) as excinfo:
+ 0 / 0
+ excinfo.match(r'[123]+')
+ """)
+ result = testdir.runpytest()
+ assert result.ret != 0
+ result.stdout.fnmatch_lines([
+ "*AssertionError*Pattern*[123]*not found*",
+ ])
+
+
+class TestFormattedExcinfo(object):
+
+ @pytest.fixture
+ def importasmod(self, request):
+ def importasmod(source):
+ source = _pytest._code.Source(source)
+ tmpdir = request.getfixturevalue("tmpdir")
+ modpath = tmpdir.join("mod.py")
+ tmpdir.ensure("__init__.py")
+ modpath.write(source)
+ if invalidate_import_caches is not None:
+ invalidate_import_caches()
+ return modpath.pyimport()
+ return importasmod
+
+ def excinfo_from_exec(self, source):
+ source = _pytest._code.Source(source).strip()
+ try:
+ exec(source.compile())
+ except KeyboardInterrupt:
+ raise
+ except: # noqa
+ return _pytest._code.ExceptionInfo()
+ assert 0, "did not raise"
+
+ def test_repr_source(self):
+ pr = FormattedExcinfo()
+ source = _pytest._code.Source("""
+ def f(x):
+ pass
+ """).strip()
+ pr.flow_marker = "|"
+ lines = pr.get_source(source, 0)
+ assert len(lines) == 2
+ assert lines[0] == "| def f(x):"
+ assert lines[1] == " pass"
+
+ def test_repr_source_excinfo(self):
+ """ check if indentation is right """
+ pr = FormattedExcinfo()
+ excinfo = self.excinfo_from_exec("""
+ def f():
+ assert 0
+ f()
+ """)
+ pr = FormattedExcinfo()
+ source = pr._getentrysource(excinfo.traceback[-1])
+ lines = pr.get_source(source, 1, excinfo)
+ assert lines == [
+ ' def f():',
+ '> assert 0',
+ 'E AssertionError'
+ ]
+
+ def test_repr_source_not_existing(self):
+ pr = FormattedExcinfo()
+ co = compile("raise ValueError()", "", "exec")
+ try:
+ exec(co)
+ except ValueError:
+ excinfo = _pytest._code.ExceptionInfo()
+ repr = pr.repr_excinfo(excinfo)
+ assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
+ if py.std.sys.version_info[0] >= 3:
+ assert repr.chain[0][0].reprentries[1].lines[0] == "> ???"
+
+ def test_repr_many_line_source_not_existing(self):
+ pr = FormattedExcinfo()
+ co = compile("""
+a = 1
+raise ValueError()
+""", "", "exec")
+ try:
+ exec(co)
+ except ValueError:
+ excinfo = _pytest._code.ExceptionInfo()
+ repr = pr.repr_excinfo(excinfo)
+ assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
+ if py.std.sys.version_info[0] >= 3:
+ assert repr.chain[0][0].reprentries[1].lines[0] == "> ???"
+
+ def test_repr_source_failing_fullsource(self):
+ pr = FormattedExcinfo()
+
+ class FakeCode(object):
+ class raw(object):
+ co_filename = '?'
+
+ path = '?'
+ firstlineno = 5
+
+ def fullsource(self):
+ return None
+
+ fullsource = property(fullsource)
+
+ class FakeFrame(object):
+ code = FakeCode()
+ f_locals = {}
+ f_globals = {}
+
+ class FakeTracebackEntry(_pytest._code.Traceback.Entry):
+ def __init__(self, tb, excinfo=None):
+ self.lineno = 5 + 3
+
+ @property
+ def frame(self):
+ return FakeFrame()
+
+ class Traceback(_pytest._code.Traceback):
+ Entry = FakeTracebackEntry
+
+ class FakeExcinfo(_pytest._code.ExceptionInfo):
+ typename = "Foo"
+ value = Exception()
+
+ def __init__(self):
+ pass
+
+ def exconly(self, tryshort):
+ return "EXC"
+
+ def errisinstance(self, cls):
+ return False
+
+ excinfo = FakeExcinfo()
+
+ class FakeRawTB(object):
+ tb_next = None
+
+ tb = FakeRawTB()
+ excinfo.traceback = Traceback(tb)
+
+ fail = IOError() # noqa
+ repr = pr.repr_excinfo(excinfo)
+ assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
+ if py.std.sys.version_info[0] >= 3:
+ assert repr.chain[0][0].reprentries[0].lines[0] == "> ???"
+
+ fail = py.error.ENOENT # noqa
+ repr = pr.repr_excinfo(excinfo)
+ assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
+ if py.std.sys.version_info[0] >= 3:
+ assert repr.chain[0][0].reprentries[0].lines[0] == "> ???"
+
+ def test_repr_local(self):
+ p = FormattedExcinfo(showlocals=True)
+ loc = {'y': 5, 'z': 7, 'x': 3, '@x': 2, '__builtins__': {}}
+ reprlocals = p.repr_locals(loc)
+ assert reprlocals.lines
+ assert reprlocals.lines[0] == '__builtins__ = <builtins>'
+ assert reprlocals.lines[1] == 'x = 3'
+ assert reprlocals.lines[2] == 'y = 5'
+ assert reprlocals.lines[3] == 'z = 7'
+
+ def test_repr_tracebackentry_lines(self, importasmod):
+ mod = importasmod("""
+ def func1():
+ raise ValueError("hello\\nworld")
+ """)
+ excinfo = pytest.raises(ValueError, mod.func1)
+ excinfo.traceback = excinfo.traceback.filter()
+ p = FormattedExcinfo()
+ reprtb = p.repr_traceback_entry(excinfo.traceback[-1])
+
+ # test as intermittent entry
+ lines = reprtb.lines
+ assert lines[0] == ' def func1():'
+ assert lines[1] == '> raise ValueError("hello\\nworld")'
+
+ # test as last entry
+ p = FormattedExcinfo(showlocals=True)
+ repr_entry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
+ lines = repr_entry.lines
+ assert lines[0] == ' def func1():'
+ assert lines[1] == '> raise ValueError("hello\\nworld")'
+ assert lines[2] == 'E ValueError: hello'
+ assert lines[3] == 'E world'
+ assert not lines[4:]
+
+ loc = repr_entry.reprlocals is not None
+ loc = repr_entry.reprfileloc
+ assert loc.path == mod.__file__
+ assert loc.lineno == 3
+ # assert loc.message == "ValueError: hello"
+
+ def test_repr_tracebackentry_lines2(self, importasmod):
+ mod = importasmod("""
+ def func1(m, x, y, z):
+ raise ValueError("hello\\nworld")
+ """)
+ excinfo = pytest.raises(ValueError, mod.func1, "m" * 90, 5, 13, "z" * 120)
+ excinfo.traceback = excinfo.traceback.filter()
+ entry = excinfo.traceback[-1]
+ p = FormattedExcinfo(funcargs=True)
+ reprfuncargs = p.repr_args(entry)
+ assert reprfuncargs.args[0] == ('m', repr("m" * 90))
+ assert reprfuncargs.args[1] == ('x', '5')
+ assert reprfuncargs.args[2] == ('y', '13')
+ assert reprfuncargs.args[3] == ('z', repr("z" * 120))
+
+ p = FormattedExcinfo(funcargs=True)
+ repr_entry = p.repr_traceback_entry(entry)
+ assert repr_entry.reprfuncargs.args == reprfuncargs.args
+ tw = TWMock()
+ repr_entry.toterminal(tw)
+ assert tw.lines[0] == "m = " + repr('m' * 90)
+ assert tw.lines[1] == "x = 5, y = 13"
+ assert tw.lines[2] == "z = " + repr('z' * 120)
+
+ def test_repr_tracebackentry_lines_var_kw_args(self, importasmod):
+ mod = importasmod("""
+ def func1(x, *y, **z):
+ raise ValueError("hello\\nworld")
+ """)
+ excinfo = pytest.raises(ValueError, mod.func1, 'a', 'b', c='d')
+ excinfo.traceback = excinfo.traceback.filter()
+ entry = excinfo.traceback[-1]
+ p = FormattedExcinfo(funcargs=True)
+ reprfuncargs = p.repr_args(entry)
+ assert reprfuncargs.args[0] == ('x', repr('a'))
+ assert reprfuncargs.args[1] == ('y', repr(('b',)))
+ assert reprfuncargs.args[2] == ('z', repr({'c': 'd'}))
+
+ p = FormattedExcinfo(funcargs=True)
+ repr_entry = p.repr_traceback_entry(entry)
+ assert repr_entry.reprfuncargs.args == reprfuncargs.args
+ tw = TWMock()
+ repr_entry.toterminal(tw)
+ assert tw.lines[0] == "x = 'a', y = ('b',), z = {'c': 'd'}"
+
+ def test_repr_tracebackentry_short(self, importasmod):
+ mod = importasmod("""
+ def func1():
+ raise ValueError("hello")
+ def entry():
+ func1()
+ """)
+ excinfo = pytest.raises(ValueError, mod.entry)
+ p = FormattedExcinfo(style="short")
+ reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
+ lines = reprtb.lines
+ basename = py.path.local(mod.__file__).basename
+ assert lines[0] == ' func1()'
+ assert basename in str(reprtb.reprfileloc.path)
+ assert reprtb.reprfileloc.lineno == 5
+
+ # test last entry
+ p = FormattedExcinfo(style="short")
+ reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
+ lines = reprtb.lines
+ assert lines[0] == ' raise ValueError("hello")'
+ assert lines[1] == 'E ValueError: hello'
+ assert basename in str(reprtb.reprfileloc.path)
+ assert reprtb.reprfileloc.lineno == 3
+
+ def test_repr_tracebackentry_no(self, importasmod):
+ mod = importasmod("""
+ def func1():
+ raise ValueError("hello")
+ def entry():
+ func1()
+ """)
+ excinfo = pytest.raises(ValueError, mod.entry)
+ p = FormattedExcinfo(style="no")
+ p.repr_traceback_entry(excinfo.traceback[-2])
+
+ p = FormattedExcinfo(style="no")
+ reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
+ lines = reprentry.lines
+ assert lines[0] == 'E ValueError: hello'
+ assert not lines[1:]
+
+ def test_repr_traceback_tbfilter(self, importasmod):
+ mod = importasmod("""
+ def f(x):
+ raise ValueError(x)
+ def entry():
+ f(0)
+ """)
+ excinfo = pytest.raises(ValueError, mod.entry)
+ p = FormattedExcinfo(tbfilter=True)
+ reprtb = p.repr_traceback(excinfo)
+ assert len(reprtb.reprentries) == 2
+ p = FormattedExcinfo(tbfilter=False)
+ reprtb = p.repr_traceback(excinfo)
+ assert len(reprtb.reprentries) == 3
+
+ def test_traceback_short_no_source(self, importasmod, monkeypatch):
+ mod = importasmod("""
+ def func1():
+ raise ValueError("hello")
+ def entry():
+ func1()
+ """)
+ excinfo = pytest.raises(ValueError, mod.entry)
+ from _pytest._code.code import Code
+ monkeypatch.setattr(Code, 'path', 'bogus')
+ excinfo.traceback[0].frame.code.path = "bogus"
+ p = FormattedExcinfo(style="short")
+ reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
+ lines = reprtb.lines
+ last_p = FormattedExcinfo(style="short")
+ last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
+ last_lines = last_reprtb.lines
+ monkeypatch.undo()
+ assert lines[0] == ' func1()'
+
+ assert last_lines[0] == ' raise ValueError("hello")'
+ assert last_lines[1] == 'E ValueError: hello'
+
+ def test_repr_traceback_and_excinfo(self, importasmod):
+ mod = importasmod("""
+ def f(x):
+ raise ValueError(x)
+ def entry():
+ f(0)
+ """)
+ excinfo = pytest.raises(ValueError, mod.entry)
+
+ for style in ("long", "short"):
+ p = FormattedExcinfo(style=style)
+ reprtb = p.repr_traceback(excinfo)
+ assert len(reprtb.reprentries) == 2
+ assert reprtb.style == style
+ assert not reprtb.extraline
+ repr = p.repr_excinfo(excinfo)
+ assert repr.reprtraceback
+ assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries)
+ if py.std.sys.version_info[0] >= 3:
+ assert repr.chain[0][0]
+ assert len(repr.chain[0][0].reprentries) == len(reprtb.reprentries)
+ assert repr.reprcrash.path.endswith("mod.py")
+ assert repr.reprcrash.message == "ValueError: 0"
+
+ def test_repr_traceback_with_invalid_cwd(self, importasmod, monkeypatch):
+ mod = importasmod("""
+ def f(x):
+ raise ValueError(x)
+ def entry():
+ f(0)
+ """)
+ excinfo = pytest.raises(ValueError, mod.entry)
+
+ p = FormattedExcinfo()
+
+ def raiseos():
+ raise OSError(2)
+
+ monkeypatch.setattr(py.std.os, 'getcwd', raiseos)
+ assert p._makepath(__file__) == __file__
+ p.repr_traceback(excinfo)
+
+ def test_repr_excinfo_addouterr(self, importasmod):
+ mod = importasmod("""
+ def entry():
+ raise ValueError()
+ """)
+ excinfo = pytest.raises(ValueError, mod.entry)
+ repr = excinfo.getrepr()
+ repr.addsection("title", "content")
+ twmock = TWMock()
+ repr.toterminal(twmock)
+ assert twmock.lines[-1] == "content"
+ assert twmock.lines[-2] == ("-", "title")
+
+ def test_repr_excinfo_reprcrash(self, importasmod):
+ mod = importasmod("""
+ def entry():
+ raise ValueError()
+ """)
+ excinfo = pytest.raises(ValueError, mod.entry)
+ repr = excinfo.getrepr()
+ assert repr.reprcrash.path.endswith("mod.py")
+ assert repr.reprcrash.lineno == 3
+ assert repr.reprcrash.message == "ValueError"
+ assert str(repr.reprcrash).endswith("mod.py:3: ValueError")
+
+ def test_repr_traceback_recursion(self, importasmod):
+ mod = importasmod("""
+ def rec2(x):
+ return rec1(x+1)
+ def rec1(x):
+ return rec2(x-1)
+ def entry():
+ rec1(42)
+ """)
+ excinfo = pytest.raises(RuntimeError, mod.entry)
+
+ for style in ("short", "long", "no"):
+ p = FormattedExcinfo(style="short")
+ reprtb = p.repr_traceback(excinfo)
+ assert reprtb.extraline == "!!! Recursion detected (same locals & position)"
+ assert str(reprtb)
+
+ def test_reprexcinfo_getrepr(self, importasmod):
+ mod = importasmod("""
+ def f(x):
+ raise ValueError(x)
+ def entry():
+ f(0)
+ """)
+ excinfo = pytest.raises(ValueError, mod.entry)
+
+ for style in ("short", "long", "no"):
+ for showlocals in (True, False):
+ repr = excinfo.getrepr(style=style, showlocals=showlocals)
+ if py.std.sys.version_info[0] < 3:
+ assert isinstance(repr, ReprExceptionInfo)
+ assert repr.reprtraceback.style == style
+ if py.std.sys.version_info[0] >= 3:
+ assert isinstance(repr, ExceptionChainRepr)
+ for repr in repr.chain:
+ assert repr[0].style == style
+
+ def test_reprexcinfo_unicode(self):
+ from _pytest._code.code import TerminalRepr
+
+ class MyRepr(TerminalRepr):
+ def toterminal(self, tw):
+ tw.line(py.builtin._totext("я", "utf-8"))
+
+ x = py.builtin._totext(MyRepr())
+ assert x == py.builtin._totext("я", "utf-8")
+
+ def test_toterminal_long(self, importasmod):
+ mod = importasmod("""
+ def g(x):
+ raise ValueError(x)
+ def f():
+ g(3)
+ """)
+ excinfo = pytest.raises(ValueError, mod.f)
+ excinfo.traceback = excinfo.traceback.filter()
+ repr = excinfo.getrepr()
+ tw = TWMock()
+ repr.toterminal(tw)
+ assert tw.lines[0] == ""
+ tw.lines.pop(0)
+ assert tw.lines[0] == " def f():"
+ assert tw.lines[1] == "> g(3)"
+ assert tw.lines[2] == ""
+ line = tw.get_write_msg(3)
+ assert line.endswith("mod.py")
+ assert tw.lines[4] == (":5: ")
+ assert tw.lines[5] == ("_ ", None)
+ assert tw.lines[6] == ""
+ assert tw.lines[7] == " def g(x):"
+ assert tw.lines[8] == "> raise ValueError(x)"
+ assert tw.lines[9] == "E ValueError: 3"
+ assert tw.lines[10] == ""
+ line = tw.get_write_msg(11)
+ assert line.endswith("mod.py")
+ assert tw.lines[12] == ":3: ValueError"
+
+ def test_toterminal_long_missing_source(self, importasmod, tmpdir):
+ mod = importasmod("""
+ def g(x):
+ raise ValueError(x)
+ def f():
+ g(3)
+ """)
+ excinfo = pytest.raises(ValueError, mod.f)
+ tmpdir.join('mod.py').remove()
+ excinfo.traceback = excinfo.traceback.filter()
+ repr = excinfo.getrepr()
+ tw = TWMock()
+ repr.toterminal(tw)
+ assert tw.lines[0] == ""
+ tw.lines.pop(0)
+ assert tw.lines[0] == "> ???"
+ assert tw.lines[1] == ""
+ line = tw.get_write_msg(2)
+ assert line.endswith("mod.py")
+ assert tw.lines[3] == ":5: "
+ assert tw.lines[4] == ("_ ", None)
+ assert tw.lines[5] == ""
+ assert tw.lines[6] == "> ???"
+ assert tw.lines[7] == "E ValueError: 3"
+ assert tw.lines[8] == ""
+ line = tw.get_write_msg(9)
+ assert line.endswith("mod.py")
+ assert tw.lines[10] == ":3: ValueError"
+
+ def test_toterminal_long_incomplete_source(self, importasmod, tmpdir):
+ mod = importasmod("""
+ def g(x):
+ raise ValueError(x)
+ def f():
+ g(3)
+ """)
+ excinfo = pytest.raises(ValueError, mod.f)
+ tmpdir.join('mod.py').write('asdf')
+ excinfo.traceback = excinfo.traceback.filter()
+ repr = excinfo.getrepr()
+ tw = TWMock()
+ repr.toterminal(tw)
+ assert tw.lines[0] == ""
+ tw.lines.pop(0)
+ assert tw.lines[0] == "> ???"
+ assert tw.lines[1] == ""
+ line = tw.get_write_msg(2)
+ assert line.endswith("mod.py")
+ assert tw.lines[3] == ":5: "
+ assert tw.lines[4] == ("_ ", None)
+ assert tw.lines[5] == ""
+ assert tw.lines[6] == "> ???"
+ assert tw.lines[7] == "E ValueError: 3"
+ assert tw.lines[8] == ""
+ line = tw.get_write_msg(9)
+ assert line.endswith("mod.py")
+ assert tw.lines[10] == ":3: ValueError"
+
+ def test_toterminal_long_filenames(self, importasmod):
+ mod = importasmod("""
+ def f():
+ raise ValueError()
+ """)
+ excinfo = pytest.raises(ValueError, mod.f)
+ tw = TWMock()
+ path = py.path.local(mod.__file__)
+ old = path.dirpath().chdir()
+ try:
+ repr = excinfo.getrepr(abspath=False)
+ repr.toterminal(tw)
+ x = py.path.local().bestrelpath(path)
+ if len(x) < len(str(path)):
+ msg = tw.get_write_msg(-2)
+ assert msg == "mod.py"
+ assert tw.lines[-1] == ":3: ValueError"
+
+ repr = excinfo.getrepr(abspath=True)
+ repr.toterminal(tw)
+ msg = tw.get_write_msg(-2)
+ assert msg == path
+ line = tw.lines[-1]
+ assert line == ":3: ValueError"
+ finally:
+ old.chdir()
+
+ @pytest.mark.parametrize('reproptions', [
+ {'style': style, 'showlocals': showlocals,
+ 'funcargs': funcargs, 'tbfilter': tbfilter
+ } for style in ("long", "short", "no")
+ for showlocals in (True, False)
+ for tbfilter in (True, False)
+ for funcargs in (True, False)])
+ def test_format_excinfo(self, importasmod, reproptions):
+ mod = importasmod("""
+ def g(x):
+ raise ValueError(x)
+ def f():
+ g(3)
+ """)
+ excinfo = pytest.raises(ValueError, mod.f)
+ tw = py.io.TerminalWriter(stringio=True)
+ repr = excinfo.getrepr(**reproptions)
+ repr.toterminal(tw)
+ assert tw.stringio.getvalue()
+
+ def test_traceback_repr_style(self, importasmod):
+ mod = importasmod("""
+ def f():
+ g()
+ def g():
+ h()
+ def h():
+ i()
+ def i():
+ raise ValueError()
+ """)
+ excinfo = pytest.raises(ValueError, mod.f)
+ excinfo.traceback = excinfo.traceback.filter()
+ excinfo.traceback[1].set_repr_style("short")
+ excinfo.traceback[2].set_repr_style("short")
+ r = excinfo.getrepr(style="long")
+ tw = TWMock()
+ r.toterminal(tw)
+ for line in tw.lines:
+ print(line)
+ assert tw.lines[0] == ""
+ assert tw.lines[1] == " def f():"
+ assert tw.lines[2] == "> g()"
+ assert tw.lines[3] == ""
+ msg = tw.get_write_msg(4)
+ assert msg.endswith("mod.py")
+ assert tw.lines[5] == ":3: "
+ assert tw.lines[6] == ("_ ", None)
+ tw.get_write_msg(7)
+ assert tw.lines[8].endswith("in g")
+ assert tw.lines[9] == " h()"
+ tw.get_write_msg(10)
+ assert tw.lines[11].endswith("in h")
+ assert tw.lines[12] == " i()"
+ assert tw.lines[13] == ("_ ", None)
+ assert tw.lines[14] == ""
+ assert tw.lines[15] == " def i():"
+ assert tw.lines[16] == "> raise ValueError()"
+ assert tw.lines[17] == "E ValueError"
+ assert tw.lines[18] == ""
+ msg = tw.get_write_msg(19)
+ msg.endswith("mod.py")
+ assert tw.lines[20] == ":9: ValueError"
+
+ @pytest.mark.skipif("sys.version_info[0] < 3")
+ def test_exc_chain_repr(self, importasmod):
+ mod = importasmod("""
+ class Err(Exception):
+ pass
+ def f():
+ try:
+ g()
+ except Exception as e:
+ raise Err() from e
+ finally:
+ h()
+ def g():
+ raise ValueError()
+
+ def h():
+ raise AttributeError()
+ """)
+ excinfo = pytest.raises(AttributeError, mod.f)
+ r = excinfo.getrepr(style="long")
+ tw = TWMock()
+ r.toterminal(tw)
+ for line in tw.lines:
+ print(line)
+ assert tw.lines[0] == ""
+ assert tw.lines[1] == " def f():"
+ assert tw.lines[2] == " try:"
+ assert tw.lines[3] == "> g()"
+ assert tw.lines[4] == ""
+ line = tw.get_write_msg(5)
+ assert line.endswith('mod.py')
+ assert tw.lines[6] == ':6: '
+ assert tw.lines[7] == ("_ ", None)
+ assert tw.lines[8] == ""
+ assert tw.lines[9] == " def g():"
+ assert tw.lines[10] == "> raise ValueError()"
+ assert tw.lines[11] == "E ValueError"
+ assert tw.lines[12] == ""
+ line = tw.get_write_msg(13)
+ assert line.endswith('mod.py')
+ assert tw.lines[14] == ':12: ValueError'
+ assert tw.lines[15] == ""
+ assert tw.lines[16] == "The above exception was the direct cause of the following exception:"
+ assert tw.lines[17] == ""
+ assert tw.lines[18] == " def f():"
+ assert tw.lines[19] == " try:"
+ assert tw.lines[20] == " g()"
+ assert tw.lines[21] == " except Exception as e:"
+ assert tw.lines[22] == "> raise Err() from e"
+ assert tw.lines[23] == "E test_exc_chain_repr0.mod.Err"
+ assert tw.lines[24] == ""
+ line = tw.get_write_msg(25)
+ assert line.endswith('mod.py')
+ assert tw.lines[26] == ":8: Err"
+ assert tw.lines[27] == ""
+ assert tw.lines[28] == "During handling of the above exception, another exception occurred:"
+ assert tw.lines[29] == ""
+ assert tw.lines[30] == " def f():"
+ assert tw.lines[31] == " try:"
+ assert tw.lines[32] == " g()"
+ assert tw.lines[33] == " except Exception as e:"
+ assert tw.lines[34] == " raise Err() from e"
+ assert tw.lines[35] == " finally:"
+ assert tw.lines[36] == "> h()"
+ assert tw.lines[37] == ""
+ line = tw.get_write_msg(38)
+ assert line.endswith('mod.py')
+ assert tw.lines[39] == ":10: "
+ assert tw.lines[40] == ('_ ', None)
+ assert tw.lines[41] == ""
+ assert tw.lines[42] == " def h():"
+ assert tw.lines[43] == "> raise AttributeError()"
+ assert tw.lines[44] == "E AttributeError"
+ assert tw.lines[45] == ""
+ line = tw.get_write_msg(46)
+ assert line.endswith('mod.py')
+ assert tw.lines[47] == ":15: AttributeError"
+
+ @pytest.mark.skipif("sys.version_info[0] < 3")
+ def test_exc_repr_with_raise_from_none_chain_suppression(self, importasmod):
+ mod = importasmod("""
+ def f():
+ try:
+ g()
+ except Exception:
+ raise AttributeError() from None
+ def g():
+ raise ValueError()
+ """)
+ excinfo = pytest.raises(AttributeError, mod.f)
+ r = excinfo.getrepr(style="long")
+ tw = TWMock()
+ r.toterminal(tw)
+ for line in tw.lines:
+ print(line)
+ assert tw.lines[0] == ""
+ assert tw.lines[1] == " def f():"
+ assert tw.lines[2] == " try:"
+ assert tw.lines[3] == " g()"
+ assert tw.lines[4] == " except Exception:"
+ assert tw.lines[5] == "> raise AttributeError() from None"
+ assert tw.lines[6] == "E AttributeError"
+ assert tw.lines[7] == ""
+ line = tw.get_write_msg(8)
+ assert line.endswith('mod.py')
+ assert tw.lines[9] == ":6: AttributeError"
+ assert len(tw.lines) == 10
+
+ @pytest.mark.skipif("sys.version_info[0] < 3")
+ @pytest.mark.parametrize('reason, description', [
+ ('cause', 'The above exception was the direct cause of the following exception:'),
+ ('context', 'During handling of the above exception, another exception occurred:'),
+ ])
+ def test_exc_chain_repr_without_traceback(self, importasmod, reason, description):
+ """
+ Handle representation of exception chains where one of the exceptions doesn't have a
+ real traceback, such as those raised in a subprocess submitted by the multiprocessing
+ module (#1984).
+ """
+ from _pytest.pytester import LineMatcher
+ exc_handling_code = ' from e' if reason == 'cause' else ''
+ mod = importasmod("""
+ def f():
+ try:
+ g()
+ except Exception as e:
+ raise RuntimeError('runtime problem'){exc_handling_code}
+ def g():
+ raise ValueError('invalid value')
+ """.format(exc_handling_code=exc_handling_code))
+
+ with pytest.raises(RuntimeError) as excinfo:
+ mod.f()
+
+ # emulate the issue described in #1984
+ attr = '__%s__' % reason
+ getattr(excinfo.value, attr).__traceback__ = None
+
+ r = excinfo.getrepr()
+ tw = py.io.TerminalWriter(stringio=True)
+ tw.hasmarkup = False
+ r.toterminal(tw)
+
+ matcher = LineMatcher(tw.stringio.getvalue().splitlines())
+ matcher.fnmatch_lines([
+ "ValueError: invalid value",
+ description,
+ "* except Exception as e:",
+ "> * raise RuntimeError('runtime problem')" + exc_handling_code,
+ "E *RuntimeError: runtime problem",
+ ])
+
+
+@pytest.mark.parametrize("style", ["short", "long"])
+@pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])
+def test_repr_traceback_with_unicode(style, encoding):
+ msg = u'☹'
+ if encoding is not None:
+ msg = msg.encode(encoding)
+ try:
+ raise RuntimeError(msg)
+ except RuntimeError:
+ e_info = ExceptionInfo()
+ formatter = FormattedExcinfo(style=style)
+ repr_traceback = formatter.repr_traceback(e_info)
+ assert repr_traceback is not None
+
+
+def test_cwd_deleted(testdir):
+ testdir.makepyfile("""
+ def test(tmpdir):
+ tmpdir.chdir()
+ tmpdir.remove()
+ assert False
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['* 1 failed in *'])
+ assert 'INTERNALERROR' not in result.stdout.str() + result.stderr.str()
+
+
+def test_exception_repr_extraction_error_on_recursion():
+ """
+ Ensure we can properly detect a recursion error even
+ if some locals raise error on comparision (#2459).
+ """
+ class numpy_like(object):
+
+ def __eq__(self, other):
+ if type(other) is numpy_like:
+ raise ValueError('The truth value of an array '
+ 'with more than one element is ambiguous.')
+
+ def a(x):
+ return b(numpy_like())
+
+ def b(x):
+ return a(numpy_like())
+
+ try:
+ a(numpy_like())
+ except: # noqa
+ from _pytest._code.code import ExceptionInfo
+ from _pytest.pytester import LineMatcher
+ exc_info = ExceptionInfo()
+
+ matcher = LineMatcher(str(exc_info.getrepr()).splitlines())
+ matcher.fnmatch_lines([
+ '!!! Recursion error detected, but an error occurred locating the origin of recursion.',
+ '*The following exception happened*',
+ '*ValueError: The truth value of an array*',
+ ])
+
+
+def test_no_recursion_index_on_recursion_error():
+ """
+ Ensure that we don't break in case we can't find the recursion index
+ during a recursion error (#2486).
+ """
+ try:
+ class RecursionDepthError(object):
+ def __getattr__(self, attr):
+ return getattr(self, '_' + attr)
+
+ RecursionDepthError().trigger
+ except: # noqa
+ from _pytest._code.code import ExceptionInfo
+ exc_info = ExceptionInfo()
+ assert 'maximum recursion' in str(exc_info.getrepr())
+ else:
+ assert 0
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_source.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_source.py
new file mode 100644
index 00000000000..8eda68a6e2f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_source.py
@@ -0,0 +1,680 @@
+# flake8: noqa
+# disable flake check on this file because some constructs are strange
+# or redundant on purpose and can't be disable on a line-by-line basis
+from __future__ import absolute_import, division, print_function
+import sys
+
+import _pytest._code
+import py
+import pytest
+from _pytest._code import Source
+from _pytest._code.source import _ast
+
+if _ast is not None:
+ astonly = pytest.mark.nothing
+else:
+ astonly = pytest.mark.xfail("True", reason="only works with AST-compile")
+
+failsonjython = pytest.mark.xfail("sys.platform.startswith('java')")
+
+
+def test_source_str_function():
+ x = Source("3")
+ assert str(x) == "3"
+
+ x = Source(" 3")
+ assert str(x) == "3"
+
+ x = Source("""
+ 3
+ """, rstrip=False)
+ assert str(x) == "\n3\n "
+
+ x = Source("""
+ 3
+ """, rstrip=True)
+ assert str(x) == "\n3"
+
+
+def test_unicode():
+ try:
+ unicode
+ except NameError:
+ return
+ x = Source(unicode("4"))
+ assert str(x) == "4"
+ co = _pytest._code.compile(unicode('u"\xc3\xa5"', 'utf8'), mode='eval')
+ val = eval(co)
+ assert isinstance(val, unicode)
+
+
+def test_source_from_function():
+ source = _pytest._code.Source(test_source_str_function)
+ assert str(source).startswith('def test_source_str_function():')
+
+
+def test_source_from_method():
+ class TestClass(object):
+ def test_method(self):
+ pass
+ source = _pytest._code.Source(TestClass().test_method)
+ assert source.lines == ["def test_method(self):",
+ " pass"]
+
+
+def test_source_from_lines():
+ lines = ["a \n", "b\n", "c"]
+ source = _pytest._code.Source(lines)
+ assert source.lines == ['a ', 'b', 'c']
+
+
+def test_source_from_inner_function():
+ def f():
+ pass
+ source = _pytest._code.Source(f, deindent=False)
+ assert str(source).startswith(' def f():')
+ source = _pytest._code.Source(f)
+ assert str(source).startswith('def f():')
+
+
+def test_source_putaround_simple():
+ source = Source("raise ValueError")
+ source = source.putaround(
+ "try:", """\
+ except ValueError:
+ x = 42
+ else:
+ x = 23""")
+ assert str(source) == """\
+try:
+ raise ValueError
+except ValueError:
+ x = 42
+else:
+ x = 23"""
+
+
+def test_source_putaround():
+ source = Source()
+ source = source.putaround("""
+ if 1:
+ x=1
+ """)
+ assert str(source).strip() == "if 1:\n x=1"
+
+
+def test_source_strips():
+ source = Source("")
+ assert source == Source()
+ assert str(source) == ''
+ assert source.strip() == source
+
+
+def test_source_strip_multiline():
+ source = Source()
+ source.lines = ["", " hello", " "]
+ source2 = source.strip()
+ assert source2.lines == [" hello"]
+
+
+def test_syntaxerror_rerepresentation():
+ ex = pytest.raises(SyntaxError, _pytest._code.compile, 'xyz xyz')
+ assert ex.value.lineno == 1
+ assert ex.value.offset in (4, 7) # XXX pypy/jython versus cpython?
+ assert ex.value.text.strip(), 'x x'
+
+
+def test_isparseable():
+ assert Source("hello").isparseable()
+ assert Source("if 1:\n pass").isparseable()
+ assert Source(" \nif 1:\n pass").isparseable()
+ assert not Source("if 1:\n").isparseable()
+ assert not Source(" \nif 1:\npass").isparseable()
+ assert not Source(chr(0)).isparseable()
+
+
+class TestAccesses(object):
+ source = Source("""\
+ def f(x):
+ pass
+ def g(x):
+ pass
+ """)
+
+ def test_getrange(self):
+ x = self.source[0:2]
+ assert x.isparseable()
+ assert len(x.lines) == 2
+ assert str(x) == "def f(x):\n pass"
+
+ def test_getline(self):
+ x = self.source[0]
+ assert x == "def f(x):"
+
+ def test_len(self):
+ assert len(self.source) == 4
+
+ def test_iter(self):
+ values = [x for x in self.source]
+ assert len(values) == 4
+
+
+class TestSourceParsingAndCompiling(object):
+ source = Source("""\
+ def f(x):
+ assert (x ==
+ 3 +
+ 4)
+ """).strip()
+
+ def test_compile(self):
+ co = _pytest._code.compile("x=3")
+ d = {}
+ exec(co, d)
+ assert d['x'] == 3
+
+ def test_compile_and_getsource_simple(self):
+ co = _pytest._code.compile("x=3")
+ exec(co)
+ source = _pytest._code.Source(co)
+ assert str(source) == "x=3"
+
+ def test_compile_and_getsource_through_same_function(self):
+ def gensource(source):
+ return _pytest._code.compile(source)
+ co1 = gensource("""
+ def f():
+ raise KeyError()
+ """)
+ co2 = gensource("""
+ def f():
+ raise ValueError()
+ """)
+ source1 = py.std.inspect.getsource(co1)
+ assert 'KeyError' in source1
+ source2 = py.std.inspect.getsource(co2)
+ assert 'ValueError' in source2
+
+ def test_getstatement(self):
+ # print str(self.source)
+ ass = str(self.source[1:])
+ for i in range(1, 4):
+ # print "trying start in line %r" % self.source[i]
+ s = self.source.getstatement(i)
+ #x = s.deindent()
+ assert str(s) == ass
+
+ def test_getstatementrange_triple_quoted(self):
+ # print str(self.source)
+ source = Source("""hello('''
+ ''')""")
+ s = source.getstatement(0)
+ assert s == str(source)
+ s = source.getstatement(1)
+ assert s == str(source)
+
+ @astonly
+ def test_getstatementrange_within_constructs(self):
+ source = Source("""\
+ try:
+ try:
+ raise ValueError
+ except SomeThing:
+ pass
+ finally:
+ 42
+ """)
+ assert len(source) == 7
+ # check all lineno's that could occur in a traceback
+ # assert source.getstatementrange(0) == (0, 7)
+ # assert source.getstatementrange(1) == (1, 5)
+ assert source.getstatementrange(2) == (2, 3)
+ assert source.getstatementrange(3) == (3, 4)
+ assert source.getstatementrange(4) == (4, 5)
+ # assert source.getstatementrange(5) == (0, 7)
+ assert source.getstatementrange(6) == (6, 7)
+
+ def test_getstatementrange_bug(self):
+ source = Source("""\
+ try:
+ x = (
+ y +
+ z)
+ except:
+ pass
+ """)
+ assert len(source) == 6
+ assert source.getstatementrange(2) == (1, 4)
+
+ def test_getstatementrange_bug2(self):
+ source = Source("""\
+ assert (
+ 33
+ ==
+ [
+ X(3,
+ b=1, c=2
+ ),
+ ]
+ )
+ """)
+ assert len(source) == 9
+ assert source.getstatementrange(5) == (0, 9)
+
+ def test_getstatementrange_ast_issue58(self):
+ source = Source("""\
+
+ def test_some():
+ for a in [a for a in
+ CAUSE_ERROR]: pass
+
+ x = 3
+ """)
+ assert getstatement(2, source).lines == source.lines[2:3]
+ assert getstatement(3, source).lines == source.lines[3:4]
+
+ def test_getstatementrange_out_of_bounds_py3(self):
+ source = Source("if xxx:\n from .collections import something")
+ r = source.getstatementrange(1)
+ assert r == (1, 2)
+
+ def test_getstatementrange_with_syntaxerror_issue7(self):
+ source = Source(":")
+ pytest.raises(SyntaxError, lambda: source.getstatementrange(0))
+
+ def test_compile_to_ast(self):
+ import ast
+ source = Source("x = 4")
+ mod = source.compile(flag=ast.PyCF_ONLY_AST)
+ assert isinstance(mod, ast.Module)
+ compile(mod, "<filename>", "exec")
+
+ def test_compile_and_getsource(self):
+ co = self.source.compile()
+ py.builtin.exec_(co, globals())
+ f(7)
+ excinfo = pytest.raises(AssertionError, "f(6)")
+ frame = excinfo.traceback[-1].frame
+ stmt = frame.code.fullsource.getstatement(frame.lineno)
+ # print "block", str(block)
+ assert str(stmt).strip().startswith('assert')
+
+ @pytest.mark.parametrize('name', ['', None, 'my'])
+ def test_compilefuncs_and_path_sanity(self, name):
+ def check(comp, name):
+ co = comp(self.source, name)
+ if not name:
+ expected = "codegen %s:%d>" % (mypath, mylineno + 2 + 2)
+ else:
+ expected = "codegen %r %s:%d>" % (name, mypath, mylineno + 2 + 2)
+ fn = co.co_filename
+ assert fn.endswith(expected)
+
+ mycode = _pytest._code.Code(self.test_compilefuncs_and_path_sanity)
+ mylineno = mycode.firstlineno
+ mypath = mycode.path
+
+ for comp in _pytest._code.compile, _pytest._code.Source.compile:
+ check(comp, name)
+
+ def test_offsetless_synerr(self):
+ pytest.raises(SyntaxError, _pytest._code.compile, "lambda a,a: 0", mode='eval')
+
+
+def test_getstartingblock_singleline():
+ class A(object):
+ def __init__(self, *args):
+ frame = sys._getframe(1)
+ self.source = _pytest._code.Frame(frame).statement
+
+ x = A('x', 'y')
+
+ values = [i for i in x.source.lines if i.strip()]
+ assert len(values) == 1
+
+
+def test_getline_finally():
+ def c(): pass
+ excinfo = pytest.raises(TypeError, """
+ teardown = None
+ try:
+ c(1)
+ finally:
+ if teardown:
+ teardown()
+ """)
+ source = excinfo.traceback[-1].statement
+ assert str(source).strip() == 'c(1)'
+
+
+def test_getfuncsource_dynamic():
+ source = """
+ def f():
+ raise ValueError
+
+ def g(): pass
+ """
+ co = _pytest._code.compile(source)
+ py.builtin.exec_(co, globals())
+ assert str(_pytest._code.Source(f)).strip() == 'def f():\n raise ValueError'
+ assert str(_pytest._code.Source(g)).strip() == 'def g(): pass'
+
+
+def test_getfuncsource_with_multine_string():
+ def f():
+ c = '''while True:
+ pass
+'''
+ assert str(_pytest._code.Source(f)).strip() == "def f():\n c = '''while True:\n pass\n'''"
+
+
+def test_deindent():
+ from _pytest._code.source import deindent as deindent
+ assert deindent(['\tfoo', '\tbar', ]) == ['foo', 'bar']
+
+ def f():
+ c = '''while True:
+ pass
+'''
+ import inspect
+ lines = deindent(inspect.getsource(f).splitlines())
+ assert lines == ["def f():", " c = '''while True:", " pass", "'''"]
+
+ source = """
+ def f():
+ def g():
+ pass
+ """
+ lines = deindent(source.splitlines())
+ assert lines == ['', 'def f():', ' def g():', ' pass', ' ']
+
+
+def test_source_of_class_at_eof_without_newline(tmpdir):
+ # this test fails because the implicit inspect.getsource(A) below
+ # does not return the "x = 1" last line.
+ source = _pytest._code.Source('''
+ class A(object):
+ def method(self):
+ x = 1
+ ''')
+ path = tmpdir.join("a.py")
+ path.write(source)
+ s2 = _pytest._code.Source(tmpdir.join("a.py").pyimport().A)
+ assert str(source).strip() == str(s2).strip()
+
+
+if True:
+ def x():
+ pass
+
+
+def test_getsource_fallback():
+ from _pytest._code.source import getsource
+ expected = """def x():
+ pass"""
+ src = getsource(x)
+ assert src == expected
+
+
+def test_idem_compile_and_getsource():
+ from _pytest._code.source import getsource
+ expected = "def x(): pass"
+ co = _pytest._code.compile(expected)
+ src = getsource(co)
+ assert src == expected
+
+
+def test_findsource_fallback():
+ from _pytest._code.source import findsource
+ src, lineno = findsource(x)
+ assert 'test_findsource_simple' in str(src)
+ assert src[lineno] == ' def x():'
+
+
+def test_findsource():
+ from _pytest._code.source import findsource
+ co = _pytest._code.compile("""if 1:
+ def x():
+ pass
+""")
+
+ src, lineno = findsource(co)
+ assert 'if 1:' in str(src)
+
+ d = {}
+ eval(co, d)
+ src, lineno = findsource(d['x'])
+ assert 'if 1:' in str(src)
+ assert src[lineno] == " def x():"
+
+
+def test_getfslineno():
+ from _pytest._code import getfslineno
+
+ def f(x):
+ pass
+
+ fspath, lineno = getfslineno(f)
+
+ assert fspath.basename == "test_source.py"
+ assert lineno == _pytest._code.getrawcode(f).co_firstlineno - 1 # see findsource
+
+ class A(object):
+ pass
+
+ fspath, lineno = getfslineno(A)
+
+ _, A_lineno = py.std.inspect.findsource(A)
+ assert fspath.basename == "test_source.py"
+ assert lineno == A_lineno
+
+ assert getfslineno(3) == ("", -1)
+
+ class B(object):
+ pass
+ B.__name__ = "B2"
+ assert getfslineno(B)[1] == -1
+
+
+def test_code_of_object_instance_with_call():
+ class A(object):
+ pass
+ pytest.raises(TypeError, lambda: _pytest._code.Source(A()))
+
+ class WithCall(object):
+ def __call__(self):
+ pass
+
+ code = _pytest._code.Code(WithCall())
+ assert 'pass' in str(code.source())
+
+ class Hello(object):
+ def __call__(self):
+ pass
+ pytest.raises(TypeError, lambda: _pytest._code.Code(Hello))
+
+
+def getstatement(lineno, source):
+ from _pytest._code.source import getstatementrange_ast
+ source = _pytest._code.Source(source, deindent=False)
+ ast, start, end = getstatementrange_ast(lineno, source)
+ return source[start:end]
+
+
+def test_oneline():
+ source = getstatement(0, "raise ValueError")
+ assert str(source) == "raise ValueError"
+
+
+def test_comment_and_no_newline_at_end():
+ from _pytest._code.source import getstatementrange_ast
+ source = Source(['def test_basic_complex():',
+ ' assert 1 == 2',
+ '# vim: filetype=pyopencl:fdm=marker'])
+ ast, start, end = getstatementrange_ast(1, source)
+ assert end == 2
+
+
+def test_oneline_and_comment():
+ source = getstatement(0, "raise ValueError\n#hello")
+ assert str(source) == "raise ValueError"
+
+
+@pytest.mark.xfail(hasattr(sys, "pypy_version_info"),
+ reason='does not work on pypy')
+def test_comments():
+ source = '''def test():
+ "comment 1"
+ x = 1
+ # comment 2
+ # comment 3
+
+ assert False
+
+"""
+comment 4
+"""
+'''
+ for line in range(2, 6):
+ assert str(getstatement(line, source)) == ' x = 1'
+ for line in range(6, 10):
+ assert str(getstatement(line, source)) == ' assert False'
+ assert str(getstatement(10, source)) == '"""'
+
+
+def test_comment_in_statement():
+ source = '''test(foo=1,
+ # comment 1
+ bar=2)
+'''
+ for line in range(1, 3):
+ assert str(getstatement(line, source)) == \
+ 'test(foo=1,\n # comment 1\n bar=2)'
+
+
+def test_single_line_else():
+ source = getstatement(1, "if False: 2\nelse: 3")
+ assert str(source) == "else: 3"
+
+
+def test_single_line_finally():
+ source = getstatement(1, "try: 1\nfinally: 3")
+ assert str(source) == "finally: 3"
+
+
+def test_issue55():
+ source = ('def round_trip(dinp):\n assert 1 == dinp\n'
+ 'def test_rt():\n round_trip("""\n""")\n')
+ s = getstatement(3, source)
+ assert str(s) == ' round_trip("""\n""")'
+
+
+def XXXtest_multiline():
+ source = getstatement(0, """\
+raise ValueError(
+ 23
+)
+x = 3
+""")
+ assert str(source) == "raise ValueError(\n 23\n)"
+
+
+class TestTry(object):
+ pytestmark = astonly
+ source = """\
+try:
+ raise ValueError
+except Something:
+ raise IndexError(1)
+else:
+ raise KeyError()
+"""
+
+ def test_body(self):
+ source = getstatement(1, self.source)
+ assert str(source) == " raise ValueError"
+
+ def test_except_line(self):
+ source = getstatement(2, self.source)
+ assert str(source) == "except Something:"
+
+ def test_except_body(self):
+ source = getstatement(3, self.source)
+ assert str(source) == " raise IndexError(1)"
+
+ def test_else(self):
+ source = getstatement(5, self.source)
+ assert str(source) == " raise KeyError()"
+
+
+class TestTryFinally(object):
+ source = """\
+try:
+ raise ValueError
+finally:
+ raise IndexError(1)
+"""
+
+ def test_body(self):
+ source = getstatement(1, self.source)
+ assert str(source) == " raise ValueError"
+
+ def test_finally(self):
+ source = getstatement(3, self.source)
+ assert str(source) == " raise IndexError(1)"
+
+
+class TestIf(object):
+ pytestmark = astonly
+ source = """\
+if 1:
+ y = 3
+elif False:
+ y = 5
+else:
+ y = 7
+"""
+
+ def test_body(self):
+ source = getstatement(1, self.source)
+ assert str(source) == " y = 3"
+
+ def test_elif_clause(self):
+ source = getstatement(2, self.source)
+ assert str(source) == "elif False:"
+
+ def test_elif(self):
+ source = getstatement(3, self.source)
+ assert str(source) == " y = 5"
+
+ def test_else(self):
+ source = getstatement(5, self.source)
+ assert str(source) == " y = 7"
+
+
+def test_semicolon():
+ s = """\
+hello ; pytest.skip()
+"""
+ source = getstatement(0, s)
+ assert str(source) == s.strip()
+
+
+def test_def_online():
+ s = """\
+def func(): raise ValueError(42)
+
+def something():
+ pass
+"""
+ source = getstatement(0, s)
+ assert str(source) == "def func(): raise ValueError(42)"
+
+
+def XXX_test_expression_multiline():
+ source = """\
+something
+'''
+'''"""
+ result = getstatement(1, source)
+ assert str(result) == "'''\n'''"
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_source_multiline_block.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_source_multiline_block.py
new file mode 100644
index 00000000000..b356d191f0b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/code/test_source_multiline_block.py
@@ -0,0 +1,26 @@
+# flake8: noqa
+import sys
+
+import _pytest._code
+
+
+def test_getstartingblock_multiline():
+ """
+ This test was originally found in test_source.py, but it depends on the weird
+ formatting of the ``x = A`` construct seen here and our autopep8 tool can only exclude entire
+ files (it does not support excluding lines/blocks using the traditional #noqa comment yet,
+ see hhatto/autopep8#307). It was considered better to just move this single test to its own
+ file and exclude it from autopep8 than try to complicate things.
+ """
+ class A(object):
+ def __init__(self, *args):
+ frame = sys._getframe(1)
+ self.source = _pytest._code.Frame(frame).statement
+
+ x = A('x',
+ 'y'
+ ,
+ 'z')
+
+ values = [i for i in x.source.lines if i.strip()]
+ assert len(values) == 4
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/deprecated_test.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/deprecated_test.py
new file mode 100644
index 00000000000..11c4ad43cbc
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/deprecated_test.py
@@ -0,0 +1,127 @@
+from __future__ import absolute_import, division, print_function
+import pytest
+
+
+def test_yield_tests_deprecation(testdir):
+ testdir.makepyfile("""
+ def func1(arg, arg2):
+ assert arg == arg2
+ def test_gen():
+ yield "m1", func1, 15, 3*5
+ yield "m2", func1, 42, 6*7
+ def test_gen2():
+ for k in range(10):
+ yield func1, 1, 1
+ """)
+ result = testdir.runpytest('-ra')
+ result.stdout.fnmatch_lines([
+ '*yield tests are deprecated, and scheduled to be removed in pytest 4.0*',
+ '*2 passed*',
+ ])
+ assert result.stdout.str().count('yield tests are deprecated') == 2
+
+
+def test_funcarg_prefix_deprecation(testdir):
+ testdir.makepyfile("""
+ def pytest_funcarg__value():
+ return 10
+
+ def test_funcarg_prefix(value):
+ assert value == 10
+ """)
+ result = testdir.runpytest('-ra')
+ result.stdout.fnmatch_lines([
+ ('*pytest_funcarg__value: '
+ 'declaring fixtures using "pytest_funcarg__" prefix is deprecated '
+ 'and scheduled to be removed in pytest 4.0. '
+ 'Please remove the prefix and use the @pytest.fixture decorator instead.'),
+ '*1 passed*',
+ ])
+
+
+def test_pytest_setup_cfg_deprecated(testdir):
+ testdir.makefile('.cfg', setup='''
+ [pytest]
+ addopts = --verbose
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['*pytest*section in setup.cfg files is deprecated*use*tool:pytest*instead*'])
+
+
+def test_str_args_deprecated(tmpdir, testdir):
+ """Deprecate passing strings to pytest.main(). Scheduled for removal in pytest-4.0."""
+ from _pytest.main import EXIT_NOTESTSCOLLECTED
+ warnings = []
+
+ class Collect(object):
+ def pytest_logwarning(self, message):
+ warnings.append(message)
+
+ ret = pytest.main("%s -x" % tmpdir, plugins=[Collect()])
+ testdir.delete_loaded_modules()
+ msg = ('passing a string to pytest.main() is deprecated, '
+ 'pass a list of arguments instead.')
+ assert msg in warnings
+ assert ret == EXIT_NOTESTSCOLLECTED
+
+
+def test_getfuncargvalue_is_deprecated(request):
+ pytest.deprecated_call(request.getfuncargvalue, 'tmpdir')
+
+
+def test_resultlog_is_deprecated(testdir):
+ result = testdir.runpytest('--help')
+ result.stdout.fnmatch_lines(['*DEPRECATED path for machine-readable result log*'])
+
+ testdir.makepyfile('''
+ def test():
+ pass
+ ''')
+ result = testdir.runpytest('--result-log=%s' % testdir.tmpdir.join('result.log'))
+ result.stdout.fnmatch_lines([
+ '*--result-log is deprecated and scheduled for removal in pytest 4.0*',
+ '*See https://docs.pytest.org/*/usage.html#creating-resultlog-format-files for more information*',
+ ])
+
+
+@pytest.mark.filterwarnings('always:Metafunc.addcall is deprecated')
+def test_metafunc_addcall_deprecated(testdir):
+ testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ metafunc.addcall({'i': 1})
+ metafunc.addcall({'i': 2})
+ def test_func(i):
+ pass
+ """)
+ res = testdir.runpytest('-s')
+ assert res.ret == 0
+ res.stdout.fnmatch_lines([
+ "*Metafunc.addcall is deprecated*",
+ "*2 passed, 2 warnings*",
+ ])
+
+
+def test_terminal_reporter_writer_attr(pytestconfig):
+ """Check that TerminalReporter._tw is also available as 'writer' (#2984)
+ This attribute is planned to be deprecated in 3.4.
+ """
+ try:
+ import xdist # noqa
+ pytest.skip('xdist workers disable the terminal reporter plugin')
+ except ImportError:
+ pass
+ terminal_reporter = pytestconfig.pluginmanager.get_plugin('terminalreporter')
+ assert terminal_reporter.writer is terminal_reporter._tw
+
+
+def test_pytest_catchlog_deprecated(testdir):
+ testdir.makepyfile("""
+ def test_func(pytestconfig):
+ pytestconfig.pluginmanager.register(None, 'pytest_catchlog')
+ """)
+ res = testdir.runpytest()
+ assert res.ret == 0
+ res.stdout.fnmatch_lines([
+ "*pytest-catchlog plugin has been merged into the core*",
+ "*1 passed, 1 warnings*",
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/.gitignore b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/.gitignore
new file mode 100644
index 00000000000..490310b6c11
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/.gitignore
@@ -0,0 +1,3 @@
+build/
+dist/
+*.spec \ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/create_executable.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/create_executable.py
new file mode 100644
index 00000000000..f4f6088ef7d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/create_executable.py
@@ -0,0 +1,12 @@
+"""
+Generates an executable with pytest runner embedded using PyInstaller.
+"""
+if __name__ == '__main__':
+ import pytest
+ import subprocess
+
+ hidden = []
+ for x in pytest.freeze_includes():
+ hidden.extend(['--hidden-import', x])
+ args = ['pyinstaller', '--noconfirm'] + hidden + ['runtests_script.py']
+ subprocess.check_call(' '.join(args), shell=True)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/runtests_script.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/runtests_script.py
new file mode 100644
index 00000000000..d281601c068
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/runtests_script.py
@@ -0,0 +1,9 @@
+"""
+This is the script that is actually frozen into an executable: simply executes
+py.test main().
+"""
+
+if __name__ == '__main__':
+ import sys
+ import pytest
+ sys.exit(pytest.main())
diff --git a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/tests/test_doctest.txt b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/tests/test_doctest.txt
index e18a4b68cc9..e18a4b68cc9 100644
--- a/tests/wpt/web-platform-tests/tools/pytest/testing/cx_freeze/tests/test_doctest.txt
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/tests/test_doctest.txt
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/tests/test_trivial.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/tests/test_trivial.py
new file mode 100644
index 00000000000..45622b850bb
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/tests/test_trivial.py
@@ -0,0 +1,7 @@
+
+def test_upper():
+ assert 'foo'.upper() == 'FOO'
+
+
+def test_lower():
+ assert 'FOO'.lower() == 'foo'
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/tox_run.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/tox_run.py
new file mode 100644
index 00000000000..3fc38804095
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/freeze/tox_run.py
@@ -0,0 +1,12 @@
+"""
+Called by tox.ini: uses the generated executable to run the tests in ./tests/
+directory.
+"""
+if __name__ == '__main__':
+ import os
+ import sys
+
+ executable = os.path.join(os.getcwd(), 'dist', 'runtests_script', 'runtests_script')
+ if sys.platform.startswith('win'):
+ executable += '.exe'
+ sys.exit(os.system('%s tests' % executable))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/logging/test_fixture.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/logging/test_fixture.py
new file mode 100644
index 00000000000..c27b31137ff
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/logging/test_fixture.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+import logging
+
+
+logger = logging.getLogger(__name__)
+sublogger = logging.getLogger(__name__ + '.baz')
+
+
+def test_fixture_help(testdir):
+ result = testdir.runpytest('--fixtures')
+ result.stdout.fnmatch_lines(['*caplog*'])
+
+
+def test_change_level(caplog):
+ caplog.set_level(logging.INFO)
+ logger.debug('handler DEBUG level')
+ logger.info('handler INFO level')
+
+ caplog.set_level(logging.CRITICAL, logger=sublogger.name)
+ sublogger.warning('logger WARNING level')
+ sublogger.critical('logger CRITICAL level')
+
+ assert 'DEBUG' not in caplog.text
+ assert 'INFO' in caplog.text
+ assert 'WARNING' not in caplog.text
+ assert 'CRITICAL' in caplog.text
+
+
+def test_with_statement(caplog):
+ with caplog.at_level(logging.INFO):
+ logger.debug('handler DEBUG level')
+ logger.info('handler INFO level')
+
+ with caplog.at_level(logging.CRITICAL, logger=sublogger.name):
+ sublogger.warning('logger WARNING level')
+ sublogger.critical('logger CRITICAL level')
+
+ assert 'DEBUG' not in caplog.text
+ assert 'INFO' in caplog.text
+ assert 'WARNING' not in caplog.text
+ assert 'CRITICAL' in caplog.text
+
+
+def test_log_access(caplog):
+ logger.info('boo %s', 'arg')
+ assert caplog.records[0].levelname == 'INFO'
+ assert caplog.records[0].msg == 'boo %s'
+ assert 'boo arg' in caplog.text
+
+
+def test_record_tuples(caplog):
+ logger.info('boo %s', 'arg')
+
+ assert caplog.record_tuples == [
+ (__name__, logging.INFO, 'boo arg'),
+ ]
+
+
+def test_unicode(caplog):
+ logger.info(u'bū')
+ assert caplog.records[0].levelname == 'INFO'
+ assert caplog.records[0].msg == u'bū'
+ assert u'bū' in caplog.text
+
+
+def test_clear(caplog):
+ logger.info(u'bū')
+ assert len(caplog.records)
+ caplog.clear()
+ assert not len(caplog.records)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/logging/test_reporting.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/logging/test_reporting.py
new file mode 100644
index 00000000000..c02ee217227
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/logging/test_reporting.py
@@ -0,0 +1,398 @@
+# -*- coding: utf-8 -*-
+import os
+import pytest
+
+
+def test_nothing_logged(testdir):
+ testdir.makepyfile('''
+ import sys
+
+ def test_foo():
+ sys.stdout.write('text going to stdout')
+ sys.stderr.write('text going to stderr')
+ assert False
+ ''')
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.stdout.fnmatch_lines(['*- Captured stdout call -*',
+ 'text going to stdout'])
+ result.stdout.fnmatch_lines(['*- Captured stderr call -*',
+ 'text going to stderr'])
+ with pytest.raises(pytest.fail.Exception):
+ result.stdout.fnmatch_lines(['*- Captured *log call -*'])
+
+
+def test_messages_logged(testdir):
+ testdir.makepyfile('''
+ import sys
+ import logging
+
+ logger = logging.getLogger(__name__)
+
+ def test_foo():
+ sys.stdout.write('text going to stdout')
+ sys.stderr.write('text going to stderr')
+ logger.info('text going to logger')
+ assert False
+ ''')
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.stdout.fnmatch_lines(['*- Captured *log call -*',
+ '*text going to logger*'])
+ result.stdout.fnmatch_lines(['*- Captured stdout call -*',
+ 'text going to stdout'])
+ result.stdout.fnmatch_lines(['*- Captured stderr call -*',
+ 'text going to stderr'])
+
+
+def test_setup_logging(testdir):
+ testdir.makepyfile('''
+ import logging
+
+ logger = logging.getLogger(__name__)
+
+ def setup_function(function):
+ logger.info('text going to logger from setup')
+
+ def test_foo():
+ logger.info('text going to logger from call')
+ assert False
+ ''')
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.stdout.fnmatch_lines(['*- Captured *log setup -*',
+ '*text going to logger from setup*',
+ '*- Captured *log call -*',
+ '*text going to logger from call*'])
+
+
+def test_teardown_logging(testdir):
+ testdir.makepyfile('''
+ import logging
+
+ logger = logging.getLogger(__name__)
+
+ def test_foo():
+ logger.info('text going to logger from call')
+
+ def teardown_function(function):
+ logger.info('text going to logger from teardown')
+ assert False
+ ''')
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.stdout.fnmatch_lines(['*- Captured *log call -*',
+ '*text going to logger from call*',
+ '*- Captured *log teardown -*',
+ '*text going to logger from teardown*'])
+
+
+def test_disable_log_capturing(testdir):
+ testdir.makepyfile('''
+ import sys
+ import logging
+
+ logger = logging.getLogger(__name__)
+
+ def test_foo():
+ sys.stdout.write('text going to stdout')
+ logger.warning('catch me if you can!')
+ sys.stderr.write('text going to stderr')
+ assert False
+ ''')
+ result = testdir.runpytest('--no-print-logs')
+ print(result.stdout)
+ assert result.ret == 1
+ result.stdout.fnmatch_lines(['*- Captured stdout call -*',
+ 'text going to stdout'])
+ result.stdout.fnmatch_lines(['*- Captured stderr call -*',
+ 'text going to stderr'])
+ with pytest.raises(pytest.fail.Exception):
+ result.stdout.fnmatch_lines(['*- Captured *log call -*'])
+
+
+def test_disable_log_capturing_ini(testdir):
+ testdir.makeini(
+ '''
+ [pytest]
+ log_print=False
+ '''
+ )
+ testdir.makepyfile('''
+ import sys
+ import logging
+
+ logger = logging.getLogger(__name__)
+
+ def test_foo():
+ sys.stdout.write('text going to stdout')
+ logger.warning('catch me if you can!')
+ sys.stderr.write('text going to stderr')
+ assert False
+ ''')
+ result = testdir.runpytest()
+ print(result.stdout)
+ assert result.ret == 1
+ result.stdout.fnmatch_lines(['*- Captured stdout call -*',
+ 'text going to stdout'])
+ result.stdout.fnmatch_lines(['*- Captured stderr call -*',
+ 'text going to stderr'])
+ with pytest.raises(pytest.fail.Exception):
+ result.stdout.fnmatch_lines(['*- Captured *log call -*'])
+
+
+def test_log_cli_default_level(testdir):
+ # Default log file level
+ testdir.makepyfile('''
+ import pytest
+ import logging
+ def test_log_cli(request):
+ plugin = request.config.pluginmanager.getplugin('logging-plugin')
+ assert plugin.log_cli_handler.level == logging.WARNING
+ logging.getLogger('catchlog').info("This log message won't be shown")
+ logging.getLogger('catchlog').warning("This log message will be shown")
+ print('PASSED')
+ ''')
+
+ result = testdir.runpytest('-s')
+
+ # fnmatch_lines does an assertion internally
+ result.stdout.fnmatch_lines([
+ 'test_log_cli_default_level.py PASSED',
+ ])
+ result.stderr.fnmatch_lines([
+ "* This log message will be shown"
+ ])
+ for line in result.errlines:
+ try:
+ assert "This log message won't be shown" in line
+ pytest.fail("A log message was shown and it shouldn't have been")
+ except AssertionError:
+ continue
+
+ # make sure that that we get a '0' exit code for the testsuite
+ assert result.ret == 0
+
+
+def test_log_cli_level(testdir):
+ # Default log file level
+ testdir.makepyfile('''
+ import pytest
+ import logging
+ def test_log_cli(request):
+ plugin = request.config.pluginmanager.getplugin('logging-plugin')
+ assert plugin.log_cli_handler.level == logging.INFO
+ logging.getLogger('catchlog').debug("This log message won't be shown")
+ logging.getLogger('catchlog').info("This log message will be shown")
+ print('PASSED')
+ ''')
+
+ result = testdir.runpytest('-s', '--log-cli-level=INFO')
+
+ # fnmatch_lines does an assertion internally
+ result.stdout.fnmatch_lines([
+ 'test_log_cli_level.py PASSED',
+ ])
+ result.stderr.fnmatch_lines([
+ "* This log message will be shown"
+ ])
+ for line in result.errlines:
+ try:
+ assert "This log message won't be shown" in line
+ pytest.fail("A log message was shown and it shouldn't have been")
+ except AssertionError:
+ continue
+
+ # make sure that that we get a '0' exit code for the testsuite
+ assert result.ret == 0
+
+ result = testdir.runpytest('-s', '--log-level=INFO')
+
+ # fnmatch_lines does an assertion internally
+ result.stdout.fnmatch_lines([
+ 'test_log_cli_level.py PASSED',
+ ])
+ result.stderr.fnmatch_lines([
+ "* This log message will be shown"
+ ])
+ for line in result.errlines:
+ try:
+ assert "This log message won't be shown" in line
+ pytest.fail("A log message was shown and it shouldn't have been")
+ except AssertionError:
+ continue
+
+ # make sure that that we get a '0' exit code for the testsuite
+ assert result.ret == 0
+
+
+def test_log_cli_ini_level(testdir):
+ testdir.makeini(
+ """
+ [pytest]
+ log_cli_level = INFO
+ """)
+ testdir.makepyfile('''
+ import pytest
+ import logging
+ def test_log_cli(request):
+ plugin = request.config.pluginmanager.getplugin('logging-plugin')
+ assert plugin.log_cli_handler.level == logging.INFO
+ logging.getLogger('catchlog').debug("This log message won't be shown")
+ logging.getLogger('catchlog').info("This log message will be shown")
+ print('PASSED')
+ ''')
+
+ result = testdir.runpytest('-s')
+
+ # fnmatch_lines does an assertion internally
+ result.stdout.fnmatch_lines([
+ 'test_log_cli_ini_level.py PASSED',
+ ])
+ result.stderr.fnmatch_lines([
+ "* This log message will be shown"
+ ])
+ for line in result.errlines:
+ try:
+ assert "This log message won't be shown" in line
+ pytest.fail("A log message was shown and it shouldn't have been")
+ except AssertionError:
+ continue
+
+ # make sure that that we get a '0' exit code for the testsuite
+ assert result.ret == 0
+
+
+def test_log_file_cli(testdir):
+ # Default log file level
+ testdir.makepyfile('''
+ import pytest
+ import logging
+ def test_log_file(request):
+ plugin = request.config.pluginmanager.getplugin('logging-plugin')
+ assert plugin.log_file_handler.level == logging.WARNING
+ logging.getLogger('catchlog').info("This log message won't be shown")
+ logging.getLogger('catchlog').warning("This log message will be shown")
+ print('PASSED')
+ ''')
+
+ log_file = testdir.tmpdir.join('pytest.log').strpath
+
+ result = testdir.runpytest('-s', '--log-file={0}'.format(log_file))
+
+ # fnmatch_lines does an assertion internally
+ result.stdout.fnmatch_lines([
+ 'test_log_file_cli.py PASSED',
+ ])
+
+ # make sure that that we get a '0' exit code for the testsuite
+ assert result.ret == 0
+ assert os.path.isfile(log_file)
+ with open(log_file) as rfh:
+ contents = rfh.read()
+ assert "This log message will be shown" in contents
+ assert "This log message won't be shown" not in contents
+
+
+def test_log_file_cli_level(testdir):
+ # Default log file level
+ testdir.makepyfile('''
+ import pytest
+ import logging
+ def test_log_file(request):
+ plugin = request.config.pluginmanager.getplugin('logging-plugin')
+ assert plugin.log_file_handler.level == logging.INFO
+ logging.getLogger('catchlog').debug("This log message won't be shown")
+ logging.getLogger('catchlog').info("This log message will be shown")
+ print('PASSED')
+ ''')
+
+ log_file = testdir.tmpdir.join('pytest.log').strpath
+
+ result = testdir.runpytest('-s',
+ '--log-file={0}'.format(log_file),
+ '--log-file-level=INFO')
+
+ # fnmatch_lines does an assertion internally
+ result.stdout.fnmatch_lines([
+ 'test_log_file_cli_level.py PASSED',
+ ])
+
+ # make sure that that we get a '0' exit code for the testsuite
+ assert result.ret == 0
+ assert os.path.isfile(log_file)
+ with open(log_file) as rfh:
+ contents = rfh.read()
+ assert "This log message will be shown" in contents
+ assert "This log message won't be shown" not in contents
+
+
+def test_log_file_ini(testdir):
+ log_file = testdir.tmpdir.join('pytest.log').strpath
+
+ testdir.makeini(
+ """
+ [pytest]
+ log_file={0}
+ """.format(log_file))
+ testdir.makepyfile('''
+ import pytest
+ import logging
+ def test_log_file(request):
+ plugin = request.config.pluginmanager.getplugin('logging-plugin')
+ assert plugin.log_file_handler.level == logging.WARNING
+ logging.getLogger('catchlog').info("This log message won't be shown")
+ logging.getLogger('catchlog').warning("This log message will be shown")
+ print('PASSED')
+ ''')
+
+ result = testdir.runpytest('-s')
+
+ # fnmatch_lines does an assertion internally
+ result.stdout.fnmatch_lines([
+ 'test_log_file_ini.py PASSED',
+ ])
+
+ # make sure that that we get a '0' exit code for the testsuite
+ assert result.ret == 0
+ assert os.path.isfile(log_file)
+ with open(log_file) as rfh:
+ contents = rfh.read()
+ assert "This log message will be shown" in contents
+ assert "This log message won't be shown" not in contents
+
+
+def test_log_file_ini_level(testdir):
+ log_file = testdir.tmpdir.join('pytest.log').strpath
+
+ testdir.makeini(
+ """
+ [pytest]
+ log_file={0}
+ log_file_level = INFO
+ """.format(log_file))
+ testdir.makepyfile('''
+ import pytest
+ import logging
+ def test_log_file(request):
+ plugin = request.config.pluginmanager.getplugin('logging-plugin')
+ assert plugin.log_file_handler.level == logging.INFO
+ logging.getLogger('catchlog').debug("This log message won't be shown")
+ logging.getLogger('catchlog').info("This log message will be shown")
+ print('PASSED')
+ ''')
+
+ result = testdir.runpytest('-s')
+
+ # fnmatch_lines does an assertion internally
+ result.stdout.fnmatch_lines([
+ 'test_log_file_ini_level.py PASSED',
+ ])
+
+ # make sure that that we get a '0' exit code for the testsuite
+ assert result.ret == 0
+ assert os.path.isfile(log_file)
+ with open(log_file) as rfh:
+ contents = rfh.read()
+ assert "This log message will be shown" in contents
+ assert "This log message won't be shown" not in contents
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/approx.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/approx.py
new file mode 100644
index 00000000000..300e1ce86f7
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/approx.py
@@ -0,0 +1,392 @@
+# encoding: utf-8
+import operator
+import sys
+import pytest
+import doctest
+
+from pytest import approx
+from operator import eq, ne
+from decimal import Decimal
+from fractions import Fraction
+inf, nan = float('inf'), float('nan')
+
+
+class MyDocTestRunner(doctest.DocTestRunner):
+
+ def __init__(self):
+ doctest.DocTestRunner.__init__(self)
+
+ def report_failure(self, out, test, example, got):
+ raise AssertionError("'{}' evaluates to '{}', not '{}'".format(
+ example.source.strip(), got.strip(), example.want.strip()))
+
+
+class TestApprox(object):
+
+ def test_repr_string(self):
+ plus_minus = u'\u00b1' if sys.version_info[0] > 2 else u'+-'
+ tol1, tol2, infr = '1.0e-06', '2.0e-06', 'inf'
+ assert repr(approx(1.0)) == '1.0 {pm} {tol1}'.format(pm=plus_minus, tol1=tol1)
+ assert repr(approx([1.0, 2.0])) == 'approx([1.0 {pm} {tol1}, 2.0 {pm} {tol2}])'.format(
+ pm=plus_minus, tol1=tol1, tol2=tol2)
+ assert repr(approx((1.0, 2.0))) == 'approx((1.0 {pm} {tol1}, 2.0 {pm} {tol2}))'.format(
+ pm=plus_minus, tol1=tol1, tol2=tol2)
+ assert repr(approx(inf)) == 'inf'
+ assert repr(approx(1.0, rel=nan)) == '1.0 {pm} ???'.format(pm=plus_minus)
+ assert repr(approx(1.0, rel=inf)) == '1.0 {pm} {infr}'.format(pm=plus_minus, infr=infr)
+ assert repr(approx(1.0j, rel=inf)) == '1j'
+
+ # Dictionaries aren't ordered, so we need to check both orders.
+ assert repr(approx({'a': 1.0, 'b': 2.0})) in (
+ "approx({{'a': 1.0 {pm} {tol1}, 'b': 2.0 {pm} {tol2}}})".format(pm=plus_minus, tol1=tol1, tol2=tol2),
+ "approx({{'b': 2.0 {pm} {tol2}, 'a': 1.0 {pm} {tol1}}})".format(pm=plus_minus, tol1=tol1, tol2=tol2),
+ )
+
+ def test_operator_overloading(self):
+ assert 1 == approx(1, rel=1e-6, abs=1e-12)
+ assert not (1 != approx(1, rel=1e-6, abs=1e-12))
+ assert 10 != approx(1, rel=1e-6, abs=1e-12)
+ assert not (10 == approx(1, rel=1e-6, abs=1e-12))
+
+ def test_exactly_equal(self):
+ examples = [
+ (2.0, 2.0),
+ (0.1e200, 0.1e200),
+ (1.123e-300, 1.123e-300),
+ (12345, 12345.0),
+ (0.0, -0.0),
+ (345678, 345678),
+ (Decimal('1.0001'), Decimal('1.0001')),
+ (Fraction(1, 3), Fraction(-1, -3)),
+ ]
+ for a, x in examples:
+ assert a == approx(x)
+
+ def test_opposite_sign(self):
+ examples = [
+ (eq, 1e-100, -1e-100),
+ (ne, 1e100, -1e100),
+ ]
+ for op, a, x in examples:
+ assert op(a, approx(x))
+
+ def test_zero_tolerance(self):
+ within_1e10 = [
+ (1.1e-100, 1e-100),
+ (-1.1e-100, -1e-100),
+ ]
+ for a, x in within_1e10:
+ assert x == approx(x, rel=0.0, abs=0.0)
+ assert a != approx(x, rel=0.0, abs=0.0)
+ assert a == approx(x, rel=0.0, abs=5e-101)
+ assert a != approx(x, rel=0.0, abs=5e-102)
+ assert a == approx(x, rel=5e-1, abs=0.0)
+ assert a != approx(x, rel=5e-2, abs=0.0)
+
+ def test_negative_tolerance(self):
+ # Negative tolerances are not allowed.
+ illegal_kwargs = [
+ dict(rel=-1e100),
+ dict(abs=-1e100),
+ dict(rel=1e100, abs=-1e100),
+ dict(rel=-1e100, abs=1e100),
+ dict(rel=-1e100, abs=-1e100),
+ ]
+ for kwargs in illegal_kwargs:
+ with pytest.raises(ValueError):
+ 1.1 == approx(1, **kwargs)
+
+ def test_inf_tolerance(self):
+ # Everything should be equal if the tolerance is infinite.
+ large_diffs = [
+ (1, 1000),
+ (1e-50, 1e50),
+ (-1.0, -1e300),
+ (0.0, 10),
+ ]
+ for a, x in large_diffs:
+ assert a != approx(x, rel=0.0, abs=0.0)
+ assert a == approx(x, rel=inf, abs=0.0)
+ assert a == approx(x, rel=0.0, abs=inf)
+ assert a == approx(x, rel=inf, abs=inf)
+
+ def test_inf_tolerance_expecting_zero(self):
+ # If the relative tolerance is zero but the expected value is infinite,
+ # the actual tolerance is a NaN, which should be an error.
+ illegal_kwargs = [
+ dict(rel=inf, abs=0.0),
+ dict(rel=inf, abs=inf),
+ ]
+ for kwargs in illegal_kwargs:
+ with pytest.raises(ValueError):
+ 1 == approx(0, **kwargs)
+
+ def test_nan_tolerance(self):
+ illegal_kwargs = [
+ dict(rel=nan),
+ dict(abs=nan),
+ dict(rel=nan, abs=nan),
+ ]
+ for kwargs in illegal_kwargs:
+ with pytest.raises(ValueError):
+ 1.1 == approx(1, **kwargs)
+
+ def test_reasonable_defaults(self):
+ # Whatever the defaults are, they should work for numbers close to 1
+ # than have a small amount of floating-point error.
+ assert 0.1 + 0.2 == approx(0.3)
+
+ def test_default_tolerances(self):
+ # This tests the defaults as they are currently set. If you change the
+ # defaults, this test will fail but you should feel free to change it.
+ # None of the other tests (except the doctests) should be affected by
+ # the choice of defaults.
+ examples = [
+ # Relative tolerance used.
+ (eq, 1e100 + 1e94, 1e100),
+ (ne, 1e100 + 2e94, 1e100),
+ (eq, 1e0 + 1e-6, 1e0),
+ (ne, 1e0 + 2e-6, 1e0),
+ # Absolute tolerance used.
+ (eq, 1e-100, + 1e-106),
+ (eq, 1e-100, + 2e-106),
+ (eq, 1e-100, 0),
+ ]
+ for op, a, x in examples:
+ assert op(a, approx(x))
+
+ def test_custom_tolerances(self):
+ assert 1e8 + 1e0 == approx(1e8, rel=5e-8, abs=5e0)
+ assert 1e8 + 1e0 == approx(1e8, rel=5e-9, abs=5e0)
+ assert 1e8 + 1e0 == approx(1e8, rel=5e-8, abs=5e-1)
+ assert 1e8 + 1e0 != approx(1e8, rel=5e-9, abs=5e-1)
+
+ assert 1e0 + 1e-8 == approx(1e0, rel=5e-8, abs=5e-8)
+ assert 1e0 + 1e-8 == approx(1e0, rel=5e-9, abs=5e-8)
+ assert 1e0 + 1e-8 == approx(1e0, rel=5e-8, abs=5e-9)
+ assert 1e0 + 1e-8 != approx(1e0, rel=5e-9, abs=5e-9)
+
+ assert 1e-8 + 1e-16 == approx(1e-8, rel=5e-8, abs=5e-16)
+ assert 1e-8 + 1e-16 == approx(1e-8, rel=5e-9, abs=5e-16)
+ assert 1e-8 + 1e-16 == approx(1e-8, rel=5e-8, abs=5e-17)
+ assert 1e-8 + 1e-16 != approx(1e-8, rel=5e-9, abs=5e-17)
+
+ def test_relative_tolerance(self):
+ within_1e8_rel = [
+ (1e8 + 1e0, 1e8),
+ (1e0 + 1e-8, 1e0),
+ (1e-8 + 1e-16, 1e-8),
+ ]
+ for a, x in within_1e8_rel:
+ assert a == approx(x, rel=5e-8, abs=0.0)
+ assert a != approx(x, rel=5e-9, abs=0.0)
+
+ def test_absolute_tolerance(self):
+ within_1e8_abs = [
+ (1e8 + 9e-9, 1e8),
+ (1e0 + 9e-9, 1e0),
+ (1e-8 + 9e-9, 1e-8),
+ ]
+ for a, x in within_1e8_abs:
+ assert a == approx(x, rel=0, abs=5e-8)
+ assert a != approx(x, rel=0, abs=5e-9)
+
+ def test_expecting_zero(self):
+ examples = [
+ (ne, 1e-6, 0.0),
+ (ne, -1e-6, 0.0),
+ (eq, 1e-12, 0.0),
+ (eq, -1e-12, 0.0),
+ (ne, 2e-12, 0.0),
+ (ne, -2e-12, 0.0),
+ (ne, inf, 0.0),
+ (ne, nan, 0.0),
+ ]
+ for op, a, x in examples:
+ assert op(a, approx(x, rel=0.0, abs=1e-12))
+ assert op(a, approx(x, rel=1e-6, abs=1e-12))
+
+ def test_expecting_inf(self):
+ examples = [
+ (eq, inf, inf),
+ (eq, -inf, -inf),
+ (ne, inf, -inf),
+ (ne, 0.0, inf),
+ (ne, nan, inf),
+ ]
+ for op, a, x in examples:
+ assert op(a, approx(x))
+
+ def test_expecting_nan(self):
+ examples = [
+ (eq, nan, nan),
+ (eq, -nan, -nan),
+ (eq, nan, -nan),
+ (ne, 0.0, nan),
+ (ne, inf, nan),
+ ]
+ for op, a, x in examples:
+ # Nothing is equal to NaN by default.
+ assert a != approx(x)
+
+ # If ``nan_ok=True``, then NaN is equal to NaN.
+ assert op(a, approx(x, nan_ok=True))
+
+ def test_int(self):
+ within_1e6 = [
+ (1000001, 1000000),
+ (-1000001, -1000000),
+ ]
+ for a, x in within_1e6:
+ assert a == approx(x, rel=5e-6, abs=0)
+ assert a != approx(x, rel=5e-7, abs=0)
+ assert approx(x, rel=5e-6, abs=0) == a
+ assert approx(x, rel=5e-7, abs=0) != a
+
+ def test_decimal(self):
+ within_1e6 = [
+ (Decimal('1.000001'), Decimal('1.0')),
+ (Decimal('-1.000001'), Decimal('-1.0')),
+ ]
+ for a, x in within_1e6:
+ assert a == approx(x, rel=Decimal('5e-6'), abs=0)
+ assert a != approx(x, rel=Decimal('5e-7'), abs=0)
+ assert approx(x, rel=Decimal('5e-6'), abs=0) == a
+ assert approx(x, rel=Decimal('5e-7'), abs=0) != a
+
+ def test_fraction(self):
+ within_1e6 = [
+ (1 + Fraction(1, 1000000), Fraction(1)),
+ (-1 - Fraction(-1, 1000000), Fraction(-1)),
+ ]
+ for a, x in within_1e6:
+ assert a == approx(x, rel=5e-6, abs=0)
+ assert a != approx(x, rel=5e-7, abs=0)
+ assert approx(x, rel=5e-6, abs=0) == a
+ assert approx(x, rel=5e-7, abs=0) != a
+
+ def test_complex(self):
+ within_1e6 = [
+ (1.000001 + 1.0j, 1.0 + 1.0j),
+ (1.0 + 1.000001j, 1.0 + 1.0j),
+ (-1.000001 + 1.0j, -1.0 + 1.0j),
+ (1.0 - 1.000001j, 1.0 - 1.0j),
+ ]
+ for a, x in within_1e6:
+ assert a == approx(x, rel=5e-6, abs=0)
+ assert a != approx(x, rel=5e-7, abs=0)
+ assert approx(x, rel=5e-6, abs=0) == a
+ assert approx(x, rel=5e-7, abs=0) != a
+
+ def test_list(self):
+ actual = [1 + 1e-7, 2 + 1e-8]
+ expected = [1, 2]
+
+ # Return false if any element is outside the tolerance.
+ assert actual == approx(expected, rel=5e-7, abs=0)
+ assert actual != approx(expected, rel=5e-8, abs=0)
+ assert approx(expected, rel=5e-7, abs=0) == actual
+ assert approx(expected, rel=5e-8, abs=0) != actual
+
+ def test_list_wrong_len(self):
+ assert [1, 2] != approx([1])
+ assert [1, 2] != approx([1, 2, 3])
+
+ def test_tuple(self):
+ actual = (1 + 1e-7, 2 + 1e-8)
+ expected = (1, 2)
+
+ # Return false if any element is outside the tolerance.
+ assert actual == approx(expected, rel=5e-7, abs=0)
+ assert actual != approx(expected, rel=5e-8, abs=0)
+ assert approx(expected, rel=5e-7, abs=0) == actual
+ assert approx(expected, rel=5e-8, abs=0) != actual
+
+ def test_tuple_wrong_len(self):
+ assert (1, 2) != approx((1,))
+ assert (1, 2) != approx((1, 2, 3))
+
+ def test_dict(self):
+ actual = {'a': 1 + 1e-7, 'b': 2 + 1e-8}
+ # Dictionaries became ordered in python3.6, so switch up the order here
+ # to make sure it doesn't matter.
+ expected = {'b': 2, 'a': 1}
+
+ # Return false if any element is outside the tolerance.
+ assert actual == approx(expected, rel=5e-7, abs=0)
+ assert actual != approx(expected, rel=5e-8, abs=0)
+ assert approx(expected, rel=5e-7, abs=0) == actual
+ assert approx(expected, rel=5e-8, abs=0) != actual
+
+ def test_dict_wrong_len(self):
+ assert {'a': 1, 'b': 2} != approx({'a': 1})
+ assert {'a': 1, 'b': 2} != approx({'a': 1, 'c': 2})
+ assert {'a': 1, 'b': 2} != approx({'a': 1, 'b': 2, 'c': 3})
+
+ def test_numpy_array(self):
+ np = pytest.importorskip('numpy')
+
+ actual = np.array([1 + 1e-7, 2 + 1e-8])
+ expected = np.array([1, 2])
+
+ # Return false if any element is outside the tolerance.
+ assert actual == approx(expected, rel=5e-7, abs=0)
+ assert actual != approx(expected, rel=5e-8, abs=0)
+ assert approx(expected, rel=5e-7, abs=0) == expected
+ assert approx(expected, rel=5e-8, abs=0) != actual
+
+ # Should be able to compare lists with numpy arrays.
+ assert list(actual) == approx(expected, rel=5e-7, abs=0)
+ assert list(actual) != approx(expected, rel=5e-8, abs=0)
+ assert actual == approx(list(expected), rel=5e-7, abs=0)
+ assert actual != approx(list(expected), rel=5e-8, abs=0)
+
+ def test_numpy_array_wrong_shape(self):
+ np = pytest.importorskip('numpy')
+
+ a12 = np.array([[1, 2]])
+ a21 = np.array([[1], [2]])
+
+ assert a12 != approx(a21)
+ assert a21 != approx(a12)
+
+ def test_doctests(self):
+ parser = doctest.DocTestParser()
+ test = parser.get_doctest(
+ approx.__doc__,
+ {'approx': approx},
+ approx.__name__,
+ None, None,
+ )
+ runner = MyDocTestRunner()
+ runner.run(test)
+
+ def test_unicode_plus_minus(self, testdir):
+ """
+ Comparing approx instances inside lists should not produce an error in the detailed diff.
+ Integration test for issue #2111.
+ """
+ testdir.makepyfile("""
+ import pytest
+ def test_foo():
+ assert [3] == [pytest.approx(4)]
+ """)
+ expected = '4.0e-06'
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*At index 0 diff: 3 != 4 * {0}'.format(expected),
+ '=* 1 failed in *=',
+ ])
+
+ @pytest.mark.parametrize('op', [
+ pytest.param(operator.le, id='<='),
+ pytest.param(operator.lt, id='<'),
+ pytest.param(operator.ge, id='>='),
+ pytest.param(operator.gt, id='>'),
+ ])
+ def test_comparison_operator_type_error(self, op):
+ """
+ pytest.approx should raise TypeError for operators other than == and != (#2003).
+ """
+ with pytest.raises(TypeError):
+ op(1, approx(1, rel=1e-6, abs=1e-12))
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/collect.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/collect.py
new file mode 100644
index 00000000000..16c2154b8c1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/collect.py
@@ -0,0 +1,1402 @@
+# -*- coding: utf-8 -*-
+import os
+import sys
+from textwrap import dedent
+
+import _pytest._code
+import py
+import pytest
+from _pytest.main import (
+ Collector,
+ EXIT_NOTESTSCOLLECTED
+)
+
+
+ignore_parametrized_marks = pytest.mark.filterwarnings('ignore:Applying marks directly to parameters')
+
+
+class TestModule(object):
+ def test_failing_import(self, testdir):
+ modcol = testdir.getmodulecol("import alksdjalskdjalkjals")
+ pytest.raises(Collector.CollectError, modcol.collect)
+
+ def test_import_duplicate(self, testdir):
+ a = testdir.mkdir("a")
+ b = testdir.mkdir("b")
+ p = a.ensure("test_whatever.py")
+ p.pyimport()
+ del py.std.sys.modules['test_whatever']
+ b.ensure("test_whatever.py")
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*import*mismatch*",
+ "*imported*test_whatever*",
+ "*%s*" % a.join("test_whatever.py"),
+ "*not the same*",
+ "*%s*" % b.join("test_whatever.py"),
+ "*HINT*",
+ ])
+
+ def test_import_prepend_append(self, testdir, monkeypatch):
+ syspath = list(sys.path)
+ monkeypatch.setattr(sys, "path", syspath)
+ root1 = testdir.mkdir("root1")
+ root2 = testdir.mkdir("root2")
+ root1.ensure("x456.py")
+ root2.ensure("x456.py")
+ p = root2.join("test_x456.py")
+ monkeypatch.syspath_prepend(str(root1))
+ p.write(dedent("""\
+ import x456
+ def test():
+ assert x456.__file__.startswith(%r)
+ """ % str(root2)))
+ with root2.as_cwd():
+ reprec = testdir.inline_run("--import-mode=append")
+ reprec.assertoutcome(passed=0, failed=1)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_syntax_error_in_module(self, testdir):
+ modcol = testdir.getmodulecol("this is a syntax error")
+ pytest.raises(modcol.CollectError, modcol.collect)
+ pytest.raises(modcol.CollectError, modcol.collect)
+
+ def test_module_considers_pluginmanager_at_import(self, testdir):
+ modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',")
+ pytest.raises(ImportError, lambda: modcol.obj)
+
+ def test_invalid_test_module_name(self, testdir):
+ a = testdir.mkdir('a')
+ a.ensure('test_one.part1.py')
+ result = testdir.runpytest("-rw")
+ result.stdout.fnmatch_lines([
+ "ImportError while importing test module*test_one.part1*",
+ "Hint: make sure your test modules/packages have valid Python names.",
+ ])
+
+ @pytest.mark.parametrize('verbose', [0, 1, 2])
+ def test_show_traceback_import_error(self, testdir, verbose):
+ """Import errors when collecting modules should display the traceback (#1976).
+
+ With low verbosity we omit pytest and internal modules, otherwise show all traceback entries.
+ """
+ testdir.makepyfile(
+ foo_traceback_import_error="""
+ from bar_traceback_import_error import NOT_AVAILABLE
+ """,
+ bar_traceback_import_error="",
+ )
+ testdir.makepyfile("""
+ import foo_traceback_import_error
+ """)
+ args = ('-v',) * verbose
+ result = testdir.runpytest(*args)
+ result.stdout.fnmatch_lines([
+ "ImportError while importing test module*",
+ "Traceback:",
+ "*from bar_traceback_import_error import NOT_AVAILABLE",
+ "*cannot import name *NOT_AVAILABLE*",
+ ])
+ assert result.ret == 2
+
+ stdout = result.stdout.str()
+ for name in ('_pytest', os.path.join('py', '_path')):
+ if verbose == 2:
+ assert name in stdout
+ else:
+ assert name not in stdout
+
+ def test_show_traceback_import_error_unicode(self, testdir):
+ """Check test modules collected which raise ImportError with unicode messages
+ are handled properly (#2336).
+ """
+ testdir.makepyfile(u"""
+ # -*- coding: utf-8 -*-
+ raise ImportError(u'Something bad happened ☺')
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "ImportError while importing test module*",
+ "Traceback:",
+ "*raise ImportError*Something bad happened*",
+ ])
+ assert result.ret == 2
+
+
+class TestClass(object):
+ def test_class_with_init_warning(self, testdir):
+ testdir.makepyfile("""
+ class TestClass1(object):
+ def __init__(self):
+ pass
+ """)
+ result = testdir.runpytest("-rw")
+ result.stdout.fnmatch_lines([
+ "*cannot collect test class 'TestClass1' because it has a __init__ constructor",
+ ])
+
+ def test_class_subclassobject(self, testdir):
+ testdir.getmodulecol("""
+ class test(object):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*collected 0*",
+ ])
+
+ def test_static_method(self, testdir):
+ """Support for collecting staticmethod tests (#2528, #2699)"""
+ testdir.getmodulecol("""
+ import pytest
+ class Test(object):
+ @staticmethod
+ def test_something():
+ pass
+
+ @pytest.fixture
+ def fix(self):
+ return 1
+
+ @staticmethod
+ def test_fix(fix):
+ assert fix == 1
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*collected 2 items*",
+ "*2 passed in*",
+ ])
+
+ def test_setup_teardown_class_as_classmethod(self, testdir):
+ testdir.makepyfile(test_mod1="""
+ class TestClassMethod(object):
+ @classmethod
+ def setup_class(cls):
+ pass
+ def test_1(self):
+ pass
+ @classmethod
+ def teardown_class(cls):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*1 passed*",
+ ])
+
+ def test_issue1035_obj_has_getattr(self, testdir):
+ modcol = testdir.getmodulecol("""
+ class Chameleon(object):
+ def __getattr__(self, name):
+ return True
+ chameleon = Chameleon()
+ """)
+ colitems = modcol.collect()
+ assert len(colitems) == 0
+
+ def test_issue1579_namedtuple(self, testdir):
+ testdir.makepyfile("""
+ import collections
+
+ TestCase = collections.namedtuple('TestCase', ['a'])
+ """)
+ result = testdir.runpytest('-rw')
+ result.stdout.fnmatch_lines(
+ "*cannot collect test class 'TestCase' "
+ "because it has a __new__ constructor*"
+ )
+
+ def test_issue2234_property(self, testdir):
+ testdir.makepyfile("""
+ class TestCase(object):
+ @property
+ def prop(self):
+ raise NotImplementedError()
+ """)
+ result = testdir.runpytest()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+
+
+class TestGenerator(object):
+ def test_generative_functions(self, testdir):
+ modcol = testdir.getmodulecol("""
+ def func1(arg, arg2):
+ assert arg == arg2
+
+ def test_gen():
+ yield func1, 17, 3*5
+ yield func1, 42, 6*7
+ """)
+ colitems = modcol.collect()
+ assert len(colitems) == 1
+ gencol = colitems[0]
+ assert isinstance(gencol, pytest.Generator)
+ gencolitems = gencol.collect()
+ assert len(gencolitems) == 2
+ assert isinstance(gencolitems[0], pytest.Function)
+ assert isinstance(gencolitems[1], pytest.Function)
+ assert gencolitems[0].name == '[0]'
+ assert gencolitems[0].obj.__name__ == 'func1'
+
+ def test_generative_methods(self, testdir):
+ modcol = testdir.getmodulecol("""
+ def func1(arg, arg2):
+ assert arg == arg2
+ class TestGenMethods(object):
+ def test_gen(self):
+ yield func1, 17, 3*5
+ yield func1, 42, 6*7
+ """)
+ gencol = modcol.collect()[0].collect()[0].collect()[0]
+ assert isinstance(gencol, pytest.Generator)
+ gencolitems = gencol.collect()
+ assert len(gencolitems) == 2
+ assert isinstance(gencolitems[0], pytest.Function)
+ assert isinstance(gencolitems[1], pytest.Function)
+ assert gencolitems[0].name == '[0]'
+ assert gencolitems[0].obj.__name__ == 'func1'
+
+ def test_generative_functions_with_explicit_names(self, testdir):
+ modcol = testdir.getmodulecol("""
+ def func1(arg, arg2):
+ assert arg == arg2
+
+ def test_gen():
+ yield "seventeen", func1, 17, 3*5
+ yield "fortytwo", func1, 42, 6*7
+ """)
+ colitems = modcol.collect()
+ assert len(colitems) == 1
+ gencol = colitems[0]
+ assert isinstance(gencol, pytest.Generator)
+ gencolitems = gencol.collect()
+ assert len(gencolitems) == 2
+ assert isinstance(gencolitems[0], pytest.Function)
+ assert isinstance(gencolitems[1], pytest.Function)
+ assert gencolitems[0].name == "['seventeen']"
+ assert gencolitems[0].obj.__name__ == 'func1'
+ assert gencolitems[1].name == "['fortytwo']"
+ assert gencolitems[1].obj.__name__ == 'func1'
+
+ def test_generative_functions_unique_explicit_names(self, testdir):
+ # generative
+ modcol = testdir.getmodulecol("""
+ def func(): pass
+ def test_gen():
+ yield "name", func
+ yield "name", func
+ """)
+ colitems = modcol.collect()
+ assert len(colitems) == 1
+ gencol = colitems[0]
+ assert isinstance(gencol, pytest.Generator)
+ pytest.raises(ValueError, "gencol.collect()")
+
+ def test_generative_methods_with_explicit_names(self, testdir):
+ modcol = testdir.getmodulecol("""
+ def func1(arg, arg2):
+ assert arg == arg2
+ class TestGenMethods(object):
+ def test_gen(self):
+ yield "m1", func1, 17, 3*5
+ yield "m2", func1, 42, 6*7
+ """)
+ gencol = modcol.collect()[0].collect()[0].collect()[0]
+ assert isinstance(gencol, pytest.Generator)
+ gencolitems = gencol.collect()
+ assert len(gencolitems) == 2
+ assert isinstance(gencolitems[0], pytest.Function)
+ assert isinstance(gencolitems[1], pytest.Function)
+ assert gencolitems[0].name == "['m1']"
+ assert gencolitems[0].obj.__name__ == 'func1'
+ assert gencolitems[1].name == "['m2']"
+ assert gencolitems[1].obj.__name__ == 'func1'
+
+ def test_order_of_execution_generator_same_codeline(self, testdir, tmpdir):
+ o = testdir.makepyfile("""
+ from __future__ import print_function
+ def test_generative_order_of_execution():
+ import py, pytest
+ test_list = []
+ expected_list = list(range(6))
+
+ def list_append(item):
+ test_list.append(item)
+
+ def assert_order_of_execution():
+ print('expected order', expected_list)
+ print('but got ', test_list)
+ assert test_list == expected_list
+
+ for i in expected_list:
+ yield list_append, i
+ yield assert_order_of_execution
+ """)
+ reprec = testdir.inline_run(o)
+ passed, skipped, failed = reprec.countoutcomes()
+ assert passed == 7
+ assert not skipped and not failed
+
+ def test_order_of_execution_generator_different_codeline(self, testdir):
+ o = testdir.makepyfile("""
+ from __future__ import print_function
+ def test_generative_tests_different_codeline():
+ import py, pytest
+ test_list = []
+ expected_list = list(range(3))
+
+ def list_append_2():
+ test_list.append(2)
+
+ def list_append_1():
+ test_list.append(1)
+
+ def list_append_0():
+ test_list.append(0)
+
+ def assert_order_of_execution():
+ print('expected order', expected_list)
+ print('but got ', test_list)
+ assert test_list == expected_list
+
+ yield list_append_0
+ yield list_append_1
+ yield list_append_2
+ yield assert_order_of_execution
+ """)
+ reprec = testdir.inline_run(o)
+ passed, skipped, failed = reprec.countoutcomes()
+ assert passed == 4
+ assert not skipped and not failed
+
+ def test_setupstate_is_preserved_134(self, testdir):
+ # yield-based tests are messy wrt to setupstate because
+ # during collection they already invoke setup functions
+ # and then again when they are run. For now, we want to make sure
+ # that the old 1.3.4 behaviour is preserved such that all
+ # yielded functions all share the same "self" instance that
+ # has been used during collection.
+ o = testdir.makepyfile("""
+ setuplist = []
+ class TestClass(object):
+ def setup_method(self, func):
+ #print "setup_method", self, func
+ setuplist.append(self)
+ self.init = 42
+
+ def teardown_method(self, func):
+ self.init = None
+
+ def test_func1(self):
+ pass
+
+ def test_func2(self):
+ yield self.func2
+ yield self.func2
+
+ def func2(self):
+ assert self.init
+
+ def test_setuplist():
+ # once for test_func2 during collection
+ # once for test_func1 during test run
+ # once for test_func2 during test run
+ #print setuplist
+ assert len(setuplist) == 3, len(setuplist)
+ assert setuplist[0] == setuplist[2], setuplist
+ assert setuplist[1] != setuplist[2], setuplist
+ """)
+ reprec = testdir.inline_run(o, '-v')
+ passed, skipped, failed = reprec.countoutcomes()
+ assert passed == 4
+ assert not skipped and not failed
+
+
+class TestFunction(object):
+ def test_getmodulecollector(self, testdir):
+ item = testdir.getitem("def test_func(): pass")
+ modcol = item.getparent(pytest.Module)
+ assert isinstance(modcol, pytest.Module)
+ assert hasattr(modcol.obj, 'test_func')
+
+ def test_function_as_object_instance_ignored(self, testdir):
+ testdir.makepyfile("""
+ class A(object):
+ def __call__(self, tmpdir):
+ 0/0
+
+ test_a = A()
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome()
+
+ def test_function_equality(self, testdir, tmpdir):
+ from _pytest.fixtures import FixtureManager
+ config = testdir.parseconfigure()
+ session = testdir.Session(config)
+ session._fixturemanager = FixtureManager(session)
+
+ def func1():
+ pass
+
+ def func2():
+ pass
+
+ f1 = pytest.Function(name="name", parent=session, config=config,
+ args=(1,), callobj=func1)
+ assert f1 == f1
+ f2 = pytest.Function(name="name", config=config,
+ callobj=func2, parent=session)
+ assert f1 != f2
+
+ def test_issue197_parametrize_emptyset(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize('arg', [])
+ def test_function(arg):
+ pass
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(skipped=1)
+
+ def test_single_tuple_unwraps_values(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize(('arg',), [(1,)])
+ def test_function(arg):
+ assert arg == 1
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_issue213_parametrize_value_no_equal(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ class A(object):
+ def __eq__(self, other):
+ raise ValueError("not possible")
+ @pytest.mark.parametrize('arg', [A()])
+ def test_function(arg):
+ assert arg.__class__.__name__ == "A"
+ """)
+ reprec = testdir.inline_run("--fulltrace")
+ reprec.assertoutcome(passed=1)
+
+ def test_parametrize_with_non_hashable_values(self, testdir):
+ """Test parametrization with non-hashable values."""
+ testdir.makepyfile("""
+ archival_mapping = {
+ '1.0': {'tag': '1.0'},
+ '1.2.2a1': {'tag': 'release-1.2.2a1'},
+ }
+
+ import pytest
+ @pytest.mark.parametrize('key value'.split(),
+ archival_mapping.items())
+ def test_archival_to_version(key, value):
+ assert key in archival_mapping
+ assert value == archival_mapping[key]
+ """)
+ rec = testdir.inline_run()
+ rec.assertoutcome(passed=2)
+
+ def test_parametrize_with_non_hashable_values_indirect(self, testdir):
+ """Test parametrization with non-hashable values with indirect parametrization."""
+ testdir.makepyfile("""
+ archival_mapping = {
+ '1.0': {'tag': '1.0'},
+ '1.2.2a1': {'tag': 'release-1.2.2a1'},
+ }
+
+ import pytest
+
+ @pytest.fixture
+ def key(request):
+ return request.param
+
+ @pytest.fixture
+ def value(request):
+ return request.param
+
+ @pytest.mark.parametrize('key value'.split(),
+ archival_mapping.items(), indirect=True)
+ def test_archival_to_version(key, value):
+ assert key in archival_mapping
+ assert value == archival_mapping[key]
+ """)
+ rec = testdir.inline_run()
+ rec.assertoutcome(passed=2)
+
+ def test_parametrize_overrides_fixture(self, testdir):
+ """Test parametrization when parameter overrides existing fixture with same name."""
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def value():
+ return 'value'
+
+ @pytest.mark.parametrize('value',
+ ['overridden'])
+ def test_overridden_via_param(value):
+ assert value == 'overridden'
+
+ @pytest.mark.parametrize('somevalue', ['overridden'])
+ def test_not_overridden(value, somevalue):
+ assert value == 'value'
+ assert somevalue == 'overridden'
+
+ @pytest.mark.parametrize('other,value', [('foo', 'overridden')])
+ def test_overridden_via_multiparam(other, value):
+ assert other == 'foo'
+ assert value == 'overridden'
+ """)
+ rec = testdir.inline_run()
+ rec.assertoutcome(passed=3)
+
+ def test_parametrize_overrides_parametrized_fixture(self, testdir):
+ """Test parametrization when parameter overrides existing parametrized fixture with same name."""
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(params=[1, 2])
+ def value(request):
+ return request.param
+
+ @pytest.mark.parametrize('value',
+ ['overridden'])
+ def test_overridden_via_param(value):
+ assert value == 'overridden'
+ """)
+ rec = testdir.inline_run()
+ rec.assertoutcome(passed=1)
+
+ @ignore_parametrized_marks
+ def test_parametrize_with_mark(self, testdir):
+ items = testdir.getitems("""
+ import pytest
+ @pytest.mark.foo
+ @pytest.mark.parametrize('arg', [
+ 1,
+ pytest.mark.bar(pytest.mark.baz(2))
+ ])
+ def test_function(arg):
+ pass
+ """)
+ keywords = [item.keywords for item in items]
+ assert 'foo' in keywords[0] and 'bar' not in keywords[0] and 'baz' not in keywords[0]
+ assert 'foo' in keywords[1] and 'bar' in keywords[1] and 'baz' in keywords[1]
+
+ def test_function_equality_with_callspec(self, testdir, tmpdir):
+ items = testdir.getitems("""
+ import pytest
+ @pytest.mark.parametrize('arg', [1,2])
+ def test_function(arg):
+ pass
+ """)
+ assert items[0] != items[1]
+ assert not (items[0] == items[1])
+
+ def test_pyfunc_call(self, testdir):
+ item = testdir.getitem("def test_func(): raise ValueError")
+ config = item.config
+
+ class MyPlugin1(object):
+ def pytest_pyfunc_call(self, pyfuncitem):
+ raise ValueError
+
+ class MyPlugin2(object):
+ def pytest_pyfunc_call(self, pyfuncitem):
+ return True
+
+ config.pluginmanager.register(MyPlugin1())
+ config.pluginmanager.register(MyPlugin2())
+ config.hook.pytest_runtest_setup(item=item)
+ config.hook.pytest_pyfunc_call(pyfuncitem=item)
+
+ def test_multiple_parametrize(self, testdir):
+ modcol = testdir.getmodulecol("""
+ import pytest
+ @pytest.mark.parametrize('x', [0, 1])
+ @pytest.mark.parametrize('y', [2, 3])
+ def test1(x, y):
+ pass
+ """)
+ colitems = modcol.collect()
+ assert colitems[0].name == 'test1[2-0]'
+ assert colitems[1].name == 'test1[2-1]'
+ assert colitems[2].name == 'test1[3-0]'
+ assert colitems[3].name == 'test1[3-1]'
+
+ def test_issue751_multiple_parametrize_with_ids(self, testdir):
+ modcol = testdir.getmodulecol("""
+ import pytest
+ @pytest.mark.parametrize('x', [0], ids=['c'])
+ @pytest.mark.parametrize('y', [0, 1], ids=['a', 'b'])
+ class Test(object):
+ def test1(self, x, y):
+ pass
+ def test2(self, x, y):
+ pass
+ """)
+ colitems = modcol.collect()[0].collect()[0].collect()
+ assert colitems[0].name == 'test1[a-c]'
+ assert colitems[1].name == 'test1[b-c]'
+ assert colitems[2].name == 'test2[a-c]'
+ assert colitems[3].name == 'test2[b-c]'
+
+ @ignore_parametrized_marks
+ def test_parametrize_skipif(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ m = pytest.mark.skipif('True')
+
+ @pytest.mark.parametrize('x', [0, 1, m(2)])
+ def test_skip_if(x):
+ assert x < 2
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('* 2 passed, 1 skipped in *')
+
+ @ignore_parametrized_marks
+ def test_parametrize_skip(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ m = pytest.mark.skip('')
+
+ @pytest.mark.parametrize('x', [0, 1, m(2)])
+ def test_skip(x):
+ assert x < 2
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('* 2 passed, 1 skipped in *')
+
+ @ignore_parametrized_marks
+ def test_parametrize_skipif_no_skip(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ m = pytest.mark.skipif('False')
+
+ @pytest.mark.parametrize('x', [0, 1, m(2)])
+ def test_skipif_no_skip(x):
+ assert x < 2
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('* 1 failed, 2 passed in *')
+
+ @ignore_parametrized_marks
+ def test_parametrize_xfail(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ m = pytest.mark.xfail('True')
+
+ @pytest.mark.parametrize('x', [0, 1, m(2)])
+ def test_xfail(x):
+ assert x < 2
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('* 2 passed, 1 xfailed in *')
+
+ @ignore_parametrized_marks
+ def test_parametrize_passed(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ m = pytest.mark.xfail('True')
+
+ @pytest.mark.parametrize('x', [0, 1, m(2)])
+ def test_xfail(x):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('* 2 passed, 1 xpassed in *')
+
+ @ignore_parametrized_marks
+ def test_parametrize_xfail_passed(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ m = pytest.mark.xfail('False')
+
+ @pytest.mark.parametrize('x', [0, 1, m(2)])
+ def test_passed(x):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('* 3 passed in *')
+
+ def test_function_original_name(self, testdir):
+ items = testdir.getitems("""
+ import pytest
+ @pytest.mark.parametrize('arg', [1,2])
+ def test_func(arg):
+ pass
+ """)
+ assert [x.originalname for x in items] == ['test_func', 'test_func']
+
+
+class TestSorting(object):
+ def test_check_equality(self, testdir):
+ modcol = testdir.getmodulecol("""
+ def test_pass(): pass
+ def test_fail(): assert 0
+ """)
+ fn1 = testdir.collect_by_name(modcol, "test_pass")
+ assert isinstance(fn1, pytest.Function)
+ fn2 = testdir.collect_by_name(modcol, "test_pass")
+ assert isinstance(fn2, pytest.Function)
+
+ assert fn1 == fn2
+ assert fn1 != modcol
+ if py.std.sys.version_info < (3, 0):
+ assert cmp(fn1, fn2) == 0
+ assert hash(fn1) == hash(fn2)
+
+ fn3 = testdir.collect_by_name(modcol, "test_fail")
+ assert isinstance(fn3, pytest.Function)
+ assert not (fn1 == fn3)
+ assert fn1 != fn3
+
+ for fn in fn1, fn2, fn3:
+ assert fn != 3
+ assert fn != modcol
+ assert fn != [1, 2, 3]
+ assert [1, 2, 3] != fn
+ assert modcol != fn
+
+ def test_allow_sane_sorting_for_decorators(self, testdir):
+ modcol = testdir.getmodulecol("""
+ def dec(f):
+ g = lambda: f(2)
+ g.place_as = f
+ return g
+
+
+ def test_b(y):
+ pass
+ test_b = dec(test_b)
+
+ def test_a(y):
+ pass
+ test_a = dec(test_a)
+ """)
+ colitems = modcol.collect()
+ assert len(colitems) == 2
+ assert [item.name for item in colitems] == ['test_b', 'test_a']
+
+
+class TestConftestCustomization(object):
+ def test_pytest_pycollect_module(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ class MyModule(pytest.Module):
+ pass
+ def pytest_pycollect_makemodule(path, parent):
+ if path.basename == "test_xyz.py":
+ return MyModule(path, parent)
+ """)
+ testdir.makepyfile("def test_some(): pass")
+ testdir.makepyfile(test_xyz="def test_func(): pass")
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*<Module*test_pytest*",
+ "*<MyModule*xyz*",
+ ])
+
+ def test_customized_pymakemodule_issue205_subdir(self, testdir):
+ b = testdir.mkdir("a").mkdir("b")
+ b.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_pycollect_makemodule():
+ outcome = yield
+ mod = outcome.get_result()
+ mod.obj.hello = "world"
+ """))
+ b.join("test_module.py").write(_pytest._code.Source("""
+ def test_hello():
+ assert hello == "world"
+ """))
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_customized_pymakeitem(self, testdir):
+ b = testdir.mkdir("a").mkdir("b")
+ b.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_pycollect_makeitem():
+ outcome = yield
+ if outcome.excinfo is None:
+ result = outcome.get_result()
+ if result:
+ for func in result:
+ func._some123 = "world"
+ """))
+ b.join("test_module.py").write(_pytest._code.Source("""
+ import pytest
+
+ @pytest.fixture()
+ def obj(request):
+ return request.node._some123
+ def test_hello(obj):
+ assert obj == "world"
+ """))
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_pytest_pycollect_makeitem(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ class MyFunction(pytest.Function):
+ pass
+ def pytest_pycollect_makeitem(collector, name, obj):
+ if name == "some":
+ return MyFunction(name, collector)
+ """)
+ testdir.makepyfile("def some(): pass")
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*MyFunction*some*",
+ ])
+
+ def test_makeitem_non_underscore(self, testdir, monkeypatch):
+ modcol = testdir.getmodulecol("def _hello(): pass")
+ values = []
+ monkeypatch.setattr(pytest.Module, 'makeitem',
+ lambda self, name, obj: values.append(name))
+ values = modcol.collect()
+ assert '_hello' not in values
+
+ def test_issue2369_collect_module_fileext(self, testdir):
+ """Ensure we can collect files with weird file extensions as Python
+ modules (#2369)"""
+ # We'll implement a little finder and loader to import files containing
+ # Python source code whose file extension is ".narf".
+ testdir.makeconftest("""
+ import sys, os, imp
+ from _pytest.python import Module
+
+ class Loader:
+ def load_module(self, name):
+ return imp.load_source(name, name + ".narf")
+ class Finder:
+ def find_module(self, name, path=None):
+ if os.path.exists(name + ".narf"):
+ return Loader()
+ sys.meta_path.append(Finder())
+
+ def pytest_collect_file(path, parent):
+ if path.ext == ".narf":
+ return Module(path, parent)""")
+ testdir.makefile(".narf", """
+ def test_something():
+ assert 1 + 1 == 2""")
+ # Use runpytest_subprocess, since we're futzing with sys.meta_path.
+ result = testdir.runpytest_subprocess()
+ result.stdout.fnmatch_lines('*1 passed*')
+
+
+def test_setup_only_available_in_subdir(testdir):
+ sub1 = testdir.mkpydir("sub1")
+ sub2 = testdir.mkpydir("sub2")
+ sub1.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+ def pytest_runtest_setup(item):
+ assert item.fspath.purebasename == "test_in_sub1"
+ def pytest_runtest_call(item):
+ assert item.fspath.purebasename == "test_in_sub1"
+ def pytest_runtest_teardown(item):
+ assert item.fspath.purebasename == "test_in_sub1"
+ """))
+ sub2.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+ def pytest_runtest_setup(item):
+ assert item.fspath.purebasename == "test_in_sub2"
+ def pytest_runtest_call(item):
+ assert item.fspath.purebasename == "test_in_sub2"
+ def pytest_runtest_teardown(item):
+ assert item.fspath.purebasename == "test_in_sub2"
+ """))
+ sub1.join("test_in_sub1.py").write("def test_1(): pass")
+ sub2.join("test_in_sub2.py").write("def test_2(): pass")
+ result = testdir.runpytest("-v", "-s")
+ result.assert_outcomes(passed=2)
+
+
+def test_modulecol_roundtrip(testdir):
+ modcol = testdir.getmodulecol("pass", withinit=True)
+ trail = modcol.nodeid
+ newcol = modcol.session.perform_collect([trail], genitems=0)[0]
+ assert modcol.name == newcol.name
+
+
+class TestTracebackCutting(object):
+ def test_skip_simple(self):
+ excinfo = pytest.raises(pytest.skip.Exception, 'pytest.skip("xxx")')
+ assert excinfo.traceback[-1].frame.code.name == "skip"
+ assert excinfo.traceback[-1].ishidden()
+
+ def test_traceback_argsetup(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture
+ def hello(request):
+ raise ValueError("xyz")
+ """)
+ p = testdir.makepyfile("def test(hello): pass")
+ result = testdir.runpytest(p)
+ assert result.ret != 0
+ out = result.stdout.str()
+ assert "xyz" in out
+ assert "conftest.py:5: ValueError" in out
+ numentries = out.count("_ _ _") # separator for traceback entries
+ assert numentries == 0
+
+ result = testdir.runpytest("--fulltrace", p)
+ out = result.stdout.str()
+ assert "conftest.py:5: ValueError" in out
+ numentries = out.count("_ _ _ _") # separator for traceback entries
+ assert numentries > 3
+
+ def test_traceback_error_during_import(self, testdir):
+ testdir.makepyfile("""
+ x = 1
+ x = 2
+ x = 17
+ asd
+ """)
+ result = testdir.runpytest()
+ assert result.ret != 0
+ out = result.stdout.str()
+ assert "x = 1" not in out
+ assert "x = 2" not in out
+ result.stdout.fnmatch_lines([
+ " *asd*",
+ "E*NameError*",
+ ])
+ result = testdir.runpytest("--fulltrace")
+ out = result.stdout.str()
+ assert "x = 1" in out
+ assert "x = 2" in out
+ result.stdout.fnmatch_lines([
+ ">*asd*",
+ "E*NameError*",
+ ])
+
+ def test_traceback_filter_error_during_fixture_collection(self, testdir):
+ """integration test for issue #995.
+ """
+ testdir.makepyfile("""
+ import pytest
+
+ def fail_me(func):
+ ns = {}
+ exec('def w(): raise ValueError("fail me")', ns)
+ return ns['w']
+
+ @pytest.fixture(scope='class')
+ @fail_me
+ def fail_fixture():
+ pass
+
+ def test_failing_fixture(fail_fixture):
+ pass
+ """)
+ result = testdir.runpytest()
+ assert result.ret != 0
+ out = result.stdout.str()
+ assert "INTERNALERROR>" not in out
+ result.stdout.fnmatch_lines([
+ "*ValueError: fail me*",
+ "* 1 error in *",
+ ])
+
+ def test_filter_traceback_generated_code(self):
+ """test that filter_traceback() works with the fact that
+ py.code.Code.path attribute might return an str object.
+ In this case, one of the entries on the traceback was produced by
+ dynamically generated code.
+ See: https://bitbucket.org/pytest-dev/py/issues/71
+ This fixes #995.
+ """
+ from _pytest.python import filter_traceback
+ try:
+ ns = {}
+ exec('def foo(): raise ValueError', ns)
+ ns['foo']()
+ except ValueError:
+ _, _, tb = sys.exc_info()
+
+ tb = _pytest._code.Traceback(tb)
+ assert isinstance(tb[-1].path, str)
+ assert not filter_traceback(tb[-1])
+
+ def test_filter_traceback_path_no_longer_valid(self, testdir):
+ """test that filter_traceback() works with the fact that
+ py.code.Code.path attribute might return an str object.
+ In this case, one of the files in the traceback no longer exists.
+ This fixes #1133.
+ """
+ from _pytest.python import filter_traceback
+ testdir.syspathinsert()
+ testdir.makepyfile(filter_traceback_entry_as_str='''
+ def foo():
+ raise ValueError
+ ''')
+ try:
+ import filter_traceback_entry_as_str
+ filter_traceback_entry_as_str.foo()
+ except ValueError:
+ _, _, tb = sys.exc_info()
+
+ testdir.tmpdir.join('filter_traceback_entry_as_str.py').remove()
+ tb = _pytest._code.Traceback(tb)
+ assert isinstance(tb[-1].path, str)
+ assert filter_traceback(tb[-1])
+
+
+class TestReportInfo(object):
+ def test_itemreport_reportinfo(self, testdir, linecomp):
+ testdir.makeconftest("""
+ import pytest
+ class MyFunction(pytest.Function):
+ def reportinfo(self):
+ return "ABCDE", 42, "custom"
+ def pytest_pycollect_makeitem(collector, name, obj):
+ if name == "test_func":
+ return MyFunction(name, parent=collector)
+ """)
+ item = testdir.getitem("def test_func(): pass")
+ item.config.pluginmanager.getplugin("runner")
+ assert item.location == ("ABCDE", 42, "custom")
+
+ def test_func_reportinfo(self, testdir):
+ item = testdir.getitem("def test_func(): pass")
+ fspath, lineno, modpath = item.reportinfo()
+ assert fspath == item.fspath
+ assert lineno == 0
+ assert modpath == "test_func"
+
+ def test_class_reportinfo(self, testdir):
+ modcol = testdir.getmodulecol("""
+ # lineno 0
+ class TestClass(object):
+ def test_hello(self): pass
+ """)
+ classcol = testdir.collect_by_name(modcol, "TestClass")
+ fspath, lineno, msg = classcol.reportinfo()
+ assert fspath == modcol.fspath
+ assert lineno == 1
+ assert msg == "TestClass"
+
+ def test_generator_reportinfo(self, testdir):
+ modcol = testdir.getmodulecol("""
+ # lineno 0
+ def test_gen():
+ def check(x):
+ assert x
+ yield check, 3
+ """)
+ gencol = testdir.collect_by_name(modcol, "test_gen")
+ fspath, lineno, modpath = gencol.reportinfo()
+ assert fspath == modcol.fspath
+ assert lineno == 1
+ assert modpath == "test_gen"
+
+ genitem = gencol.collect()[0]
+ fspath, lineno, modpath = genitem.reportinfo()
+ assert fspath == modcol.fspath
+ assert lineno == 2
+ assert modpath == "test_gen[0]"
+ """
+ def test_func():
+ pass
+ def test_genfunc():
+ def check(x):
+ pass
+ yield check, 3
+ class TestClass(object):
+ def test_method(self):
+ pass
+ """
+
+ def test_reportinfo_with_nasty_getattr(self, testdir):
+ # https://github.com/pytest-dev/pytest/issues/1204
+ modcol = testdir.getmodulecol("""
+ # lineno 0
+ class TestClass(object):
+ def __getattr__(self, name):
+ return "this is not an int"
+
+ def test_foo(self):
+ pass
+ """)
+ classcol = testdir.collect_by_name(modcol, "TestClass")
+ instance = classcol.collect()[0]
+ fspath, lineno, msg = instance.reportinfo()
+
+
+def test_customized_python_discovery(testdir):
+ testdir.makeini("""
+ [pytest]
+ python_files=check_*.py
+ python_classes=Check
+ python_functions=check
+ """)
+ p = testdir.makepyfile("""
+ def check_simple():
+ pass
+ class CheckMyApp(object):
+ def check_meth(self):
+ pass
+ """)
+ p2 = p.new(basename=p.basename.replace("test", "check"))
+ p.move(p2)
+ result = testdir.runpytest("--collect-only", "-s")
+ result.stdout.fnmatch_lines([
+ "*check_customized*",
+ "*check_simple*",
+ "*CheckMyApp*",
+ "*check_meth*",
+ ])
+
+ result = testdir.runpytest()
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*2 passed*",
+ ])
+
+
+def test_customized_python_discovery_functions(testdir):
+ testdir.makeini("""
+ [pytest]
+ python_functions=_test
+ """)
+ testdir.makepyfile("""
+ def _test_underscore():
+ pass
+ """)
+ result = testdir.runpytest("--collect-only", "-s")
+ result.stdout.fnmatch_lines([
+ "*_test_underscore*",
+ ])
+
+ result = testdir.runpytest()
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*1 passed*",
+ ])
+
+
+def test_collector_attributes(testdir):
+ testdir.makeconftest("""
+ import pytest
+ def pytest_pycollect_makeitem(collector):
+ assert collector.Function == pytest.Function
+ assert collector.Class == pytest.Class
+ assert collector.Instance == pytest.Instance
+ assert collector.Module == pytest.Module
+ """)
+ testdir.makepyfile("""
+ def test_hello():
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*1 passed*",
+ ])
+
+
+def test_customize_through_attributes(testdir):
+ testdir.makeconftest("""
+ import pytest
+ class MyFunction(pytest.Function):
+ pass
+ class MyInstance(pytest.Instance):
+ Function = MyFunction
+ class MyClass(pytest.Class):
+ Instance = MyInstance
+
+ def pytest_pycollect_makeitem(collector, name, obj):
+ if name.startswith("MyTestClass"):
+ return MyClass(name, parent=collector)
+ """)
+ testdir.makepyfile("""
+ class MyTestClass(object):
+ def test_hello(self):
+ pass
+ """)
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*MyClass*",
+ "*MyInstance*",
+ "*MyFunction*test_hello*",
+ ])
+
+
+def test_unorderable_types(testdir):
+ testdir.makepyfile("""
+ class TestJoinEmpty(object):
+ pass
+
+ def make_test():
+ class Test(object):
+ pass
+ Test.__name__ = "TestFoo"
+ return Test
+ TestFoo = make_test()
+ """)
+ result = testdir.runpytest()
+ assert "TypeError" not in result.stdout.str()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+
+
+def test_collect_functools_partial(testdir):
+ """
+ Test that collection of functools.partial object works, and arguments
+ to the wrapped functions are dealt correctly (see #811).
+ """
+ testdir.makepyfile("""
+ import functools
+ import pytest
+
+ @pytest.fixture
+ def fix1():
+ return 'fix1'
+
+ @pytest.fixture
+ def fix2():
+ return 'fix2'
+
+ def check1(i, fix1):
+ assert i == 2
+ assert fix1 == 'fix1'
+
+ def check2(fix1, i):
+ assert i == 2
+ assert fix1 == 'fix1'
+
+ def check3(fix1, i, fix2):
+ assert i == 2
+ assert fix1 == 'fix1'
+ assert fix2 == 'fix2'
+
+ test_ok_1 = functools.partial(check1, i=2)
+ test_ok_2 = functools.partial(check1, i=2, fix1='fix1')
+ test_ok_3 = functools.partial(check1, 2)
+ test_ok_4 = functools.partial(check2, i=2)
+ test_ok_5 = functools.partial(check3, i=2)
+ test_ok_6 = functools.partial(check3, i=2, fix1='fix1')
+
+ test_fail_1 = functools.partial(check2, 2)
+ test_fail_2 = functools.partial(check3, 2)
+ """)
+ result = testdir.inline_run()
+ result.assertoutcome(passed=6, failed=2)
+
+
+def test_dont_collect_non_function_callable(testdir):
+ """Test for issue https://github.com/pytest-dev/pytest/issues/331
+
+ In this case an INTERNALERROR occurred trying to report the failure of
+ a test like this one because py test failed to get the source lines.
+ """
+ testdir.makepyfile("""
+ class Oh(object):
+ def __call__(self):
+ pass
+
+ test_a = Oh()
+
+ def test_real():
+ pass
+ """)
+ result = testdir.runpytest('-rw')
+ result.stdout.fnmatch_lines([
+ '*collected 1 item*',
+ "*cannot collect 'test_a' because it is not a function*",
+ '*1 passed, 1 warnings in *',
+ ])
+
+
+def test_class_injection_does_not_break_collection(testdir):
+ """Tests whether injection during collection time will terminate testing.
+
+ In this case the error should not occur if the TestClass itself
+ is modified during collection time, and the original method list
+ is still used for collection.
+ """
+ testdir.makeconftest("""
+ from test_inject import TestClass
+ def pytest_generate_tests(metafunc):
+ TestClass.changed_var = {}
+ """)
+ testdir.makepyfile(test_inject='''
+ class TestClass(object):
+ def test_injection(self):
+ """Test being parametrized."""
+ pass
+ ''')
+ result = testdir.runpytest()
+ assert "RuntimeError: dictionary changed size during iteration" not in result.stdout.str()
+ result.stdout.fnmatch_lines(['*1 passed*'])
+
+
+def test_syntax_error_with_non_ascii_chars(testdir):
+ """Fix decoding issue while formatting SyntaxErrors during collection (#578)
+ """
+ testdir.makepyfile(u"""
+ # -*- coding: UTF-8 -*-
+
+ ☃
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*ERROR collecting*',
+ '*SyntaxError*',
+ '*1 error in*',
+ ])
+
+
+def test_skip_duplicates_by_default(testdir):
+ """Test for issue https://github.com/pytest-dev/pytest/issues/1609 (#1609)
+
+ Ignore duplicate directories.
+ """
+ a = testdir.mkdir("a")
+ fh = a.join("test_a.py")
+ fh.write(_pytest._code.Source("""
+ import pytest
+ def test_real():
+ pass
+ """))
+ result = testdir.runpytest(a.strpath, a.strpath)
+ result.stdout.fnmatch_lines([
+ '*collected 1 item*',
+ ])
+
+
+def test_keep_duplicates(testdir):
+ """Test for issue https://github.com/pytest-dev/pytest/issues/1609 (#1609)
+
+ Use --keep-duplicates to collect tests from duplicate directories.
+ """
+ a = testdir.mkdir("a")
+ fh = a.join("test_a.py")
+ fh.write(_pytest._code.Source("""
+ import pytest
+ def test_real():
+ pass
+ """))
+ result = testdir.runpytest("--keep-duplicates", a.strpath, a.strpath)
+ result.stdout.fnmatch_lines([
+ '*collected 2 item*',
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/fixture.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/fixture.py
new file mode 100644
index 00000000000..b159e8ebb8e
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/fixture.py
@@ -0,0 +1,3171 @@
+from textwrap import dedent
+
+import _pytest._code
+import pytest
+from _pytest.pytester import get_public_names
+from _pytest.fixtures import FixtureLookupError
+from _pytest import fixtures
+
+
+def test_getfuncargnames():
+ def f():
+ pass
+ assert not fixtures.getfuncargnames(f)
+
+ def g(arg):
+ pass
+ assert fixtures.getfuncargnames(g) == ('arg',)
+
+ def h(arg1, arg2="hello"):
+ pass
+ assert fixtures.getfuncargnames(h) == ('arg1',)
+
+ def h(arg1, arg2, arg3="hello"):
+ pass
+ assert fixtures.getfuncargnames(h) == ('arg1', 'arg2')
+
+ class A(object):
+ def f(self, arg1, arg2="hello"):
+ pass
+
+ @staticmethod
+ def static(arg1, arg2):
+ pass
+
+ assert fixtures.getfuncargnames(A().f) == ('arg1',)
+ assert fixtures.getfuncargnames(A.static, cls=A) == ('arg1', 'arg2')
+
+
+class TestFillFixtures(object):
+ def test_fillfuncargs_exposed(self):
+ # used by oejskit, kept for compatibility
+ assert pytest._fillfuncargs == fixtures.fillfixtures
+
+ def test_funcarg_lookupfails(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def xyzsomething(request):
+ return 42
+
+ def test_func(some):
+ pass
+ """)
+ result = testdir.runpytest() # "--collect-only")
+ assert result.ret != 0
+ result.stdout.fnmatch_lines([
+ "*def test_func(some)*",
+ "*fixture*some*not found*",
+ "*xyzsomething*",
+ ])
+
+ def test_funcarg_basic(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+
+ @pytest.fixture
+ def some(request):
+ return request.function.__name__
+ @pytest.fixture
+ def other(request):
+ return 42
+ def test_func(some, other):
+ pass
+ """)
+ fixtures.fillfixtures(item)
+ del item.funcargs["request"]
+ assert len(get_public_names(item.funcargs)) == 2
+ assert item.funcargs['some'] == "test_func"
+ assert item.funcargs['other'] == 42
+
+ def test_funcarg_lookup_modulelevel(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def something(request):
+ return request.function.__name__
+
+ class TestClass(object):
+ def test_method(self, something):
+ assert something == "test_method"
+ def test_func(something):
+ assert something == "test_func"
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+ def test_funcarg_lookup_classlevel(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ class TestClass(object):
+
+ @pytest.fixture
+ def something(self, request):
+ return request.instance
+
+ def test_method(self, something):
+ assert something is self
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*1 passed*"
+ ])
+
+ def test_conftest_funcargs_only_available_in_subdir(self, testdir):
+ sub1 = testdir.mkpydir("sub1")
+ sub2 = testdir.mkpydir("sub2")
+ sub1.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+ @pytest.fixture
+ def arg1(request):
+ pytest.raises(Exception, "request.getfixturevalue('arg2')")
+ """))
+ sub2.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+ @pytest.fixture
+ def arg2(request):
+ pytest.raises(Exception, "request.getfixturevalue('arg1')")
+ """))
+
+ sub1.join("test_in_sub1.py").write("def test_1(arg1): pass")
+ sub2.join("test_in_sub2.py").write("def test_2(arg2): pass")
+ result = testdir.runpytest("-v")
+ result.assert_outcomes(passed=2)
+
+ def test_extend_fixture_module_class(self, testdir):
+ testfile = testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def spam():
+ return 'spam'
+
+ class TestSpam(object):
+
+ @pytest.fixture
+ def spam(self, spam):
+ return spam * 2
+
+ def test_spam(self, spam):
+ assert spam == 'spamspam'
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["*1 passed*"])
+ result = testdir.runpytest(testfile)
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_extend_fixture_conftest_module(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture
+ def spam():
+ return 'spam'
+ """)
+ testfile = testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def spam(spam):
+ return spam * 2
+
+ def test_spam(spam):
+ assert spam == 'spamspam'
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["*1 passed*"])
+ result = testdir.runpytest(testfile)
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_extend_fixture_conftest_conftest(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture
+ def spam():
+ return 'spam'
+ """)
+ pkg = testdir.mkpydir("pkg")
+ pkg.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+
+ @pytest.fixture
+ def spam(spam):
+ return spam * 2
+ """))
+ testfile = pkg.join("test_spam.py")
+ testfile.write(_pytest._code.Source("""
+ def test_spam(spam):
+ assert spam == "spamspam"
+ """))
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["*1 passed*"])
+ result = testdir.runpytest(testfile)
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_extend_fixture_conftest_plugin(self, testdir):
+ testdir.makepyfile(testplugin="""
+ import pytest
+
+ @pytest.fixture
+ def foo():
+ return 7
+ """)
+ testdir.syspathinsert()
+ testdir.makeconftest("""
+ import pytest
+
+ pytest_plugins = 'testplugin'
+
+ @pytest.fixture
+ def foo(foo):
+ return foo + 7
+ """)
+ testdir.makepyfile("""
+ def test_foo(foo):
+ assert foo == 14
+ """)
+ result = testdir.runpytest('-s')
+ assert result.ret == 0
+
+ def test_extend_fixture_plugin_plugin(self, testdir):
+ # Two plugins should extend each order in loading order
+ testdir.makepyfile(testplugin0="""
+ import pytest
+
+ @pytest.fixture
+ def foo():
+ return 7
+ """)
+ testdir.makepyfile(testplugin1="""
+ import pytest
+
+ @pytest.fixture
+ def foo(foo):
+ return foo + 7
+ """)
+ testdir.syspathinsert()
+ testdir.makepyfile("""
+ pytest_plugins = ['testplugin0', 'testplugin1']
+
+ def test_foo(foo):
+ assert foo == 14
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 0
+
+ def test_override_parametrized_fixture_conftest_module(self, testdir):
+ """Test override of the parametrized fixture with non-parametrized one on the test module level."""
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture(params=[1, 2, 3])
+ def spam(request):
+ return request.param
+ """)
+ testfile = testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def spam():
+ return 'spam'
+
+ def test_spam(spam):
+ assert spam == 'spam'
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["*1 passed*"])
+ result = testdir.runpytest(testfile)
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_override_parametrized_fixture_conftest_conftest(self, testdir):
+ """Test override of the parametrized fixture with non-parametrized one on the conftest level."""
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture(params=[1, 2, 3])
+ def spam(request):
+ return request.param
+ """)
+ subdir = testdir.mkpydir('subdir')
+ subdir.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+
+ @pytest.fixture
+ def spam():
+ return 'spam'
+ """))
+ testfile = subdir.join("test_spam.py")
+ testfile.write(_pytest._code.Source("""
+ def test_spam(spam):
+ assert spam == "spam"
+ """))
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["*1 passed*"])
+ result = testdir.runpytest(testfile)
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_override_non_parametrized_fixture_conftest_module(self, testdir):
+ """Test override of the non-parametrized fixture with parametrized one on the test module level."""
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture
+ def spam():
+ return 'spam'
+ """)
+ testfile = testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(params=[1, 2, 3])
+ def spam(request):
+ return request.param
+
+ params = {'spam': 1}
+
+ def test_spam(spam):
+ assert spam == params['spam']
+ params['spam'] += 1
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["*3 passed*"])
+ result = testdir.runpytest(testfile)
+ result.stdout.fnmatch_lines(["*3 passed*"])
+
+ def test_override_non_parametrized_fixture_conftest_conftest(self, testdir):
+ """Test override of the non-parametrized fixture with parametrized one on the conftest level."""
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture
+ def spam():
+ return 'spam'
+ """)
+ subdir = testdir.mkpydir('subdir')
+ subdir.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+
+ @pytest.fixture(params=[1, 2, 3])
+ def spam(request):
+ return request.param
+ """))
+ testfile = subdir.join("test_spam.py")
+ testfile.write(_pytest._code.Source("""
+ params = {'spam': 1}
+
+ def test_spam(spam):
+ assert spam == params['spam']
+ params['spam'] += 1
+ """))
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["*3 passed*"])
+ result = testdir.runpytest(testfile)
+ result.stdout.fnmatch_lines(["*3 passed*"])
+
+ def test_override_autouse_fixture_with_parametrized_fixture_conftest_conftest(self, testdir):
+ """Test override of the autouse fixture with parametrized one on the conftest level.
+ This test covers the issue explained in issue 1601
+ """
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture(autouse=True)
+ def spam():
+ return 'spam'
+ """)
+ subdir = testdir.mkpydir('subdir')
+ subdir.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+
+ @pytest.fixture(params=[1, 2, 3])
+ def spam(request):
+ return request.param
+ """))
+ testfile = subdir.join("test_spam.py")
+ testfile.write(_pytest._code.Source("""
+ params = {'spam': 1}
+
+ def test_spam(spam):
+ assert spam == params['spam']
+ params['spam'] += 1
+ """))
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["*3 passed*"])
+ result = testdir.runpytest(testfile)
+ result.stdout.fnmatch_lines(["*3 passed*"])
+
+ def test_autouse_fixture_plugin(self, testdir):
+ # A fixture from a plugin has no baseid set, which screwed up
+ # the autouse fixture handling.
+ testdir.makepyfile(testplugin="""
+ import pytest
+
+ @pytest.fixture(autouse=True)
+ def foo(request):
+ request.function.foo = 7
+ """)
+ testdir.syspathinsert()
+ testdir.makepyfile("""
+ pytest_plugins = 'testplugin'
+
+ def test_foo(request):
+ assert request.function.foo == 7
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 0
+
+ def test_funcarg_lookup_error(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture
+ def a_fixture(): pass
+
+ @pytest.fixture
+ def b_fixture(): pass
+
+ @pytest.fixture
+ def c_fixture(): pass
+
+ @pytest.fixture
+ def d_fixture(): pass
+ """)
+ testdir.makepyfile("""
+ def test_lookup_error(unknown):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*ERROR at setup of test_lookup_error*",
+ " def test_lookup_error(unknown):*",
+ "E fixture 'unknown' not found",
+ "> available fixtures:*a_fixture,*b_fixture,*c_fixture,*d_fixture*monkeypatch,*", # sorted
+ "> use 'py*test --fixtures *' for help on them.",
+ "*1 error*",
+ ])
+ assert "INTERNAL" not in result.stdout.str()
+
+ def test_fixture_excinfo_leak(self, testdir):
+ # on python2 sys.excinfo would leak into fixture executions
+ testdir.makepyfile("""
+ import sys
+ import traceback
+ import pytest
+
+ @pytest.fixture
+ def leak():
+ if sys.exc_info()[0]: # python3 bug :)
+ traceback.print_exc()
+ #fails
+ assert sys.exc_info() == (None, None, None)
+
+ def test_leak(leak):
+ if sys.exc_info()[0]: # python3 bug :)
+ traceback.print_exc()
+ assert sys.exc_info() == (None, None, None)
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 0
+
+
+class TestRequestBasic(object):
+ def test_request_attributes(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+
+ @pytest.fixture
+ def something(request): pass
+ def test_func(something): pass
+ """)
+ req = fixtures.FixtureRequest(item)
+ assert req.function == item.obj
+ assert req.keywords == item.keywords
+ assert hasattr(req.module, 'test_func')
+ assert req.cls is None
+ assert req.function.__name__ == "test_func"
+ assert req.config == item.config
+ assert repr(req).find(req.function.__name__) != -1
+
+ def test_request_attributes_method(self, testdir):
+ item, = testdir.getitems("""
+ import pytest
+ class TestB(object):
+
+ @pytest.fixture
+ def something(self, request):
+ return 1
+ def test_func(self, something):
+ pass
+ """)
+ req = item._request
+ assert req.cls.__name__ == "TestB"
+ assert req.instance.__class__ == req.cls
+
+ def test_request_contains_funcarg_arg2fixturedefs(self, testdir):
+ modcol = testdir.getmodulecol("""
+ import pytest
+ @pytest.fixture
+ def something(request):
+ pass
+ class TestClass(object):
+ def test_method(self, something):
+ pass
+ """)
+ item1, = testdir.genitems([modcol])
+ assert item1.name == "test_method"
+ arg2fixturedefs = fixtures.FixtureRequest(item1)._arg2fixturedefs
+ assert len(arg2fixturedefs) == 1
+ assert arg2fixturedefs['something'][0].argname == "something"
+
+ def test_getfixturevalue_recursive(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture
+ def something(request):
+ return 1
+ """)
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def something(request):
+ return request.getfixturevalue("something") + 1
+ def test_func(something):
+ assert something == 2
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ @pytest.mark.parametrize(
+ 'getfixmethod', ('getfixturevalue', 'getfuncargvalue'))
+ def test_getfixturevalue(self, testdir, getfixmethod):
+ item = testdir.getitem("""
+ import pytest
+ values = [2]
+ @pytest.fixture
+ def something(request): return 1
+ @pytest.fixture
+ def other(request):
+ return values.pop()
+ def test_func(something): pass
+ """)
+ import contextlib
+ if getfixmethod == 'getfuncargvalue':
+ warning_expectation = pytest.warns(DeprecationWarning)
+ else:
+ # see #1830 for a cleaner way to accomplish this
+ @contextlib.contextmanager
+ def expecting_no_warning():
+ yield
+
+ warning_expectation = expecting_no_warning()
+
+ req = item._request
+ with warning_expectation:
+ fixture_fetcher = getattr(req, getfixmethod)
+ with pytest.raises(FixtureLookupError):
+ fixture_fetcher("notexists")
+ val = fixture_fetcher("something")
+ assert val == 1
+ val = fixture_fetcher("something")
+ assert val == 1
+ val2 = fixture_fetcher("other")
+ assert val2 == 2
+ val2 = fixture_fetcher("other") # see about caching
+ assert val2 == 2
+ pytest._fillfuncargs(item)
+ assert item.funcargs["something"] == 1
+ assert len(get_public_names(item.funcargs)) == 2
+ assert "request" in item.funcargs
+
+ def test_request_addfinalizer(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ teardownlist = []
+ @pytest.fixture
+ def something(request):
+ request.addfinalizer(lambda: teardownlist.append(1))
+ def test_func(something): pass
+ """)
+ item.session._setupstate.prepare(item)
+ pytest._fillfuncargs(item)
+ # successively check finalization calls
+ teardownlist = item.getparent(pytest.Module).obj.teardownlist
+ ss = item.session._setupstate
+ assert not teardownlist
+ ss.teardown_exact(item, None)
+ print(ss.stack)
+ assert teardownlist == [1]
+
+ def test_mark_as_fixture_with_prefix_and_decorator_fails(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture
+ def pytest_funcarg__marked_with_prefix_and_decorator():
+ pass
+ """)
+ result = testdir.runpytest_subprocess()
+ assert result.ret != 0
+ result.stdout.fnmatch_lines([
+ "*AssertionError: fixtures cannot have*@pytest.fixture*",
+ "*pytest_funcarg__marked_with_prefix_and_decorator*"
+ ])
+
+ def test_request_addfinalizer_failing_setup(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = [1]
+ @pytest.fixture
+ def myfix(request):
+ request.addfinalizer(values.pop)
+ assert 0
+ def test_fix(myfix):
+ pass
+ def test_finalizer_ran():
+ assert not values
+ """)
+ reprec = testdir.inline_run("-s")
+ reprec.assertoutcome(failed=1, passed=1)
+
+ def test_request_addfinalizer_failing_setup_module(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = [1, 2]
+ @pytest.fixture(scope="module")
+ def myfix(request):
+ request.addfinalizer(values.pop)
+ request.addfinalizer(values.pop)
+ assert 0
+ def test_fix(myfix):
+ pass
+ """)
+ reprec = testdir.inline_run("-s")
+ mod = reprec.getcalls("pytest_runtest_setup")[0].item.module
+ assert not mod.values
+
+ def test_request_addfinalizer_partial_setup_failure(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture
+ def something(request):
+ request.addfinalizer(lambda: values.append(None))
+ def test_func(something, missingarg):
+ pass
+ def test_second():
+ assert len(values) == 1
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*1 error*" # XXX the whole module collection fails
+ ])
+
+ def test_request_subrequest_addfinalizer_exceptions(self, testdir):
+ """
+ Ensure exceptions raised during teardown by a finalizer are suppressed
+ until all finalizers are called, re-raising the first exception (#2440)
+ """
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ def _excepts(where):
+ raise Exception('Error in %s fixture' % where)
+ @pytest.fixture
+ def subrequest(request):
+ return request
+ @pytest.fixture
+ def something(subrequest):
+ subrequest.addfinalizer(lambda: values.append(1))
+ subrequest.addfinalizer(lambda: values.append(2))
+ subrequest.addfinalizer(lambda: _excepts('something'))
+ @pytest.fixture
+ def excepts(subrequest):
+ subrequest.addfinalizer(lambda: _excepts('excepts'))
+ subrequest.addfinalizer(lambda: values.append(3))
+ def test_first(something, excepts):
+ pass
+ def test_second():
+ assert values == [3, 2, 1]
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*Exception: Error in excepts fixture',
+ '* 2 passed, 1 error in *',
+ ])
+
+ def test_request_getmodulepath(self, testdir):
+ modcol = testdir.getmodulecol("def test_somefunc(): pass")
+ item, = testdir.genitems([modcol])
+ req = fixtures.FixtureRequest(item)
+ assert req.fspath == modcol.fspath
+
+ def test_request_fixturenames(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ from _pytest.pytester import get_public_names
+ @pytest.fixture()
+ def arg1():
+ pass
+ @pytest.fixture()
+ def farg(arg1):
+ pass
+ @pytest.fixture(autouse=True)
+ def sarg(tmpdir):
+ pass
+ def test_function(request, farg):
+ assert set(get_public_names(request.fixturenames)) == \
+ set(["tmpdir", "sarg", "arg1", "request", "farg",
+ "tmpdir_factory"])
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_funcargnames_compatattr(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def pytest_generate_tests(metafunc):
+ assert metafunc.funcargnames == metafunc.fixturenames
+ @pytest.fixture
+ def fn(request):
+ assert request._pyfuncitem.funcargnames == \
+ request._pyfuncitem.fixturenames
+ return request.funcargnames, request.fixturenames
+
+ def test_hello(fn):
+ assert fn[0] == fn[1]
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_setupdecorator_and_xunit(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(scope='module', autouse=True)
+ def setup_module():
+ values.append("module")
+ @pytest.fixture(autouse=True)
+ def setup_function():
+ values.append("function")
+
+ def test_func():
+ pass
+
+ class TestClass(object):
+ @pytest.fixture(scope="class", autouse=True)
+ def setup_class(self):
+ values.append("class")
+ @pytest.fixture(autouse=True)
+ def setup_method(self):
+ values.append("method")
+ def test_method(self):
+ pass
+ def test_all():
+ assert values == ["module", "function", "class",
+ "function", "method", "function"]
+ """)
+ reprec = testdir.inline_run("-v")
+ reprec.assertoutcome(passed=3)
+
+ def test_fixtures_sub_subdir_normalize_sep(self, testdir):
+ # this tests that normalization of nodeids takes place
+ b = testdir.mkdir("tests").mkdir("unit")
+ b.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+ @pytest.fixture
+ def arg1():
+ pass
+ """))
+ p = b.join("test_module.py")
+ p.write("def test_func(arg1): pass")
+ result = testdir.runpytest(p, "--fixtures")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines("""
+ *fixtures defined*conftest*
+ *arg1*
+ """)
+
+ def test_show_fixtures_color_yes(self, testdir):
+ testdir.makepyfile("def test_this(): assert 1")
+ result = testdir.runpytest('--color=yes', '--fixtures')
+ assert '\x1b[32mtmpdir' in result.stdout.str()
+
+ def test_newstyle_with_request(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture()
+ def arg(request):
+ pass
+ def test_1(arg):
+ pass
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_setupcontext_no_param(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(params=[1,2])
+ def arg(request):
+ return request.param
+
+ @pytest.fixture(autouse=True)
+ def mysetup(request, arg):
+ assert not hasattr(request, "param")
+ def test_1(arg):
+ assert arg in (1,2)
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+
+class TestRequestMarking(object):
+ def test_applymarker(self, testdir):
+ item1, item2 = testdir.getitems("""
+ import pytest
+
+ @pytest.fixture
+ def something(request):
+ pass
+ class TestClass(object):
+ def test_func1(self, something):
+ pass
+ def test_func2(self, something):
+ pass
+ """)
+ req1 = fixtures.FixtureRequest(item1)
+ assert 'xfail' not in item1.keywords
+ req1.applymarker(pytest.mark.xfail)
+ assert 'xfail' in item1.keywords
+ assert 'skipif' not in item1.keywords
+ req1.applymarker(pytest.mark.skipif)
+ assert 'skipif' in item1.keywords
+ pytest.raises(ValueError, "req1.applymarker(42)")
+
+ def test_accesskeywords(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture()
+ def keywords(request):
+ return request.keywords
+ @pytest.mark.XYZ
+ def test_function(keywords):
+ assert keywords["XYZ"]
+ assert "abc" not in keywords
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_accessmarker_dynamic(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ @pytest.fixture()
+ def keywords(request):
+ return request.keywords
+
+ @pytest.fixture(scope="class", autouse=True)
+ def marking(request):
+ request.applymarker(pytest.mark.XYZ("hello"))
+ """)
+ testdir.makepyfile("""
+ import pytest
+ def test_fun1(keywords):
+ assert keywords["XYZ"] is not None
+ assert "abc" not in keywords
+ def test_fun2(keywords):
+ assert keywords["XYZ"] is not None
+ assert "abc" not in keywords
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+
+class TestRequestCachedSetup(object):
+ def test_request_cachedsetup_defaultmodule(self, testdir):
+ reprec = testdir.inline_runsource("""
+ mysetup = ["hello",].pop
+
+ import pytest
+
+ @pytest.fixture
+ def something(request):
+ return request.cached_setup(mysetup, scope="module")
+
+ def test_func1(something):
+ assert something == "hello"
+ class TestClass(object):
+ def test_func1a(self, something):
+ assert something == "hello"
+ """)
+ reprec.assertoutcome(passed=2)
+
+ def test_request_cachedsetup_class(self, testdir):
+ reprec = testdir.inline_runsource("""
+ mysetup = ["hello", "hello2", "hello3"].pop
+
+ import pytest
+ @pytest.fixture
+ def something(request):
+ return request.cached_setup(mysetup, scope="class")
+ def test_func1(something):
+ assert something == "hello3"
+ def test_func2(something):
+ assert something == "hello2"
+ class TestClass(object):
+ def test_func1a(self, something):
+ assert something == "hello"
+ def test_func2b(self, something):
+ assert something == "hello"
+ """)
+ reprec.assertoutcome(passed=4)
+
+ def test_request_cachedsetup_extrakey(self, testdir):
+ item1 = testdir.getitem("def test_func(): pass")
+ req1 = fixtures.FixtureRequest(item1)
+ values = ["hello", "world"]
+
+ def setup():
+ return values.pop()
+
+ ret1 = req1.cached_setup(setup, extrakey=1)
+ ret2 = req1.cached_setup(setup, extrakey=2)
+ assert ret2 == "hello"
+ assert ret1 == "world"
+ ret1b = req1.cached_setup(setup, extrakey=1)
+ ret2b = req1.cached_setup(setup, extrakey=2)
+ assert ret1 == ret1b
+ assert ret2 == ret2b
+
+ def test_request_cachedsetup_cache_deletion(self, testdir):
+ item1 = testdir.getitem("def test_func(): pass")
+ req1 = fixtures.FixtureRequest(item1)
+ values = []
+
+ def setup():
+ values.append("setup")
+
+ def teardown(val):
+ values.append("teardown")
+
+ req1.cached_setup(setup, teardown, scope="function")
+ assert values == ['setup']
+ # artificial call of finalizer
+ setupstate = req1._pyfuncitem.session._setupstate
+ setupstate._callfinalizers(item1)
+ assert values == ["setup", "teardown"]
+ req1.cached_setup(setup, teardown, scope="function")
+ assert values == ["setup", "teardown", "setup"]
+ setupstate._callfinalizers(item1)
+ assert values == ["setup", "teardown", "setup", "teardown"]
+
+ def test_request_cached_setup_two_args(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def arg1(request):
+ return request.cached_setup(lambda: 42)
+ @pytest.fixture
+ def arg2(request):
+ return request.cached_setup(lambda: 17)
+ def test_two_different_setups(arg1, arg2):
+ assert arg1 != arg2
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines([
+ "*1 passed*"
+ ])
+
+ def test_request_cached_setup_getfixturevalue(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def arg1(request):
+ arg1 = request.getfixturevalue("arg2")
+ return request.cached_setup(lambda: arg1 + 1)
+ @pytest.fixture
+ def arg2(request):
+ return request.cached_setup(lambda: 10)
+ def test_two_funcarg(arg1):
+ assert arg1 == 11
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines([
+ "*1 passed*"
+ ])
+
+ def test_request_cached_setup_functional(self, testdir):
+ testdir.makepyfile(test_0="""
+ import pytest
+ values = []
+ @pytest.fixture
+ def something(request):
+ val = request.cached_setup(fsetup, fteardown)
+ return val
+ def fsetup(mycache=[1]):
+ values.append(mycache.pop())
+ return values
+ def fteardown(something):
+ values.remove(something[0])
+ values.append(2)
+ def test_list_once(something):
+ assert something == [1]
+ def test_list_twice(something):
+ assert something == [1]
+ """)
+ testdir.makepyfile(test_1="""
+ import test_0 # should have run already
+ def test_check_test0_has_teardown_correct():
+ assert test_0.values == [2]
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines([
+ "*3 passed*"
+ ])
+
+ def test_issue117_sessionscopeteardown(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def app(request):
+ app = request.cached_setup(
+ scope='session',
+ setup=lambda: 0,
+ teardown=lambda x: 3/x)
+ return app
+ def test_func(app):
+ pass
+ """)
+ result = testdir.runpytest()
+ assert result.ret != 0
+ result.stdout.fnmatch_lines([
+ "*3/x*",
+ "*ZeroDivisionError*",
+ ])
+
+
+class TestFixtureUsages(object):
+ def test_noargfixturedec(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture
+ def arg1():
+ return 1
+
+ def test_func(arg1):
+ assert arg1 == 1
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_receives_funcargs(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture()
+ def arg1():
+ return 1
+
+ @pytest.fixture()
+ def arg2(arg1):
+ return arg1 + 1
+
+ def test_add(arg2):
+ assert arg2 == 2
+ def test_all(arg1, arg2):
+ assert arg1 == 1
+ assert arg2 == 2
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+ def test_receives_funcargs_scope_mismatch(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope="function")
+ def arg1():
+ return 1
+
+ @pytest.fixture(scope="module")
+ def arg2(arg1):
+ return arg1 + 1
+
+ def test_add(arg2):
+ assert arg2 == 2
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*ScopeMismatch*involved factories*",
+ "* def arg2*",
+ "* def arg1*",
+ "*1 error*"
+ ])
+
+ def test_receives_funcargs_scope_mismatch_issue660(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope="function")
+ def arg1():
+ return 1
+
+ @pytest.fixture(scope="module")
+ def arg2(arg1):
+ return arg1 + 1
+
+ def test_add(arg1, arg2):
+ assert arg2 == 2
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*ScopeMismatch*involved factories*",
+ "* def arg2*",
+ "*1 error*"
+ ])
+
+ def test_invalid_scope(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope="functions")
+ def badscope():
+ pass
+
+ def test_nothing(badscope):
+ pass
+ """)
+ result = testdir.runpytest_inprocess()
+ result.stdout.fnmatch_lines(
+ ("*ValueError: fixture badscope from test_invalid_scope.py has an unsupported"
+ " scope value 'functions'")
+ )
+
+ def test_funcarg_parametrized_and_used_twice(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(params=[1,2])
+ def arg1(request):
+ values.append(1)
+ return request.param
+
+ @pytest.fixture()
+ def arg2(arg1):
+ return arg1 + 1
+
+ def test_add(arg1, arg2):
+ assert arg2 == arg1 + 1
+ assert len(values) == arg1
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*2 passed*"
+ ])
+
+ def test_factory_uses_unknown_funcarg_as_dependency_error(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture()
+ def fail(missing):
+ return
+
+ @pytest.fixture()
+ def call_fail(fail):
+ return
+
+ def test_missing(call_fail):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ *pytest.fixture()*
+ *def call_fail(fail)*
+ *pytest.fixture()*
+ *def fail*
+ *fixture*'missing'*not found*
+ """)
+
+ def test_factory_setup_as_classes_fails(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ class arg1(object):
+ def __init__(self, request):
+ self.x = 1
+ arg1 = pytest.fixture()(arg1)
+
+ """)
+ reprec = testdir.inline_run()
+ values = reprec.getfailedcollections()
+ assert len(values) == 1
+
+ def test_request_can_be_overridden(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture()
+ def request(request):
+ request.a = 1
+ return request
+ def test_request(request):
+ assert request.a == 1
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_usefixtures_marker(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ values = []
+
+ @pytest.fixture(scope="class")
+ def myfix(request):
+ request.cls.hello = "world"
+ values.append(1)
+
+ class TestClass(object):
+ def test_one(self):
+ assert self.hello == "world"
+ assert len(values) == 1
+ def test_two(self):
+ assert self.hello == "world"
+ assert len(values) == 1
+ pytest.mark.usefixtures("myfix")(TestClass)
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+ def test_usefixtures_ini(self, testdir):
+ testdir.makeini("""
+ [pytest]
+ usefixtures = myfix
+ """)
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture(scope="class")
+ def myfix(request):
+ request.cls.hello = "world"
+
+ """)
+ testdir.makepyfile("""
+ class TestClass(object):
+ def test_one(self):
+ assert self.hello == "world"
+ def test_two(self):
+ assert self.hello == "world"
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+ def test_usefixtures_seen_in_showmarkers(self, testdir):
+ result = testdir.runpytest("--markers")
+ result.stdout.fnmatch_lines("""
+ *usefixtures(fixturename1*mark tests*fixtures*
+ """)
+
+ def test_request_instance_issue203(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ class TestClass(object):
+ @pytest.fixture
+ def setup1(self, request):
+ assert self == request.instance
+ self.arg1 = 1
+ def test_hello(self, setup1):
+ assert self.arg1 == 1
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_fixture_parametrized_with_iterator(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ values = []
+ def f():
+ yield 1
+ yield 2
+ dec = pytest.fixture(scope="module", params=f())
+
+ @dec
+ def arg(request):
+ return request.param
+ @dec
+ def arg2(request):
+ return request.param
+
+ def test_1(arg):
+ values.append(arg)
+ def test_2(arg2):
+ values.append(arg2*10)
+ """)
+ reprec = testdir.inline_run("-v")
+ reprec.assertoutcome(passed=4)
+ values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
+ assert values == [1, 2, 10, 20]
+
+
+class TestFixtureManagerParseFactories(object):
+
+ @pytest.fixture
+ def testdir(self, request):
+ testdir = request.getfixturevalue("testdir")
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture
+ def hello(request):
+ return "conftest"
+
+ @pytest.fixture
+ def fm(request):
+ return request._fixturemanager
+
+ @pytest.fixture
+ def item(request):
+ return request._pyfuncitem
+ """)
+ return testdir
+
+ def test_parsefactories_evil_objects_issue214(self, testdir):
+ testdir.makepyfile("""
+ class A(object):
+ def __call__(self):
+ pass
+ def __getattr__(self, name):
+ raise RuntimeError()
+ a = A()
+ def test_hello():
+ pass
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1, failed=0)
+
+ def test_parsefactories_conftest(self, testdir):
+ testdir.makepyfile("""
+ def test_hello(item, fm):
+ for name in ("fm", "hello", "item"):
+ faclist = fm.getfixturedefs(name, item.nodeid)
+ assert len(faclist) == 1
+ fac = faclist[0]
+ assert fac.func.__name__ == name
+ """)
+ reprec = testdir.inline_run("-s")
+ reprec.assertoutcome(passed=1)
+
+ def test_parsefactories_conftest_and_module_and_class(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def hello(request):
+ return "module"
+ class TestClass(object):
+ @pytest.fixture
+ def hello(self, request):
+ return "class"
+ def test_hello(self, item, fm):
+ faclist = fm.getfixturedefs("hello", item.nodeid)
+ print (faclist)
+ assert len(faclist) == 3
+ assert faclist[0].func(item._request) == "conftest"
+ assert faclist[1].func(item._request) == "module"
+ assert faclist[2].func(item._request) == "class"
+ """)
+ reprec = testdir.inline_run("-s")
+ reprec.assertoutcome(passed=1)
+
+ def test_parsefactories_relative_node_ids(self, testdir):
+ # example mostly taken from:
+ # https://mail.python.org/pipermail/pytest-dev/2014-September/002617.html
+ runner = testdir.mkdir("runner")
+ package = testdir.mkdir("package")
+ package.join("conftest.py").write(dedent("""\
+ import pytest
+ @pytest.fixture
+ def one():
+ return 1
+ """))
+ package.join("test_x.py").write(dedent("""\
+ def test_x(one):
+ assert one == 1
+ """))
+ sub = package.mkdir("sub")
+ sub.join("__init__.py").ensure()
+ sub.join("conftest.py").write(dedent("""\
+ import pytest
+ @pytest.fixture
+ def one():
+ return 2
+ """))
+ sub.join("test_y.py").write(dedent("""\
+ def test_x(one):
+ assert one == 2
+ """))
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+ with runner.as_cwd():
+ reprec = testdir.inline_run("..")
+ reprec.assertoutcome(passed=2)
+
+
+class TestAutouseDiscovery(object):
+
+ @pytest.fixture
+ def testdir(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ @pytest.fixture(autouse=True)
+ def perfunction(request, tmpdir):
+ pass
+
+ @pytest.fixture()
+ def arg1(tmpdir):
+ pass
+ @pytest.fixture(autouse=True)
+ def perfunction2(arg1):
+ pass
+
+ @pytest.fixture
+ def fm(request):
+ return request._fixturemanager
+
+ @pytest.fixture
+ def item(request):
+ return request._pyfuncitem
+ """)
+ return testdir
+
+ def test_parsefactories_conftest(self, testdir):
+ testdir.makepyfile("""
+ from _pytest.pytester import get_public_names
+ def test_check_setup(item, fm):
+ autousenames = fm._getautousenames(item.nodeid)
+ assert len(get_public_names(autousenames)) == 2
+ assert "perfunction2" in autousenames
+ assert "perfunction" in autousenames
+ """)
+ reprec = testdir.inline_run("-s")
+ reprec.assertoutcome(passed=1)
+
+ def test_two_classes_separated_autouse(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ class TestA(object):
+ values = []
+ @pytest.fixture(autouse=True)
+ def setup1(self):
+ self.values.append(1)
+ def test_setup1(self):
+ assert self.values == [1]
+ class TestB(object):
+ values = []
+ @pytest.fixture(autouse=True)
+ def setup2(self):
+ self.values.append(1)
+ def test_setup2(self):
+ assert self.values == [1]
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+ def test_setup_at_classlevel(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ class TestClass(object):
+ @pytest.fixture(autouse=True)
+ def permethod(self, request):
+ request.instance.funcname = request.function.__name__
+ def test_method1(self):
+ assert self.funcname == "test_method1"
+ def test_method2(self):
+ assert self.funcname == "test_method2"
+ """)
+ reprec = testdir.inline_run("-s")
+ reprec.assertoutcome(passed=2)
+
+ @pytest.mark.xfail(reason="'enabled' feature not implemented")
+ def test_setup_enabled_functionnode(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ def enabled(parentnode, markers):
+ return "needsdb" in markers
+
+ @pytest.fixture(params=[1,2])
+ def db(request):
+ return request.param
+
+ @pytest.fixture(enabled=enabled, autouse=True)
+ def createdb(db):
+ pass
+
+ def test_func1(request):
+ assert "db" not in request.fixturenames
+
+ @pytest.mark.needsdb
+ def test_func2(request):
+ assert "db" in request.fixturenames
+ """)
+ reprec = testdir.inline_run("-s")
+ reprec.assertoutcome(passed=2)
+
+ def test_callables_nocode(self, testdir):
+ """
+ a imported mock.call would break setup/factory discovery
+ due to it being callable and __code__ not being a code object
+ """
+ testdir.makepyfile("""
+ class _call(tuple):
+ def __call__(self, *k, **kw):
+ pass
+ def __getattr__(self, k):
+ return self
+
+ call = _call()
+ """)
+ reprec = testdir.inline_run("-s")
+ reprec.assertoutcome(failed=0, passed=0)
+
+ def test_autouse_in_conftests(self, testdir):
+ a = testdir.mkdir("a")
+ b = testdir.mkdir("a1")
+ conftest = testdir.makeconftest("""
+ import pytest
+ @pytest.fixture(autouse=True)
+ def hello():
+ xxx
+ """)
+ conftest.move(a.join(conftest.basename))
+ a.join("test_something.py").write("def test_func(): pass")
+ b.join("test_otherthing.py").write("def test_func(): pass")
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ *1 passed*1 error*
+ """)
+
+ def test_autouse_in_module_and_two_classes(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(autouse=True)
+ def append1():
+ values.append("module")
+ def test_x():
+ assert values == ["module"]
+
+ class TestA(object):
+ @pytest.fixture(autouse=True)
+ def append2(self):
+ values.append("A")
+ def test_hello(self):
+ assert values == ["module", "module", "A"], values
+ class TestA2(object):
+ def test_world(self):
+ assert values == ["module", "module", "A", "module"], values
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=3)
+
+
+class TestAutouseManagement(object):
+ def test_autouse_conftest_mid_directory(self, testdir):
+ pkgdir = testdir.mkpydir("xyz123")
+ pkgdir.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+ @pytest.fixture(autouse=True)
+ def app():
+ import sys
+ sys._myapp = "hello"
+ """))
+ t = pkgdir.ensure("tests", "test_app.py")
+ t.write(_pytest._code.Source("""
+ import sys
+ def test_app():
+ assert sys._myapp == "hello"
+ """))
+ reprec = testdir.inline_run("-s")
+ reprec.assertoutcome(passed=1)
+
+ def test_autouse_honored_for_yield(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(autouse=True)
+ def tst():
+ global x
+ x = 3
+ def test_gen():
+ def f(hello):
+ assert x == abs(hello)
+ yield f, 3
+ yield f, -3
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+ def test_funcarg_and_setup(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(scope="module")
+ def arg():
+ values.append(1)
+ return 0
+ @pytest.fixture(scope="module", autouse=True)
+ def something(arg):
+ values.append(2)
+
+ def test_hello(arg):
+ assert len(values) == 2
+ assert values == [1,2]
+ assert arg == 0
+
+ def test_hello2(arg):
+ assert len(values) == 2
+ assert values == [1,2]
+ assert arg == 0
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+ def test_uses_parametrized_resource(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(params=[1,2])
+ def arg(request):
+ return request.param
+
+ @pytest.fixture(autouse=True)
+ def something(arg):
+ values.append(arg)
+
+ def test_hello():
+ if len(values) == 1:
+ assert values == [1]
+ elif len(values) == 2:
+ assert values == [1, 2]
+ else:
+ 0/0
+
+ """)
+ reprec = testdir.inline_run("-s")
+ reprec.assertoutcome(passed=2)
+
+ def test_session_parametrized_function(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ values = []
+
+ @pytest.fixture(scope="session", params=[1,2])
+ def arg(request):
+ return request.param
+
+ @pytest.fixture(scope="function", autouse=True)
+ def append(request, arg):
+ if request.function.__name__ == "test_some":
+ values.append(arg)
+
+ def test_some():
+ pass
+
+ def test_result(arg):
+ assert len(values) == arg
+ assert values[:arg] == [1,2][:arg]
+ """)
+ reprec = testdir.inline_run("-v", "-s")
+ reprec.assertoutcome(passed=4)
+
+ def test_class_function_parametrization_finalization(self, testdir):
+ p = testdir.makeconftest("""
+ import pytest
+ import pprint
+
+ values = []
+
+ @pytest.fixture(scope="function", params=[1,2])
+ def farg(request):
+ return request.param
+
+ @pytest.fixture(scope="class", params=list("ab"))
+ def carg(request):
+ return request.param
+
+ @pytest.fixture(scope="function", autouse=True)
+ def append(request, farg, carg):
+ def fin():
+ values.append("fin_%s%s" % (carg, farg))
+ request.addfinalizer(fin)
+ """)
+ testdir.makepyfile("""
+ import pytest
+
+ class TestClass(object):
+ def test_1(self):
+ pass
+ class TestClass2(object):
+ def test_2(self):
+ pass
+ """)
+ confcut = "--confcutdir={0}".format(testdir.tmpdir)
+ reprec = testdir.inline_run("-v", "-s", confcut)
+ reprec.assertoutcome(passed=8)
+ config = reprec.getcalls("pytest_unconfigure")[0].config
+ values = config.pluginmanager._getconftestmodules(p)[0].values
+ assert values == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2
+
+ def test_scope_ordering(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(scope="function", autouse=True)
+ def fappend2():
+ values.append(2)
+ @pytest.fixture(scope="class", autouse=True)
+ def classappend3():
+ values.append(3)
+ @pytest.fixture(scope="module", autouse=True)
+ def mappend():
+ values.append(1)
+
+ class TestHallo(object):
+ def test_method(self):
+ assert values == [1,3,2]
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_parametrization_setup_teardown_ordering(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ def pytest_generate_tests(metafunc):
+ if metafunc.cls is not None:
+ metafunc.parametrize("item", [1,2], scope="class")
+ class TestClass(object):
+ @pytest.fixture(scope="class", autouse=True)
+ def addteardown(self, item, request):
+ values.append("setup-%d" % item)
+ request.addfinalizer(lambda: values.append("teardown-%d" % item))
+ def test_step1(self, item):
+ values.append("step1-%d" % item)
+ def test_step2(self, item):
+ values.append("step2-%d" % item)
+
+ def test_finish():
+ print (values)
+ assert values == ["setup-1", "step1-1", "step2-1", "teardown-1",
+ "setup-2", "step1-2", "step2-2", "teardown-2",]
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=5)
+
+ def test_ordering_autouse_before_explicit(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ values = []
+ @pytest.fixture(autouse=True)
+ def fix1():
+ values.append(1)
+ @pytest.fixture()
+ def arg1():
+ values.append(2)
+ def test_hello(arg1):
+ assert values == [1,2]
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ @pytest.mark.issue226
+ @pytest.mark.parametrize("param1", ["", "params=[1]"], ids=["p00", "p01"])
+ @pytest.mark.parametrize("param2", ["", "params=[1]"], ids=["p10", "p11"])
+ def test_ordering_dependencies_torndown_first(self, testdir, param1, param2):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(%(param1)s)
+ def arg1(request):
+ request.addfinalizer(lambda: values.append("fin1"))
+ values.append("new1")
+ @pytest.fixture(%(param2)s)
+ def arg2(request, arg1):
+ request.addfinalizer(lambda: values.append("fin2"))
+ values.append("new2")
+
+ def test_arg(arg2):
+ pass
+ def test_check():
+ assert values == ["new1", "new2", "fin2", "fin1"]
+ """ % locals())
+ reprec = testdir.inline_run("-s")
+ reprec.assertoutcome(passed=2)
+
+
+class TestFixtureMarker(object):
+ def test_parametrize(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(params=["a", "b", "c"])
+ def arg(request):
+ return request.param
+ values = []
+ def test_param(arg):
+ values.append(arg)
+ def test_result():
+ assert values == list("abc")
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=4)
+
+ def test_multiple_parametrization_issue_736(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(params=[1,2,3])
+ def foo(request):
+ return request.param
+
+ @pytest.mark.parametrize('foobar', [4,5,6])
+ def test_issue(foo, foobar):
+ assert foo in [1,2,3]
+ assert foobar in [4,5,6]
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=9)
+
+ @pytest.mark.parametrize('param_args', ["'fixt, val'", "'fixt,val'", "['fixt', 'val']", "('fixt', 'val')"])
+ def test_override_parametrized_fixture_issue_979(self, testdir, param_args):
+ """Make sure a parametrized argument can override a parametrized fixture.
+
+ This was a regression introduced in the fix for #736.
+ """
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(params=[1, 2])
+ def fixt(request):
+ return request.param
+
+ @pytest.mark.parametrize(%s, [(3, 'x'), (4, 'x')])
+ def test_foo(fixt, val):
+ pass
+ """ % param_args)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+ def test_scope_session(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(scope="module")
+ def arg():
+ values.append(1)
+ return 1
+
+ def test_1(arg):
+ assert arg == 1
+ def test_2(arg):
+ assert arg == 1
+ assert len(values) == 1
+ class TestClass(object):
+ def test3(self, arg):
+ assert arg == 1
+ assert len(values) == 1
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=3)
+
+ def test_scope_session_exc(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(scope="session")
+ def fix():
+ values.append(1)
+ pytest.skip('skipping')
+
+ def test_1(fix):
+ pass
+ def test_2(fix):
+ pass
+ def test_last():
+ assert values == [1]
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(skipped=2, passed=1)
+
+ def test_scope_session_exc_two_fix(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ m = []
+ @pytest.fixture(scope="session")
+ def a():
+ values.append(1)
+ pytest.skip('skipping')
+ @pytest.fixture(scope="session")
+ def b(a):
+ m.append(1)
+
+ def test_1(b):
+ pass
+ def test_2(b):
+ pass
+ def test_last():
+ assert values == [1]
+ assert m == []
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(skipped=2, passed=1)
+
+ def test_scope_exc(self, testdir):
+ testdir.makepyfile(
+ test_foo="""
+ def test_foo(fix):
+ pass
+ """,
+ test_bar="""
+ def test_bar(fix):
+ pass
+ """,
+ conftest="""
+ import pytest
+ reqs = []
+ @pytest.fixture(scope="session")
+ def fix(request):
+ reqs.append(1)
+ pytest.skip()
+ @pytest.fixture
+ def req_list():
+ return reqs
+ """,
+ test_real="""
+ def test_last(req_list):
+ assert req_list == [1]
+ """
+ )
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(skipped=2, passed=1)
+
+ def test_scope_module_uses_session(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(scope="module")
+ def arg():
+ values.append(1)
+ return 1
+
+ def test_1(arg):
+ assert arg == 1
+ def test_2(arg):
+ assert arg == 1
+ assert len(values) == 1
+ class TestClass(object):
+ def test3(self, arg):
+ assert arg == 1
+ assert len(values) == 1
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=3)
+
+ def test_scope_module_and_finalizer(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ finalized_list = []
+ created_list = []
+ @pytest.fixture(scope="module")
+ def arg(request):
+ created_list.append(1)
+ assert request.scope == "module"
+ request.addfinalizer(lambda: finalized_list.append(1))
+ @pytest.fixture
+ def created(request):
+ return len(created_list)
+ @pytest.fixture
+ def finalized(request):
+ return len(finalized_list)
+ """)
+ testdir.makepyfile(
+ test_mod1="""
+ def test_1(arg, created, finalized):
+ assert created == 1
+ assert finalized == 0
+ def test_2(arg, created, finalized):
+ assert created == 1
+ assert finalized == 0""",
+ test_mod2="""
+ def test_3(arg, created, finalized):
+ assert created == 2
+ assert finalized == 1""",
+ test_mode3="""
+ def test_4(arg, created, finalized):
+ assert created == 3
+ assert finalized == 2
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=4)
+
+ @pytest.mark.parametrize("method", [
+ 'request.getfixturevalue("arg")',
+ 'request.cached_setup(lambda: None, scope="function")',
+ ], ids=["getfixturevalue", "cached_setup"])
+ def test_scope_mismatch_various(self, testdir, method):
+ testdir.makeconftest("""
+ import pytest
+ finalized = []
+ created = []
+ @pytest.fixture(scope="function")
+ def arg(request):
+ pass
+ """)
+ testdir.makepyfile(
+ test_mod1="""
+ import pytest
+ @pytest.fixture(scope="session")
+ def arg(request):
+ %s
+ def test_1(arg):
+ pass
+ """ % method)
+ result = testdir.runpytest()
+ assert result.ret != 0
+ result.stdout.fnmatch_lines([
+ "*ScopeMismatch*You tried*function*session*request*",
+ ])
+
+ def test_register_only_with_mark(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ @pytest.fixture()
+ def arg():
+ return 1
+ """)
+ testdir.makepyfile(
+ test_mod1="""
+ import pytest
+ @pytest.fixture()
+ def arg(arg):
+ return arg + 1
+ def test_1(arg):
+ assert arg == 2
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_parametrize_and_scope(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope="module", params=["a", "b", "c"])
+ def arg(request):
+ return request.param
+ values = []
+ def test_param(arg):
+ values.append(arg)
+ """)
+ reprec = testdir.inline_run("-v")
+ reprec.assertoutcome(passed=3)
+ values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
+ assert len(values) == 3
+ assert "a" in values
+ assert "b" in values
+ assert "c" in values
+
+ def test_scope_mismatch(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ @pytest.fixture(scope="function")
+ def arg(request):
+ pass
+ """)
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope="session")
+ def arg(arg):
+ pass
+ def test_mismatch(arg):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*ScopeMismatch*",
+ "*1 error*",
+ ])
+
+ def test_parametrize_separated_order(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(scope="module", params=[1, 2])
+ def arg(request):
+ return request.param
+
+ values = []
+ def test_1(arg):
+ values.append(arg)
+ def test_2(arg):
+ values.append(arg)
+ """)
+ reprec = testdir.inline_run("-v")
+ reprec.assertoutcome(passed=4)
+ values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
+ assert values == [1, 1, 2, 2]
+
+ def test_module_parametrized_ordering(self, testdir):
+ testdir.makeini("""
+ [pytest]
+ console_output_style=classic
+ """)
+ testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture(scope="session", params="s1 s2".split())
+ def sarg():
+ pass
+ @pytest.fixture(scope="module", params="m1 m2".split())
+ def marg():
+ pass
+ """)
+ testdir.makepyfile(test_mod1="""
+ def test_func(sarg):
+ pass
+ def test_func1(marg):
+ pass
+ """, test_mod2="""
+ def test_func2(sarg):
+ pass
+ def test_func3(sarg, marg):
+ pass
+ def test_func3b(sarg, marg):
+ pass
+ def test_func4(marg):
+ pass
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines("""
+ test_mod1.py::test_func[s1] PASSED
+ test_mod2.py::test_func2[s1] PASSED
+ test_mod2.py::test_func3[s1-m1] PASSED
+ test_mod2.py::test_func3b[s1-m1] PASSED
+ test_mod2.py::test_func3[s1-m2] PASSED
+ test_mod2.py::test_func3b[s1-m2] PASSED
+ test_mod1.py::test_func[s2] PASSED
+ test_mod2.py::test_func2[s2] PASSED
+ test_mod2.py::test_func3[s2-m1] PASSED
+ test_mod2.py::test_func3b[s2-m1] PASSED
+ test_mod2.py::test_func4[m1] PASSED
+ test_mod2.py::test_func3[s2-m2] PASSED
+ test_mod2.py::test_func3b[s2-m2] PASSED
+ test_mod2.py::test_func4[m2] PASSED
+ test_mod1.py::test_func1[m1] PASSED
+ test_mod1.py::test_func1[m2] PASSED
+ """)
+
+ def test_class_ordering(self, testdir):
+ testdir.makeini("""
+ [pytest]
+ console_output_style=classic
+ """)
+ testdir.makeconftest("""
+ import pytest
+
+ values = []
+
+ @pytest.fixture(scope="function", params=[1,2])
+ def farg(request):
+ return request.param
+
+ @pytest.fixture(scope="class", params=list("ab"))
+ def carg(request):
+ return request.param
+
+ @pytest.fixture(scope="function", autouse=True)
+ def append(request, farg, carg):
+ def fin():
+ values.append("fin_%s%s" % (carg, farg))
+ request.addfinalizer(fin)
+ """)
+ testdir.makepyfile("""
+ import pytest
+
+ class TestClass2(object):
+ def test_1(self):
+ pass
+ def test_2(self):
+ pass
+ class TestClass(object):
+ def test_3(self):
+ pass
+ """)
+ result = testdir.runpytest("-vs")
+ result.stdout.fnmatch_lines("""
+ test_class_ordering.py::TestClass2::test_1[1-a] PASSED
+ test_class_ordering.py::TestClass2::test_1[2-a] PASSED
+ test_class_ordering.py::TestClass2::test_2[1-a] PASSED
+ test_class_ordering.py::TestClass2::test_2[2-a] PASSED
+ test_class_ordering.py::TestClass2::test_1[1-b] PASSED
+ test_class_ordering.py::TestClass2::test_1[2-b] PASSED
+ test_class_ordering.py::TestClass2::test_2[1-b] PASSED
+ test_class_ordering.py::TestClass2::test_2[2-b] PASSED
+ test_class_ordering.py::TestClass::test_3[1-a] PASSED
+ test_class_ordering.py::TestClass::test_3[2-a] PASSED
+ test_class_ordering.py::TestClass::test_3[1-b] PASSED
+ test_class_ordering.py::TestClass::test_3[2-b] PASSED
+ """)
+
+ def test_parametrize_separated_order_higher_scope_first(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(scope="function", params=[1, 2])
+ def arg(request):
+ param = request.param
+ request.addfinalizer(lambda: values.append("fin:%s" % param))
+ values.append("create:%s" % param)
+ return request.param
+
+ @pytest.fixture(scope="module", params=["mod1", "mod2"])
+ def modarg(request):
+ param = request.param
+ request.addfinalizer(lambda: values.append("fin:%s" % param))
+ values.append("create:%s" % param)
+ return request.param
+
+ values = []
+ def test_1(arg):
+ values.append("test1")
+ def test_2(modarg):
+ values.append("test2")
+ def test_3(arg, modarg):
+ values.append("test3")
+ def test_4(modarg, arg):
+ values.append("test4")
+ """)
+ reprec = testdir.inline_run("-v")
+ reprec.assertoutcome(passed=12)
+ values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
+ expected = [
+ 'create:1', 'test1', 'fin:1', 'create:2', 'test1',
+ 'fin:2', 'create:mod1', 'test2', 'create:1', 'test3',
+ 'fin:1', 'create:2', 'test3', 'fin:2', 'create:1',
+ 'test4', 'fin:1', 'create:2', 'test4', 'fin:2',
+ 'fin:mod1', 'create:mod2', 'test2', 'create:1', 'test3',
+ 'fin:1', 'create:2', 'test3', 'fin:2', 'create:1',
+ 'test4', 'fin:1', 'create:2', 'test4', 'fin:2',
+ 'fin:mod2']
+ import pprint
+ pprint.pprint(list(zip(values, expected)))
+ assert values == expected
+
+ def test_parametrized_fixture_teardown_order(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(params=[1,2], scope="class")
+ def param1(request):
+ return request.param
+
+ values = []
+
+ class TestClass(object):
+ @classmethod
+ @pytest.fixture(scope="class", autouse=True)
+ def setup1(self, request, param1):
+ values.append(1)
+ request.addfinalizer(self.teardown1)
+ @classmethod
+ def teardown1(self):
+ assert values.pop() == 1
+ @pytest.fixture(scope="class", autouse=True)
+ def setup2(self, request, param1):
+ values.append(2)
+ request.addfinalizer(self.teardown2)
+ @classmethod
+ def teardown2(self):
+ assert values.pop() == 2
+ def test(self):
+ pass
+
+ def test_finish():
+ assert not values
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines("""
+ *3 passed*
+ """)
+ assert "error" not in result.stdout.str()
+
+ def test_fixture_finalizer(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ import sys
+
+ @pytest.fixture
+ def browser(request):
+
+ def finalize():
+ sys.stdout.write('Finalized')
+ request.addfinalizer(finalize)
+ return {}
+ """)
+ b = testdir.mkdir("subdir")
+ b.join("test_overridden_fixture_finalizer.py").write(dedent("""
+ import pytest
+ @pytest.fixture
+ def browser(browser):
+ browser['visited'] = True
+ return browser
+
+ def test_browser(browser):
+ assert browser['visited'] is True
+ """))
+ reprec = testdir.runpytest("-s")
+ for test in ['test_browser']:
+ reprec.stdout.fnmatch_lines('*Finalized*')
+
+ def test_class_scope_with_normal_tests(self, testdir):
+ testpath = testdir.makepyfile("""
+ import pytest
+
+ class Box(object):
+ value = 0
+
+ @pytest.fixture(scope='class')
+ def a(request):
+ Box.value += 1
+ return Box.value
+
+ def test_a(a):
+ assert a == 1
+
+ class Test1(object):
+ def test_b(self, a):
+ assert a == 2
+
+ class Test2(object):
+ def test_c(self, a):
+ assert a == 3""")
+ reprec = testdir.inline_run(testpath)
+ for test in ['test_a', 'test_b', 'test_c']:
+ assert reprec.matchreport(test).passed
+
+ def test_request_is_clean(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(params=[1, 2])
+ def fix(request):
+ request.addfinalizer(lambda: values.append(request.param))
+ def test_fix(fix):
+ pass
+ """)
+ reprec = testdir.inline_run("-s")
+ values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
+ assert values == [1, 2]
+
+ def test_parametrize_separated_lifecycle(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ values = []
+ @pytest.fixture(scope="module", params=[1, 2])
+ def arg(request):
+ x = request.param
+ request.addfinalizer(lambda: values.append("fin%s" % x))
+ return request.param
+ def test_1(arg):
+ values.append(arg)
+ def test_2(arg):
+ values.append(arg)
+ """)
+ reprec = testdir.inline_run("-vs")
+ reprec.assertoutcome(passed=4)
+ values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
+ import pprint
+ pprint.pprint(values)
+ # assert len(values) == 6
+ assert values[0] == values[1] == 1
+ assert values[2] == "fin1"
+ assert values[3] == values[4] == 2
+ assert values[5] == "fin2"
+
+ def test_parametrize_function_scoped_finalizers_called(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(scope="function", params=[1, 2])
+ def arg(request):
+ x = request.param
+ request.addfinalizer(lambda: values.append("fin%s" % x))
+ return request.param
+
+ values = []
+ def test_1(arg):
+ values.append(arg)
+ def test_2(arg):
+ values.append(arg)
+ def test_3():
+ assert len(values) == 8
+ assert values == [1, "fin1", 2, "fin2", 1, "fin1", 2, "fin2"]
+ """)
+ reprec = testdir.inline_run("-v")
+ reprec.assertoutcome(passed=5)
+
+ @pytest.mark.issue246
+ @pytest.mark.parametrize("scope", ["session", "function", "module"])
+ def test_finalizer_order_on_parametrization(self, scope, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+
+ @pytest.fixture(scope=%(scope)r, params=["1"])
+ def fix1(request):
+ return request.param
+
+ @pytest.fixture(scope=%(scope)r)
+ def fix2(request, base):
+ def cleanup_fix2():
+ assert not values, "base should not have been finalized"
+ request.addfinalizer(cleanup_fix2)
+
+ @pytest.fixture(scope=%(scope)r)
+ def base(request, fix1):
+ def cleanup_base():
+ values.append("fin_base")
+ print ("finalizing base")
+ request.addfinalizer(cleanup_base)
+
+ def test_begin():
+ pass
+ def test_baz(base, fix2):
+ pass
+ def test_other():
+ pass
+ """ % {"scope": scope})
+ reprec = testdir.inline_run("-lvs")
+ reprec.assertoutcome(passed=3)
+
+ @pytest.mark.issue396
+ def test_class_scope_parametrization_ordering(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ @pytest.fixture(params=["John", "Doe"], scope="class")
+ def human(request):
+ request.addfinalizer(lambda: values.append("fin %s" % request.param))
+ return request.param
+
+ class TestGreetings(object):
+ def test_hello(self, human):
+ values.append("test_hello")
+
+ class TestMetrics(object):
+ def test_name(self, human):
+ values.append("test_name")
+
+ def test_population(self, human):
+ values.append("test_population")
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=6)
+ values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
+ assert values == ["test_hello", "fin John", "test_hello", "fin Doe",
+ "test_name", "test_population", "fin John",
+ "test_name", "test_population", "fin Doe"]
+
+ def test_parametrize_setup_function(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(scope="module", params=[1, 2])
+ def arg(request):
+ return request.param
+
+ @pytest.fixture(scope="module", autouse=True)
+ def mysetup(request, arg):
+ request.addfinalizer(lambda: values.append("fin%s" % arg))
+ values.append("setup%s" % arg)
+
+ values = []
+ def test_1(arg):
+ values.append(arg)
+ def test_2(arg):
+ values.append(arg)
+ def test_3():
+ import pprint
+ pprint.pprint(values)
+ if arg == 1:
+ assert values == ["setup1", 1, 1, ]
+ elif arg == 2:
+ assert values == ["setup1", 1, 1, "fin1",
+ "setup2", 2, 2, ]
+
+ """)
+ reprec = testdir.inline_run("-v")
+ reprec.assertoutcome(passed=6)
+
+ def test_fixture_marked_function_not_collected_as_test(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture
+ def test_app():
+ return 1
+
+ def test_something(test_app):
+ assert test_app == 1
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_params_and_ids(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(params=[object(), object()],
+ ids=['alpha', 'beta'])
+ def fix(request):
+ return request.param
+
+ def test_foo(fix):
+ assert 1
+ """)
+ res = testdir.runpytest('-v')
+ res.stdout.fnmatch_lines([
+ '*test_foo*alpha*',
+ '*test_foo*beta*'])
+
+ def test_params_and_ids_yieldfixture(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.yield_fixture(params=[object(), object()],
+ ids=['alpha', 'beta'])
+ def fix(request):
+ yield request.param
+
+ def test_foo(fix):
+ assert 1
+ """)
+ res = testdir.runpytest('-v')
+ res.stdout.fnmatch_lines([
+ '*test_foo*alpha*',
+ '*test_foo*beta*'])
+
+ @pytest.mark.issue920
+ def test_deterministic_fixture_collection(self, testdir, monkeypatch):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(scope="module",
+ params=["A",
+ "B",
+ "C"])
+ def A(request):
+ return request.param
+
+ @pytest.fixture(scope="module",
+ params=["DDDDDDDDD", "EEEEEEEEEEEE", "FFFFFFFFFFF", "banansda"])
+ def B(request, A):
+ return request.param
+
+ def test_foo(B):
+ # Something funky is going on here.
+ # Despite specified seeds, on what is collected,
+ # sometimes we get unexpected passes. hashing B seems
+ # to help?
+ assert hash(B) or True
+ """)
+ monkeypatch.setenv("PYTHONHASHSEED", "1")
+ out1 = testdir.runpytest_subprocess("-v")
+ monkeypatch.setenv("PYTHONHASHSEED", "2")
+ out2 = testdir.runpytest_subprocess("-v")
+ out1 = [line for line in out1.outlines if line.startswith("test_deterministic_fixture_collection.py::test_foo")]
+ out2 = [line for line in out2.outlines if line.startswith("test_deterministic_fixture_collection.py::test_foo")]
+ assert len(out1) == 12
+ assert out1 == out2
+
+
+class TestRequestScopeAccess(object):
+ pytestmark = pytest.mark.parametrize(("scope", "ok", "error"), [
+ ["session", "", "fspath class function module"],
+ ["module", "module fspath", "cls function"],
+ ["class", "module fspath cls", "function"],
+ ["function", "module fspath cls function", ""]
+ ])
+
+ def test_setup(self, testdir, scope, ok, error):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope=%r, autouse=True)
+ def myscoped(request):
+ for x in %r:
+ assert hasattr(request, x)
+ for x in %r:
+ pytest.raises(AttributeError, lambda:
+ getattr(request, x))
+ assert request.session
+ assert request.config
+ def test_func():
+ pass
+ """ % (scope, ok.split(), error.split()))
+ reprec = testdir.inline_run("-l")
+ reprec.assertoutcome(passed=1)
+
+ def test_funcarg(self, testdir, scope, ok, error):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope=%r)
+ def arg(request):
+ for x in %r:
+ assert hasattr(request, x)
+ for x in %r:
+ pytest.raises(AttributeError, lambda:
+ getattr(request, x))
+ assert request.session
+ assert request.config
+ def test_func(arg):
+ pass
+ """ % (scope, ok.split(), error.split()))
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+
+class TestErrors(object):
+ def test_subfactory_missing_funcarg(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture()
+ def gen(qwe123):
+ return 1
+ def test_something(gen):
+ pass
+ """)
+ result = testdir.runpytest()
+ assert result.ret != 0
+ result.stdout.fnmatch_lines([
+ "*def gen(qwe123):*",
+ "*fixture*qwe123*not found*",
+ "*1 error*",
+ ])
+
+ def test_issue498_fixture_finalizer_failing(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture
+ def fix1(request):
+ def f():
+ raise KeyError
+ request.addfinalizer(f)
+ return object()
+
+ values = []
+ def test_1(fix1):
+ values.append(fix1)
+ def test_2(fix1):
+ values.append(fix1)
+ def test_3():
+ assert values[0] != values[1]
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ *ERROR*teardown*test_1*
+ *KeyError*
+ *ERROR*teardown*test_2*
+ *KeyError*
+ *3 pass*2 error*
+ """)
+
+ def test_setupfunc_missing_funcarg(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(autouse=True)
+ def gen(qwe123):
+ return 1
+ def test_something():
+ pass
+ """)
+ result = testdir.runpytest()
+ assert result.ret != 0
+ result.stdout.fnmatch_lines([
+ "*def gen(qwe123):*",
+ "*fixture*qwe123*not found*",
+ "*1 error*",
+ ])
+
+
+class TestShowFixtures(object):
+ def test_funcarg_compat(self, testdir):
+ config = testdir.parseconfigure("--funcargs")
+ assert config.option.showfixtures
+
+ def test_show_fixtures(self, testdir):
+ result = testdir.runpytest("--fixtures")
+ result.stdout.fnmatch_lines([
+ "*tmpdir*",
+ "*temporary directory*",
+ ])
+
+ def test_show_fixtures_verbose(self, testdir):
+ result = testdir.runpytest("--fixtures", "-v")
+ result.stdout.fnmatch_lines([
+ "*tmpdir*--*tmpdir.py*",
+ "*temporary directory*",
+ ])
+
+ def test_show_fixtures_testmodule(self, testdir):
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture
+ def _arg0():
+ """ hidden """
+ @pytest.fixture
+ def arg1():
+ """ hello world """
+ ''')
+ result = testdir.runpytest("--fixtures", p)
+ result.stdout.fnmatch_lines("""
+ *tmpdir
+ *fixtures defined from*
+ *arg1*
+ *hello world*
+ """)
+ assert "arg0" not in result.stdout.str()
+
+ @pytest.mark.parametrize("testmod", [True, False])
+ def test_show_fixtures_conftest(self, testdir, testmod):
+ testdir.makeconftest('''
+ import pytest
+ @pytest.fixture
+ def arg1():
+ """ hello world """
+ ''')
+ if testmod:
+ testdir.makepyfile("""
+ def test_hello():
+ pass
+ """)
+ result = testdir.runpytest("--fixtures")
+ result.stdout.fnmatch_lines("""
+ *tmpdir*
+ *fixtures defined from*conftest*
+ *arg1*
+ *hello world*
+ """)
+
+ def test_show_fixtures_trimmed_doc(self, testdir):
+ p = testdir.makepyfile(dedent('''
+ import pytest
+ @pytest.fixture
+ def arg1():
+ """
+ line1
+ line2
+
+ """
+ @pytest.fixture
+ def arg2():
+ """
+ line1
+ line2
+
+ """
+ '''))
+ result = testdir.runpytest("--fixtures", p)
+ result.stdout.fnmatch_lines(dedent("""
+ * fixtures defined from test_show_fixtures_trimmed_doc *
+ arg2
+ line1
+ line2
+ arg1
+ line1
+ line2
+
+ """))
+
+ def test_show_fixtures_indented_doc(self, testdir):
+ p = testdir.makepyfile(dedent('''
+ import pytest
+ @pytest.fixture
+ def fixture1():
+ """
+ line1
+ indented line
+ """
+ '''))
+ result = testdir.runpytest("--fixtures", p)
+ result.stdout.fnmatch_lines(dedent("""
+ * fixtures defined from test_show_fixtures_indented_doc *
+ fixture1
+ line1
+ indented line
+ """))
+
+ def test_show_fixtures_indented_doc_first_line_unindented(self, testdir):
+ p = testdir.makepyfile(dedent('''
+ import pytest
+ @pytest.fixture
+ def fixture1():
+ """line1
+ line2
+ indented line
+ """
+ '''))
+ result = testdir.runpytest("--fixtures", p)
+ result.stdout.fnmatch_lines(dedent("""
+ * fixtures defined from test_show_fixtures_indented_doc_first_line_unindented *
+ fixture1
+ line1
+ line2
+ indented line
+ """))
+
+ def test_show_fixtures_indented_in_class(self, testdir):
+ p = testdir.makepyfile(dedent('''
+ import pytest
+ class TestClass:
+ @pytest.fixture
+ def fixture1(self):
+ """line1
+ line2
+ indented line
+ """
+ '''))
+ result = testdir.runpytest("--fixtures", p)
+ result.stdout.fnmatch_lines(dedent("""
+ * fixtures defined from test_show_fixtures_indented_in_class *
+ fixture1
+ line1
+ line2
+ indented line
+ """))
+
+ def test_show_fixtures_different_files(self, testdir):
+ """
+ #833: --fixtures only shows fixtures from first file
+ """
+ testdir.makepyfile(test_a='''
+ import pytest
+
+ @pytest.fixture
+ def fix_a():
+ """Fixture A"""
+ pass
+
+ def test_a(fix_a):
+ pass
+ ''')
+ testdir.makepyfile(test_b='''
+ import pytest
+
+ @pytest.fixture
+ def fix_b():
+ """Fixture B"""
+ pass
+
+ def test_b(fix_b):
+ pass
+ ''')
+ result = testdir.runpytest("--fixtures")
+ result.stdout.fnmatch_lines("""
+ * fixtures defined from test_a *
+ fix_a
+ Fixture A
+
+ * fixtures defined from test_b *
+ fix_b
+ Fixture B
+ """)
+
+ def test_show_fixtures_with_same_name(self, testdir):
+ testdir.makeconftest('''
+ import pytest
+ @pytest.fixture
+ def arg1():
+ """Hello World in conftest.py"""
+ return "Hello World"
+ ''')
+ testdir.makepyfile('''
+ def test_foo(arg1):
+ assert arg1 == "Hello World"
+ ''')
+ testdir.makepyfile('''
+ import pytest
+ @pytest.fixture
+ def arg1():
+ """Hi from test module"""
+ return "Hi"
+ def test_bar(arg1):
+ assert arg1 == "Hi"
+ ''')
+ result = testdir.runpytest("--fixtures")
+ result.stdout.fnmatch_lines('''
+ * fixtures defined from conftest *
+ arg1
+ Hello World in conftest.py
+
+ * fixtures defined from test_show_fixtures_with_same_name *
+ arg1
+ Hi from test module
+ ''')
+
+
+@pytest.mark.parametrize('flavor', ['fixture', 'yield_fixture'])
+class TestContextManagerFixtureFuncs(object):
+
+ def test_simple(self, testdir, flavor):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.{flavor}
+ def arg1():
+ print ("setup")
+ yield 1
+ print ("teardown")
+ def test_1(arg1):
+ print ("test1 %s" % arg1)
+ def test_2(arg1):
+ print ("test2 %s" % arg1)
+ assert 0
+ """.format(flavor=flavor))
+ result = testdir.runpytest("-s")
+ result.stdout.fnmatch_lines("""
+ *setup*
+ *test1 1*
+ *teardown*
+ *setup*
+ *test2 1*
+ *teardown*
+ """)
+
+ def test_scoped(self, testdir, flavor):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.{flavor}(scope="module")
+ def arg1():
+ print ("setup")
+ yield 1
+ print ("teardown")
+ def test_1(arg1):
+ print ("test1 %s" % arg1)
+ def test_2(arg1):
+ print ("test2 %s" % arg1)
+ """.format(flavor=flavor))
+ result = testdir.runpytest("-s")
+ result.stdout.fnmatch_lines("""
+ *setup*
+ *test1 1*
+ *test2 1*
+ *teardown*
+ """)
+
+ def test_setup_exception(self, testdir, flavor):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.{flavor}(scope="module")
+ def arg1():
+ pytest.fail("setup")
+ yield 1
+ def test_1(arg1):
+ pass
+ """.format(flavor=flavor))
+ result = testdir.runpytest("-s")
+ result.stdout.fnmatch_lines("""
+ *pytest.fail*setup*
+ *1 error*
+ """)
+
+ def test_teardown_exception(self, testdir, flavor):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.{flavor}(scope="module")
+ def arg1():
+ yield 1
+ pytest.fail("teardown")
+ def test_1(arg1):
+ pass
+ """.format(flavor=flavor))
+ result = testdir.runpytest("-s")
+ result.stdout.fnmatch_lines("""
+ *pytest.fail*teardown*
+ *1 passed*1 error*
+ """)
+
+ def test_yields_more_than_one(self, testdir, flavor):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.{flavor}(scope="module")
+ def arg1():
+ yield 1
+ yield 2
+ def test_1(arg1):
+ pass
+ """.format(flavor=flavor))
+ result = testdir.runpytest("-s")
+ result.stdout.fnmatch_lines("""
+ *fixture function*
+ *test_yields*:2*
+ """)
+
+ def test_custom_name(self, testdir, flavor):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.{flavor}(name='meow')
+ def arg1():
+ return 'mew'
+ def test_1(meow):
+ print(meow)
+ """.format(flavor=flavor))
+ result = testdir.runpytest("-s")
+ result.stdout.fnmatch_lines("*mew*")
+
+
+class TestParameterizedSubRequest(object):
+ def test_call_from_fixture(self, testdir):
+ testfile = testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(params=[0, 1, 2])
+ def fix_with_param(request):
+ return request.param
+
+ @pytest.fixture
+ def get_named_fixture(request):
+ return request.getfixturevalue('fix_with_param')
+
+ def test_foo(request, get_named_fixture):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ E*Failed: The requested fixture has no parameter defined for the current test.
+ E*
+ E*Requested fixture 'fix_with_param' defined in:
+ E*{0}:4
+ E*Requested here:
+ E*{1}:9
+ *1 error*
+ """.format(testfile.basename, testfile.basename))
+
+ def test_call_from_test(self, testdir):
+ testfile = testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(params=[0, 1, 2])
+ def fix_with_param(request):
+ return request.param
+
+ def test_foo(request):
+ request.getfixturevalue('fix_with_param')
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ E*Failed: The requested fixture has no parameter defined for the current test.
+ E*
+ E*Requested fixture 'fix_with_param' defined in:
+ E*{0}:4
+ E*Requested here:
+ E*{1}:8
+ *1 failed*
+ """.format(testfile.basename, testfile.basename))
+
+ def test_external_fixture(self, testdir):
+ conffile = testdir.makeconftest("""
+ import pytest
+
+ @pytest.fixture(params=[0, 1, 2])
+ def fix_with_param(request):
+ return request.param
+ """)
+
+ testfile = testdir.makepyfile("""
+ def test_foo(request):
+ request.getfixturevalue('fix_with_param')
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ E*Failed: The requested fixture has no parameter defined for the current test.
+ E*
+ E*Requested fixture 'fix_with_param' defined in:
+ E*{0}:4
+ E*Requested here:
+ E*{1}:2
+ *1 failed*
+ """.format(conffile.basename, testfile.basename))
+
+ def test_non_relative_path(self, testdir):
+ tests_dir = testdir.mkdir('tests')
+ fixdir = testdir.mkdir('fixtures')
+ fixfile = fixdir.join("fix.py")
+ fixfile.write(_pytest._code.Source("""
+ import pytest
+
+ @pytest.fixture(params=[0, 1, 2])
+ def fix_with_param(request):
+ return request.param
+ """))
+
+ testfile = tests_dir.join("test_foos.py")
+ testfile.write(_pytest._code.Source("""
+ from fix import fix_with_param
+
+ def test_foo(request):
+ request.getfixturevalue('fix_with_param')
+ """))
+
+ tests_dir.chdir()
+ testdir.syspathinsert(fixdir)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ E*Failed: The requested fixture has no parameter defined for the current test.
+ E*
+ E*Requested fixture 'fix_with_param' defined in:
+ E*{0}:5
+ E*Requested here:
+ E*{1}:5
+ *1 failed*
+ """.format(fixfile.strpath, testfile.basename))
+
+
+def test_pytest_fixture_setup_and_post_finalizer_hook(testdir):
+ testdir.makeconftest("""
+ from __future__ import print_function
+ def pytest_fixture_setup(fixturedef, request):
+ print('ROOT setup hook called for {0} from {1}'.format(fixturedef.argname, request.node.name))
+ def pytest_fixture_post_finalizer(fixturedef, request):
+ print('ROOT finalizer hook called for {0} from {1}'.format(fixturedef.argname, request.node.name))
+ """)
+ testdir.makepyfile(**{
+ 'tests/conftest.py': """
+ from __future__ import print_function
+ def pytest_fixture_setup(fixturedef, request):
+ print('TESTS setup hook called for {0} from {1}'.format(fixturedef.argname, request.node.name))
+ def pytest_fixture_post_finalizer(fixturedef, request):
+ print('TESTS finalizer hook called for {0} from {1}'.format(fixturedef.argname, request.node.name))
+ """,
+ 'tests/test_hooks.py': """
+ from __future__ import print_function
+ import pytest
+
+ @pytest.fixture()
+ def my_fixture():
+ return 'some'
+
+ def test_func(my_fixture):
+ print('TEST test_func')
+ assert my_fixture == 'some'
+ """
+ })
+ result = testdir.runpytest("-s")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*TESTS setup hook called for my_fixture from test_func*",
+ "*ROOT setup hook called for my_fixture from test_func*",
+ "*TEST test_func*",
+ "*TESTS finalizer hook called for my_fixture from test_func*",
+ "*ROOT finalizer hook called for my_fixture from test_func*",
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/integration.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/integration.py
new file mode 100644
index 00000000000..6ea29fa98b9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/integration.py
@@ -0,0 +1,384 @@
+import pytest
+from _pytest import python
+from _pytest import runner
+
+
+class TestOEJSKITSpecials(object):
+ def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage
+ testdir.makeconftest("""
+ import pytest
+ def pytest_pycollect_makeitem(collector, name, obj):
+ if name == "MyClass":
+ return MyCollector(name, parent=collector)
+ class MyCollector(pytest.Collector):
+ def reportinfo(self):
+ return self.fspath, 3, "xyz"
+ """)
+ modcol = testdir.getmodulecol("""
+ import pytest
+ @pytest.fixture
+ def arg1(request):
+ return 42
+ class MyClass(object):
+ pass
+ """)
+ # this hook finds funcarg factories
+ rep = runner.collect_one_node(collector=modcol)
+ clscol = rep.result[0]
+ clscol.obj = lambda arg1: None
+ clscol.funcargs = {}
+ pytest._fillfuncargs(clscol)
+ assert clscol.funcargs['arg1'] == 42
+
+ def test_autouse_fixture(self, testdir): # rough jstests usage
+ testdir.makeconftest("""
+ import pytest
+ def pytest_pycollect_makeitem(collector, name, obj):
+ if name == "MyClass":
+ return MyCollector(name, parent=collector)
+ class MyCollector(pytest.Collector):
+ def reportinfo(self):
+ return self.fspath, 3, "xyz"
+ """)
+ modcol = testdir.getmodulecol("""
+ import pytest
+ @pytest.fixture(autouse=True)
+ def hello():
+ pass
+ @pytest.fixture
+ def arg1(request):
+ return 42
+ class MyClass(object):
+ pass
+ """)
+ # this hook finds funcarg factories
+ rep = runner.collect_one_node(modcol)
+ clscol = rep.result[0]
+ clscol.obj = lambda: None
+ clscol.funcargs = {}
+ pytest._fillfuncargs(clscol)
+ assert not clscol.funcargs
+
+
+def test_wrapped_getfslineno():
+ def func():
+ pass
+
+ def wrap(f):
+ func.__wrapped__ = f
+ func.patchings = ["qwe"]
+ return func
+
+ @wrap
+ def wrapped_func(x, y, z):
+ pass
+ fs, lineno = python.getfslineno(wrapped_func)
+ fs2, lineno2 = python.getfslineno(wrap)
+ assert lineno > lineno2, "getfslineno does not unwrap correctly"
+
+
+class TestMockDecoration(object):
+ def test_wrapped_getfuncargnames(self):
+ from _pytest.compat import getfuncargnames
+
+ def wrap(f):
+
+ def func():
+ pass
+
+ func.__wrapped__ = f
+ return func
+
+ @wrap
+ def f(x):
+ pass
+
+ values = getfuncargnames(f)
+ assert values == ("x",)
+
+ def test_wrapped_getfuncargnames_patching(self):
+ from _pytest.compat import getfuncargnames
+
+ def wrap(f):
+ def func():
+ pass
+ func.__wrapped__ = f
+ func.patchings = ["qwe"]
+ return func
+
+ @wrap
+ def f(x, y, z):
+ pass
+
+ values = getfuncargnames(f)
+ assert values == ("y", "z")
+
+ def test_unittest_mock(self, testdir):
+ pytest.importorskip("unittest.mock")
+ testdir.makepyfile("""
+ import unittest.mock
+ class T(unittest.TestCase):
+ @unittest.mock.patch("os.path.abspath")
+ def test_hello(self, abspath):
+ import os
+ os.path.abspath("hello")
+ abspath.assert_any_call("hello")
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_unittest_mock_and_fixture(self, testdir):
+ pytest.importorskip("unittest.mock")
+ testdir.makepyfile("""
+ import os.path
+ import unittest.mock
+ import pytest
+
+ @pytest.fixture
+ def inject_me():
+ pass
+
+ @unittest.mock.patch.object(os.path, "abspath",
+ new=unittest.mock.MagicMock)
+ def test_hello(inject_me):
+ import os
+ os.path.abspath("hello")
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_mock(self, testdir):
+ pytest.importorskip("mock", "1.0.1")
+ testdir.makepyfile("""
+ import os
+ import unittest
+ import mock
+
+ class T(unittest.TestCase):
+ @mock.patch("os.path.abspath")
+ def test_hello(self, abspath):
+ os.path.abspath("hello")
+ abspath.assert_any_call("hello")
+ def mock_basename(path):
+ return "mock_basename"
+ @mock.patch("os.path.abspath")
+ @mock.patch("os.path.normpath")
+ @mock.patch("os.path.basename", new=mock_basename)
+ def test_someting(normpath, abspath, tmpdir):
+ abspath.return_value = "this"
+ os.path.normpath(os.path.abspath("hello"))
+ normpath.assert_any_call("this")
+ assert os.path.basename("123") == "mock_basename"
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+ calls = reprec.getcalls("pytest_runtest_logreport")
+ funcnames = [call.report.location[2] for call in calls
+ if call.report.when == "call"]
+ assert funcnames == ["T.test_hello", "test_someting"]
+
+ def test_mock_sorting(self, testdir):
+ pytest.importorskip("mock", "1.0.1")
+ testdir.makepyfile("""
+ import os
+ import mock
+
+ @mock.patch("os.path.abspath")
+ def test_one(abspath):
+ pass
+ @mock.patch("os.path.abspath")
+ def test_two(abspath):
+ pass
+ @mock.patch("os.path.abspath")
+ def test_three(abspath):
+ pass
+ """)
+ reprec = testdir.inline_run()
+ calls = reprec.getreports("pytest_runtest_logreport")
+ calls = [x for x in calls if x.when == "call"]
+ names = [x.nodeid.split("::")[-1] for x in calls]
+ assert names == ["test_one", "test_two", "test_three"]
+
+ def test_mock_double_patch_issue473(self, testdir):
+ pytest.importorskip("mock", "1.0.1")
+ testdir.makepyfile("""
+ from mock import patch
+ from pytest import mark
+
+ @patch('os.getcwd')
+ @patch('os.path')
+ @mark.slow
+ class TestSimple(object):
+ def test_simple_thing(self, mock_path, mock_getcwd):
+ pass
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+
+class TestReRunTests(object):
+ def test_rerun(self, testdir):
+ testdir.makeconftest("""
+ from _pytest.runner import runtestprotocol
+ def pytest_runtest_protocol(item, nextitem):
+ runtestprotocol(item, log=False, nextitem=nextitem)
+ runtestprotocol(item, log=True, nextitem=nextitem)
+ """)
+ testdir.makepyfile("""
+ import pytest
+ count = 0
+ req = None
+ @pytest.fixture
+ def fix(request):
+ global count, req
+ assert request != req
+ req = request
+ print ("fix count %s" % count)
+ count += 1
+ def test_fix(fix):
+ pass
+ """)
+ result = testdir.runpytest("-s")
+ result.stdout.fnmatch_lines("""
+ *fix count 0*
+ *fix count 1*
+ """)
+ result.stdout.fnmatch_lines("""
+ *2 passed*
+ """)
+
+
+def test_pytestconfig_is_session_scoped():
+ from _pytest.fixtures import pytestconfig
+ assert pytestconfig._pytestfixturefunction.scope == "session"
+
+
+class TestNoselikeTestAttribute(object):
+ def test_module_with_global_test(self, testdir):
+ testdir.makepyfile("""
+ __test__ = False
+ def test_hello():
+ pass
+ """)
+ reprec = testdir.inline_run()
+ assert not reprec.getfailedcollections()
+ calls = reprec.getreports("pytest_runtest_logreport")
+ assert not calls
+
+ def test_class_and_method(self, testdir):
+ testdir.makepyfile("""
+ __test__ = True
+ def test_func():
+ pass
+ test_func.__test__ = False
+
+ class TestSome(object):
+ __test__ = False
+ def test_method(self):
+ pass
+ """)
+ reprec = testdir.inline_run()
+ assert not reprec.getfailedcollections()
+ calls = reprec.getreports("pytest_runtest_logreport")
+ assert not calls
+
+ def test_unittest_class(self, testdir):
+ testdir.makepyfile("""
+ import unittest
+ class TC(unittest.TestCase):
+ def test_1(self):
+ pass
+ class TC2(unittest.TestCase):
+ __test__ = False
+ def test_2(self):
+ pass
+ """)
+ reprec = testdir.inline_run()
+ assert not reprec.getfailedcollections()
+ call = reprec.getcalls("pytest_collection_modifyitems")[0]
+ assert len(call.items) == 1
+ assert call.items[0].cls.__name__ == "TC"
+
+ def test_class_with_nasty_getattr(self, testdir):
+ """Make sure we handle classes with a custom nasty __getattr__ right.
+
+ With a custom __getattr__ which e.g. returns a function (like with a
+ RPC wrapper), we shouldn't assume this meant "__test__ = True".
+ """
+ # https://github.com/pytest-dev/pytest/issues/1204
+ testdir.makepyfile("""
+ class MetaModel(type):
+
+ def __getattr__(cls, key):
+ return lambda: None
+
+
+ BaseModel = MetaModel('Model', (), {})
+
+
+ class Model(BaseModel):
+
+ __metaclass__ = MetaModel
+
+ def test_blah(self):
+ pass
+ """)
+ reprec = testdir.inline_run()
+ assert not reprec.getfailedcollections()
+ call = reprec.getcalls("pytest_collection_modifyitems")[0]
+ assert not call.items
+
+
+@pytest.mark.issue351
+class TestParameterize(object):
+
+ def test_idfn_marker(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ def idfn(param):
+ if param == 0:
+ return 'spam'
+ elif param == 1:
+ return 'ham'
+ else:
+ return None
+
+ @pytest.mark.parametrize('a,b', [(0, 2), (1, 2)], ids=idfn)
+ def test_params(a, b):
+ pass
+ """)
+ res = testdir.runpytest('--collect-only')
+ res.stdout.fnmatch_lines([
+ "*spam-2*",
+ "*ham-2*",
+ ])
+
+ def test_idfn_fixture(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ def idfn(param):
+ if param == 0:
+ return 'spam'
+ elif param == 1:
+ return 'ham'
+ else:
+ return None
+
+ @pytest.fixture(params=[0, 1], ids=idfn)
+ def a(request):
+ return request.param
+
+ @pytest.fixture(params=[1, 2], ids=idfn)
+ def b(request):
+ return request.param
+
+ def test_params(a, b):
+ pass
+ """)
+ res = testdir.runpytest('--collect-only')
+ res.stdout.fnmatch_lines([
+ "*spam-2*",
+ "*ham-2*",
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/metafunc.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/metafunc.py
new file mode 100644
index 00000000000..2ffb7bb5da2
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/metafunc.py
@@ -0,0 +1,1561 @@
+# -*- coding: utf-8 -*-
+import re
+import sys
+
+import _pytest._code
+import py
+import pytest
+from _pytest import python, fixtures
+
+import hypothesis
+from hypothesis import strategies
+
+PY3 = sys.version_info >= (3, 0)
+
+
+class TestMetafunc(object):
+ def Metafunc(self, func):
+ # the unit tests of this class check if things work correctly
+ # on the funcarg level, so we don't need a full blown
+ # initiliazation
+ class FixtureInfo(object):
+ name2fixturedefs = None
+
+ def __init__(self, names):
+ self.names_closure = names
+
+ names = fixtures.getfuncargnames(func)
+ fixtureinfo = FixtureInfo(names)
+ return python.Metafunc(func, fixtureinfo, None)
+
+ def test_no_funcargs(self, testdir):
+ def function():
+ pass
+ metafunc = self.Metafunc(function)
+ assert not metafunc.fixturenames
+ repr(metafunc._calls)
+
+ def test_function_basic(self):
+ def func(arg1, arg2="qwe"):
+ pass
+ metafunc = self.Metafunc(func)
+ assert len(metafunc.fixturenames) == 1
+ assert 'arg1' in metafunc.fixturenames
+ assert metafunc.function is func
+ assert metafunc.cls is None
+
+ def test_addcall_no_args(self):
+ def func(arg1):
+ pass
+ metafunc = self.Metafunc(func)
+ metafunc.addcall()
+ assert len(metafunc._calls) == 1
+ call = metafunc._calls[0]
+ assert call.id == "0"
+ assert not hasattr(call, 'param')
+
+ def test_addcall_id(self):
+ def func(arg1):
+ pass
+ metafunc = self.Metafunc(func)
+ pytest.raises(ValueError, "metafunc.addcall(id=None)")
+
+ metafunc.addcall(id=1)
+ pytest.raises(ValueError, "metafunc.addcall(id=1)")
+ pytest.raises(ValueError, "metafunc.addcall(id='1')")
+ metafunc.addcall(id=2)
+ assert len(metafunc._calls) == 2
+ assert metafunc._calls[0].id == "1"
+ assert metafunc._calls[1].id == "2"
+
+ def test_addcall_param(self):
+ def func(arg1):
+ pass
+ metafunc = self.Metafunc(func)
+
+ class obj(object):
+ pass
+
+ metafunc.addcall(param=obj)
+ metafunc.addcall(param=obj)
+ metafunc.addcall(param=1)
+ assert len(metafunc._calls) == 3
+ assert metafunc._calls[0].getparam("arg1") == obj
+ assert metafunc._calls[1].getparam("arg1") == obj
+ assert metafunc._calls[2].getparam("arg1") == 1
+
+ def test_addcall_funcargs(self):
+ def func(x):
+ pass
+
+ metafunc = self.Metafunc(func)
+
+ class obj(object):
+ pass
+
+ metafunc.addcall(funcargs={"x": 2})
+ metafunc.addcall(funcargs={"x": 3})
+ pytest.raises(pytest.fail.Exception, "metafunc.addcall({'xyz': 0})")
+ assert len(metafunc._calls) == 2
+ assert metafunc._calls[0].funcargs == {'x': 2}
+ assert metafunc._calls[1].funcargs == {'x': 3}
+ assert not hasattr(metafunc._calls[1], 'param')
+
+ def test_parametrize_error(self):
+ def func(x, y):
+ pass
+ metafunc = self.Metafunc(func)
+ metafunc.parametrize("x", [1, 2])
+ pytest.raises(ValueError, lambda: metafunc.parametrize("x", [5, 6]))
+ pytest.raises(ValueError, lambda: metafunc.parametrize("x", [5, 6]))
+ metafunc.parametrize("y", [1, 2])
+ pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5, 6]))
+ pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5, 6]))
+
+ def test_parametrize_bad_scope(self, testdir):
+ def func(x):
+ pass
+ metafunc = self.Metafunc(func)
+ try:
+ metafunc.parametrize("x", [1], scope='doggy')
+ except ValueError as ve:
+ assert "has an unsupported scope value 'doggy'" in str(ve)
+
+ def test_parametrize_and_id(self):
+ def func(x, y):
+ pass
+ metafunc = self.Metafunc(func)
+
+ metafunc.parametrize("x", [1, 2], ids=['basic', 'advanced'])
+ metafunc.parametrize("y", ["abc", "def"])
+ ids = [x.id for x in metafunc._calls]
+ assert ids == ["basic-abc", "basic-def", "advanced-abc", "advanced-def"]
+
+ def test_parametrize_and_id_unicode(self):
+ """Allow unicode strings for "ids" parameter in Python 2 (##1905)"""
+ def func(x):
+ pass
+ metafunc = self.Metafunc(func)
+ metafunc.parametrize("x", [1, 2], ids=[u'basic', u'advanced'])
+ ids = [x.id for x in metafunc._calls]
+ assert ids == [u"basic", u"advanced"]
+
+ def test_parametrize_with_wrong_number_of_ids(self, testdir):
+ def func(x, y):
+ pass
+ metafunc = self.Metafunc(func)
+
+ pytest.raises(ValueError, lambda:
+ metafunc.parametrize("x", [1, 2], ids=['basic']))
+
+ pytest.raises(ValueError, lambda:
+ metafunc.parametrize(("x", "y"), [("abc", "def"),
+ ("ghi", "jkl")], ids=["one"]))
+
+ @pytest.mark.issue510
+ def test_parametrize_empty_list(self):
+ def func(y):
+ pass
+ metafunc = self.Metafunc(func)
+ metafunc.parametrize("y", [])
+ assert 'skip' == metafunc._calls[0].marks[0].name
+
+ def test_parametrize_with_userobjects(self):
+ def func(x, y):
+ pass
+ metafunc = self.Metafunc(func)
+
+ class A(object):
+ pass
+
+ metafunc.parametrize("x", [A(), A()])
+ metafunc.parametrize("y", list("ab"))
+ assert metafunc._calls[0].id == "x0-a"
+ assert metafunc._calls[1].id == "x0-b"
+ assert metafunc._calls[2].id == "x1-a"
+ assert metafunc._calls[3].id == "x1-b"
+
+ @hypothesis.given(strategies.text() | strategies.binary())
+ def test_idval_hypothesis(self, value):
+ from _pytest.python import _idval
+ escaped = _idval(value, 'a', 6, None)
+ assert isinstance(escaped, str)
+ if PY3:
+ escaped.encode('ascii')
+ else:
+ escaped.decode('ascii')
+
+ def test_unicode_idval(self):
+ """This tests that Unicode strings outside the ASCII character set get
+ escaped, using byte escapes if they're in that range or unicode
+ escapes if they're not.
+
+ """
+ from _pytest.python import _idval
+ values = [
+ (
+ u'',
+ ''
+ ),
+ (
+ u'ascii',
+ 'ascii'
+ ),
+ (
+ u'ação',
+ 'a\\xe7\\xe3o'
+ ),
+ (
+ u'josé@blah.com',
+ 'jos\\xe9@blah.com'
+ ),
+ (
+ u'δοκ.ιμή@παράδειγμα.δοκιμή',
+ '\\u03b4\\u03bf\\u03ba.\\u03b9\\u03bc\\u03ae@\\u03c0\\u03b1\\u03c1\\u03ac\\u03b4\\u03b5\\u03b9\\u03b3'
+ '\\u03bc\\u03b1.\\u03b4\\u03bf\\u03ba\\u03b9\\u03bc\\u03ae'
+ ),
+ ]
+ for val, expected in values:
+ assert _idval(val, 'a', 6, None) == expected
+
+ def test_bytes_idval(self):
+ """unittest for the expected behavior to obtain ids for parametrized
+ bytes values:
+ - python2: non-ascii strings are considered bytes and formatted using
+ "binary escape", where any byte < 127 is escaped into its hex form.
+ - python3: bytes objects are always escaped using "binary escape".
+ """
+ from _pytest.python import _idval
+ values = [
+ (b'', ''),
+ (b'\xc3\xb4\xff\xe4', '\\xc3\\xb4\\xff\\xe4'),
+ (b'ascii', 'ascii'),
+ (u'αρά'.encode('utf-8'), '\\xce\\xb1\\xcf\\x81\\xce\\xac'),
+ ]
+ for val, expected in values:
+ assert _idval(val, 'a', 6, None) == expected
+
+ @pytest.mark.issue250
+ def test_idmaker_autoname(self):
+ from _pytest.python import idmaker
+ result = idmaker(("a", "b"), [pytest.param("string", 1.0),
+ pytest.param("st-ring", 2.0)])
+ assert result == ["string-1.0", "st-ring-2.0"]
+
+ result = idmaker(("a", "b"), [pytest.param(object(), 1.0),
+ pytest.param(object(), object())])
+ assert result == ["a0-1.0", "a1-b1"]
+ # unicode mixing, issue250
+ result = idmaker(
+ (py.builtin._totext("a"), "b"),
+ [pytest.param({}, b'\xc3\xb4')])
+ assert result == ['a0-\\xc3\\xb4']
+
+ def test_idmaker_with_bytes_regex(self):
+ from _pytest.python import idmaker
+ result = idmaker(("a"), [pytest.param(re.compile(b'foo'), 1.0)])
+ assert result == ["foo"]
+
+ def test_idmaker_native_strings(self):
+ from _pytest.python import idmaker
+ totext = py.builtin._totext
+ result = idmaker(("a", "b"), [
+ pytest.param(1.0, -1.1),
+ pytest.param(2, -202),
+ pytest.param("three", "three hundred"),
+ pytest.param(True, False),
+ pytest.param(None, None),
+ pytest.param(re.compile('foo'), re.compile('bar')),
+ pytest.param(str, int),
+ pytest.param(list("six"), [66, 66]),
+ pytest.param(set([7]), set("seven")),
+ pytest.param(tuple("eight"), (8, -8, 8)),
+ pytest.param(b'\xc3\xb4', b"name"),
+ pytest.param(b'\xc3\xb4', totext("other")),
+ ])
+ assert result == ["1.0--1.1",
+ "2--202",
+ "three-three hundred",
+ "True-False",
+ "None-None",
+ "foo-bar",
+ "str-int",
+ "a7-b7",
+ "a8-b8",
+ "a9-b9",
+ "\\xc3\\xb4-name",
+ "\\xc3\\xb4-other",
+ ]
+
+ def test_idmaker_enum(self):
+ from _pytest.python import idmaker
+ enum = pytest.importorskip("enum")
+ e = enum.Enum("Foo", "one, two")
+ result = idmaker(("a", "b"), [pytest.param(e.one, e.two)])
+ assert result == ["Foo.one-Foo.two"]
+
+ @pytest.mark.issue351
+ def test_idmaker_idfn(self):
+ from _pytest.python import idmaker
+
+ def ids(val):
+ if isinstance(val, Exception):
+ return repr(val)
+
+ result = idmaker(("a", "b"), [
+ pytest.param(10.0, IndexError()),
+ pytest.param(20, KeyError()),
+ pytest.param("three", [1, 2, 3]),
+ ], idfn=ids)
+ assert result == ["10.0-IndexError()",
+ "20-KeyError()",
+ "three-b2",
+ ]
+
+ @pytest.mark.issue351
+ def test_idmaker_idfn_unique_names(self):
+ from _pytest.python import idmaker
+
+ def ids(val):
+ return 'a'
+
+ result = idmaker(("a", "b"), [pytest.param(10.0, IndexError()),
+ pytest.param(20, KeyError()),
+ pytest.param("three", [1, 2, 3]),
+ ], idfn=ids)
+ assert result == ["a-a0",
+ "a-a1",
+ "a-a2",
+ ]
+
+ @pytest.mark.issue351
+ def test_idmaker_idfn_exception(self):
+ from _pytest.python import idmaker
+ from _pytest.recwarn import WarningsRecorder
+
+ class BadIdsException(Exception):
+ pass
+
+ def ids(val):
+ raise BadIdsException("ids raised")
+
+ rec = WarningsRecorder()
+ with rec:
+ idmaker(("a", "b"), [
+ pytest.param(10.0, IndexError()),
+ pytest.param(20, KeyError()),
+ pytest.param("three", [1, 2, 3]),
+ ], idfn=ids)
+
+ assert [str(i.message) for i in rec.list] == [
+ "Raised while trying to determine id of parameter a at position 0."
+ "\nUpdate your code as this will raise an error in pytest-4.0.",
+ "Raised while trying to determine id of parameter b at position 0."
+ "\nUpdate your code as this will raise an error in pytest-4.0.",
+ "Raised while trying to determine id of parameter a at position 1."
+ "\nUpdate your code as this will raise an error in pytest-4.0.",
+ "Raised while trying to determine id of parameter b at position 1."
+ "\nUpdate your code as this will raise an error in pytest-4.0.",
+ "Raised while trying to determine id of parameter a at position 2."
+ "\nUpdate your code as this will raise an error in pytest-4.0.",
+ "Raised while trying to determine id of parameter b at position 2."
+ "\nUpdate your code as this will raise an error in pytest-4.0.",
+ ]
+
+ def test_parametrize_ids_exception(self, testdir):
+ """
+ :param testdir: the instance of Testdir class, a temporary
+ test directory.
+ """
+ testdir.makepyfile("""
+ import pytest
+
+ def ids(arg):
+ raise Exception("bad ids")
+
+ @pytest.mark.parametrize("arg", ["a", "b"], ids=ids)
+ def test_foo(arg):
+ pass
+ """)
+ with pytest.warns(DeprecationWarning):
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "<Module 'test_parametrize_ids_exception.py'>",
+ " <Function 'test_foo[a]'>",
+ " <Function 'test_foo[b]'>",
+ ])
+
+ def test_idmaker_with_ids(self):
+ from _pytest.python import idmaker
+ result = idmaker(("a", "b"), [pytest.param(1, 2),
+ pytest.param(3, 4)],
+ ids=["a", None])
+ assert result == ["a", "3-4"]
+
+ def test_idmaker_with_paramset_id(self):
+ from _pytest.python import idmaker
+ result = idmaker(("a", "b"), [pytest.param(1, 2, id="me"),
+ pytest.param(3, 4, id="you")],
+ ids=["a", None])
+ assert result == ["me", "you"]
+
+ def test_idmaker_with_ids_unique_names(self):
+ from _pytest.python import idmaker
+ result = idmaker(("a"), map(pytest.param, [1, 2, 3, 4, 5]),
+ ids=["a", "a", "b", "c", "b"])
+ assert result == ["a0", "a1", "b0", "c", "b1"]
+
+ def test_addcall_and_parametrize(self):
+ def func(x, y):
+ pass
+ metafunc = self.Metafunc(func)
+ metafunc.addcall({'x': 1})
+ metafunc.parametrize('y', [2, 3])
+ assert len(metafunc._calls) == 2
+ assert metafunc._calls[0].funcargs == {'x': 1, 'y': 2}
+ assert metafunc._calls[1].funcargs == {'x': 1, 'y': 3}
+ assert metafunc._calls[0].id == "0-2"
+ assert metafunc._calls[1].id == "0-3"
+
+ @pytest.mark.issue714
+ def test_parametrize_indirect(self):
+ def func(x, y):
+ pass
+ metafunc = self.Metafunc(func)
+ metafunc.parametrize('x', [1], indirect=True)
+ metafunc.parametrize('y', [2, 3], indirect=True)
+ assert len(metafunc._calls) == 2
+ assert metafunc._calls[0].funcargs == {}
+ assert metafunc._calls[1].funcargs == {}
+ assert metafunc._calls[0].params == dict(x=1, y=2)
+ assert metafunc._calls[1].params == dict(x=1, y=3)
+
+ @pytest.mark.issue714
+ def test_parametrize_indirect_list(self):
+ def func(x, y):
+ pass
+ metafunc = self.Metafunc(func)
+ metafunc.parametrize('x, y', [('a', 'b')], indirect=['x'])
+ assert metafunc._calls[0].funcargs == dict(y='b')
+ assert metafunc._calls[0].params == dict(x='a')
+
+ @pytest.mark.issue714
+ def test_parametrize_indirect_list_all(self):
+ def func(x, y):
+ pass
+ metafunc = self.Metafunc(func)
+ metafunc.parametrize('x, y', [('a', 'b')], indirect=['x', 'y'])
+ assert metafunc._calls[0].funcargs == {}
+ assert metafunc._calls[0].params == dict(x='a', y='b')
+
+ @pytest.mark.issue714
+ def test_parametrize_indirect_list_empty(self):
+ def func(x, y):
+ pass
+ metafunc = self.Metafunc(func)
+ metafunc.parametrize('x, y', [('a', 'b')], indirect=[])
+ assert metafunc._calls[0].funcargs == dict(x='a', y='b')
+ assert metafunc._calls[0].params == {}
+
+ @pytest.mark.issue714
+ def test_parametrize_indirect_list_functional(self, testdir):
+ """
+ Test parametrization with 'indirect' parameter applied on
+ particular arguments. As y is is direct, its value should
+ be used directly rather than being passed to the fixture
+ y.
+
+ :param testdir: the instance of Testdir class, a temporary
+ test directory.
+ """
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope='function')
+ def x(request):
+ return request.param * 3
+ @pytest.fixture(scope='function')
+ def y(request):
+ return request.param * 2
+ @pytest.mark.parametrize('x, y', [('a', 'b')], indirect=['x'])
+ def test_simple(x,y):
+ assert len(x) == 3
+ assert len(y) == 1
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines([
+ "*test_simple*a-b*",
+ "*1 passed*",
+ ])
+
+ @pytest.mark.issue714
+ def test_parametrize_indirect_list_error(self, testdir):
+ def func(x, y):
+ pass
+ metafunc = self.Metafunc(func)
+ with pytest.raises(ValueError):
+ metafunc.parametrize('x, y', [('a', 'b')], indirect=['x', 'z'])
+
+ @pytest.mark.issue714
+ def test_parametrize_uses_no_fixture_error_indirect_false(self, testdir):
+ """The 'uses no fixture' error tells the user at collection time
+ that the parametrize data they've set up doesn't correspond to the
+ fixtures in their test function, rather than silently ignoring this
+ and letting the test potentially pass.
+ """
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.parametrize('x, y', [('a', 'b')], indirect=False)
+ def test_simple(x):
+ assert len(x) == 3
+ """)
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*uses no argument 'y'*",
+ ])
+
+ @pytest.mark.issue714
+ def test_parametrize_uses_no_fixture_error_indirect_true(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope='function')
+ def x(request):
+ return request.param * 3
+ @pytest.fixture(scope='function')
+ def y(request):
+ return request.param * 2
+
+ @pytest.mark.parametrize('x, y', [('a', 'b')], indirect=True)
+ def test_simple(x):
+ assert len(x) == 3
+ """)
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*uses no fixture 'y'*",
+ ])
+
+ @pytest.mark.issue714
+ def test_parametrize_indirect_uses_no_fixture_error_indirect_string(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope='function')
+ def x(request):
+ return request.param * 3
+
+ @pytest.mark.parametrize('x, y', [('a', 'b')], indirect='y')
+ def test_simple(x):
+ assert len(x) == 3
+ """)
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*uses no fixture 'y'*",
+ ])
+
+ @pytest.mark.issue714
+ def test_parametrize_indirect_uses_no_fixture_error_indirect_list(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope='function')
+ def x(request):
+ return request.param * 3
+
+ @pytest.mark.parametrize('x, y', [('a', 'b')], indirect=['y'])
+ def test_simple(x):
+ assert len(x) == 3
+ """)
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*uses no fixture 'y'*",
+ ])
+
+ @pytest.mark.issue714
+ def test_parametrize_argument_not_in_indirect_list(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope='function')
+ def x(request):
+ return request.param * 3
+
+ @pytest.mark.parametrize('x, y', [('a', 'b')], indirect=['x'])
+ def test_simple(x):
+ assert len(x) == 3
+ """)
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*uses no argument 'y'*",
+ ])
+
+ def test_addcalls_and_parametrize_indirect(self):
+ def func(x, y):
+ pass
+ metafunc = self.Metafunc(func)
+ metafunc.addcall(param="123")
+ metafunc.parametrize('x', [1], indirect=True)
+ metafunc.parametrize('y', [2, 3], indirect=True)
+ assert len(metafunc._calls) == 2
+ assert metafunc._calls[0].funcargs == {}
+ assert metafunc._calls[1].funcargs == {}
+ assert metafunc._calls[0].params == dict(x=1, y=2)
+ assert metafunc._calls[1].params == dict(x=1, y=3)
+
+ def test_parametrize_functional(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def pytest_generate_tests(metafunc):
+ metafunc.parametrize('x', [1,2], indirect=True)
+ metafunc.parametrize('y', [2])
+ @pytest.fixture
+ def x(request):
+ return request.param * 10
+
+ def test_simple(x,y):
+ assert x in (10,20)
+ assert y == 2
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines([
+ "*test_simple*1-2*",
+ "*test_simple*2-2*",
+ "*2 passed*",
+ ])
+
+ def test_parametrize_onearg(self):
+ metafunc = self.Metafunc(lambda x: None)
+ metafunc.parametrize("x", [1, 2])
+ assert len(metafunc._calls) == 2
+ assert metafunc._calls[0].funcargs == dict(x=1)
+ assert metafunc._calls[0].id == "1"
+ assert metafunc._calls[1].funcargs == dict(x=2)
+ assert metafunc._calls[1].id == "2"
+
+ def test_parametrize_onearg_indirect(self):
+ metafunc = self.Metafunc(lambda x: None)
+ metafunc.parametrize("x", [1, 2], indirect=True)
+ assert metafunc._calls[0].params == dict(x=1)
+ assert metafunc._calls[0].id == "1"
+ assert metafunc._calls[1].params == dict(x=2)
+ assert metafunc._calls[1].id == "2"
+
+ def test_parametrize_twoargs(self):
+ metafunc = self.Metafunc(lambda x, y: None)
+ metafunc.parametrize(("x", "y"), [(1, 2), (3, 4)])
+ assert len(metafunc._calls) == 2
+ assert metafunc._calls[0].funcargs == dict(x=1, y=2)
+ assert metafunc._calls[0].id == "1-2"
+ assert metafunc._calls[1].funcargs == dict(x=3, y=4)
+ assert metafunc._calls[1].id == "3-4"
+
+ def test_parametrize_multiple_times(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ pytestmark = pytest.mark.parametrize("x", [1,2])
+ def test_func(x):
+ assert 0, x
+ class TestClass(object):
+ pytestmark = pytest.mark.parametrize("y", [3,4])
+ def test_meth(self, x, y):
+ assert 0, x
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.assert_outcomes(failed=6)
+
+ def test_parametrize_CSV(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize("x, y,", [(1,2), (2,3)])
+ def test_func(x, y):
+ assert x+1 == y
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+ def test_parametrize_class_scenarios(self, testdir):
+ testdir.makepyfile("""
+ # same as doc/en/example/parametrize scenario example
+ def pytest_generate_tests(metafunc):
+ idlist = []
+ argvalues = []
+ for scenario in metafunc.cls.scenarios:
+ idlist.append(scenario[0])
+ items = scenario[1].items()
+ argnames = [x[0] for x in items]
+ argvalues.append(([x[1] for x in items]))
+ metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class")
+
+ class Test(object):
+ scenarios = [['1', {'arg': {1: 2}, "arg2": "value2"}],
+ ['2', {'arg':'value2', "arg2": "value2"}]]
+
+ def test_1(self, arg, arg2):
+ pass
+
+ def test_2(self, arg2, arg):
+ pass
+
+ def test_3(self, arg, arg2):
+ pass
+ """)
+ result = testdir.runpytest("-v")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines("""
+ *test_1*1*
+ *test_2*1*
+ *test_3*1*
+ *test_1*2*
+ *test_2*2*
+ *test_3*2*
+ *6 passed*
+ """)
+
+ def test_format_args(self):
+ def function1():
+ pass
+ assert fixtures._format_args(function1) == '()'
+
+ def function2(arg1):
+ pass
+ assert fixtures._format_args(function2) == "(arg1)"
+
+ def function3(arg1, arg2="qwe"):
+ pass
+ assert fixtures._format_args(function3) == "(arg1, arg2='qwe')"
+
+ def function4(arg1, *args, **kwargs):
+ pass
+ assert fixtures._format_args(function4) == "(arg1, *args, **kwargs)"
+
+
+class TestMetafuncFunctional(object):
+ def test_attributes(self, testdir):
+ p = testdir.makepyfile("""
+ # assumes that generate/provide runs in the same process
+ import py, pytest
+ def pytest_generate_tests(metafunc):
+ metafunc.addcall(param=metafunc)
+
+ @pytest.fixture
+ def metafunc(request):
+ assert request._pyfuncitem._genid == "0"
+ return request.param
+
+ def test_function(metafunc, pytestconfig):
+ assert metafunc.config == pytestconfig
+ assert metafunc.module.__name__ == __name__
+ assert metafunc.function == test_function
+ assert metafunc.cls is None
+
+ class TestClass(object):
+ def test_method(self, metafunc, pytestconfig):
+ assert metafunc.config == pytestconfig
+ assert metafunc.module.__name__ == __name__
+ if py.std.sys.version_info > (3, 0):
+ unbound = TestClass.test_method
+ else:
+ unbound = TestClass.test_method.im_func
+ # XXX actually have an unbound test function here?
+ assert metafunc.function == unbound
+ assert metafunc.cls == TestClass
+ """)
+ result = testdir.runpytest(p, "-v")
+ result.assert_outcomes(passed=2)
+
+ def test_addcall_with_two_funcargs_generators(self, testdir):
+ testdir.makeconftest("""
+ def pytest_generate_tests(metafunc):
+ assert "arg1" in metafunc.fixturenames
+ metafunc.addcall(funcargs=dict(arg1=1, arg2=2))
+ """)
+ p = testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ metafunc.addcall(funcargs=dict(arg1=1, arg2=1))
+
+ class TestClass(object):
+ def test_myfunc(self, arg1, arg2):
+ assert arg1 == arg2
+ """)
+ result = testdir.runpytest("-v", p)
+ result.stdout.fnmatch_lines([
+ "*test_myfunc*0*PASS*",
+ "*test_myfunc*1*FAIL*",
+ "*1 failed, 1 passed*"
+ ])
+
+ def test_two_functions(self, testdir):
+ p = testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ metafunc.addcall(param=10)
+ metafunc.addcall(param=20)
+
+ import pytest
+ @pytest.fixture
+ def arg1(request):
+ return request.param
+
+ def test_func1(arg1):
+ assert arg1 == 10
+ def test_func2(arg1):
+ assert arg1 in (10, 20)
+ """)
+ result = testdir.runpytest("-v", p)
+ result.stdout.fnmatch_lines([
+ "*test_func1*0*PASS*",
+ "*test_func1*1*FAIL*",
+ "*test_func2*PASS*",
+ "*1 failed, 3 passed*"
+ ])
+
+ def test_noself_in_method(self, testdir):
+ p = testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ assert 'xyz' not in metafunc.fixturenames
+
+ class TestHello(object):
+ def test_hello(xyz):
+ pass
+ """)
+ result = testdir.runpytest(p)
+ result.assert_outcomes(passed=1)
+
+ def test_generate_plugin_and_module(self, testdir):
+ testdir.makeconftest("""
+ def pytest_generate_tests(metafunc):
+ assert "arg1" in metafunc.fixturenames
+ metafunc.addcall(id="world", param=(2,100))
+ """)
+ p = testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ metafunc.addcall(param=(1,1), id="hello")
+
+ import pytest
+ @pytest.fixture
+ def arg1(request):
+ return request.param[0]
+ @pytest.fixture
+ def arg2(request):
+ return request.param[1]
+
+ class TestClass(object):
+ def test_myfunc(self, arg1, arg2):
+ assert arg1 == arg2
+ """)
+ result = testdir.runpytest("-v", p)
+ result.stdout.fnmatch_lines([
+ "*test_myfunc*hello*PASS*",
+ "*test_myfunc*world*FAIL*",
+ "*1 failed, 1 passed*"
+ ])
+
+ def test_generate_tests_in_class(self, testdir):
+ p = testdir.makepyfile("""
+ class TestClass(object):
+ def pytest_generate_tests(self, metafunc):
+ metafunc.addcall(funcargs={'hello': 'world'}, id="hello")
+
+ def test_myfunc(self, hello):
+ assert hello == "world"
+ """)
+ result = testdir.runpytest("-v", p)
+ result.stdout.fnmatch_lines([
+ "*test_myfunc*hello*PASS*",
+ "*1 passed*"
+ ])
+
+ def test_two_functions_not_same_instance(self, testdir):
+ p = testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ metafunc.addcall({'arg1': 10})
+ metafunc.addcall({'arg1': 20})
+
+ class TestClass(object):
+ def test_func(self, arg1):
+ assert not hasattr(self, 'x')
+ self.x = 1
+ """)
+ result = testdir.runpytest("-v", p)
+ result.stdout.fnmatch_lines([
+ "*test_func*0*PASS*",
+ "*test_func*1*PASS*",
+ "*2 pass*",
+ ])
+
+ def test_issue28_setup_method_in_generate_tests(self, testdir):
+ p = testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ metafunc.addcall({'arg1': 1})
+
+ class TestClass(object):
+ def test_method(self, arg1):
+ assert arg1 == self.val
+ def setup_method(self, func):
+ self.val = 1
+ """)
+ result = testdir.runpytest(p)
+ result.assert_outcomes(passed=1)
+
+ def test_parametrize_functional2(self, testdir):
+ testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ metafunc.parametrize("arg1", [1,2])
+ metafunc.parametrize("arg2", [4,5])
+ def test_hello(arg1, arg2):
+ assert 0, (arg1, arg2)
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*(1, 4)*",
+ "*(1, 5)*",
+ "*(2, 4)*",
+ "*(2, 5)*",
+ "*4 failed*",
+ ])
+
+ def test_parametrize_and_inner_getfixturevalue(self, testdir):
+ p = testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ metafunc.parametrize("arg1", [1], indirect=True)
+ metafunc.parametrize("arg2", [10], indirect=True)
+
+ import pytest
+ @pytest.fixture
+ def arg1(request):
+ x = request.getfixturevalue("arg2")
+ return x + request.param
+
+ @pytest.fixture
+ def arg2(request):
+ return request.param
+
+ def test_func1(arg1, arg2):
+ assert arg1 == 11
+ """)
+ result = testdir.runpytest("-v", p)
+ result.stdout.fnmatch_lines([
+ "*test_func1*1*PASS*",
+ "*1 passed*"
+ ])
+
+ def test_parametrize_on_setup_arg(self, testdir):
+ p = testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ assert "arg1" in metafunc.fixturenames
+ metafunc.parametrize("arg1", [1], indirect=True)
+
+ import pytest
+ @pytest.fixture
+ def arg1(request):
+ return request.param
+
+ @pytest.fixture
+ def arg2(request, arg1):
+ return 10 * arg1
+
+ def test_func(arg2):
+ assert arg2 == 10
+ """)
+ result = testdir.runpytest("-v", p)
+ result.stdout.fnmatch_lines([
+ "*test_func*1*PASS*",
+ "*1 passed*"
+ ])
+
+ def test_parametrize_with_ids(self, testdir):
+ testdir.makeini("""
+ [pytest]
+ console_output_style=classic
+ """)
+ testdir.makepyfile("""
+ import pytest
+ def pytest_generate_tests(metafunc):
+ metafunc.parametrize(("a", "b"), [(1,1), (1,2)],
+ ids=["basic", "advanced"])
+
+ def test_function(a, b):
+ assert a == b
+ """)
+ result = testdir.runpytest("-v")
+ assert result.ret == 1
+ result.stdout.fnmatch_lines_random([
+ "*test_function*basic*PASSED",
+ "*test_function*advanced*FAILED",
+ ])
+
+ def test_parametrize_without_ids(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def pytest_generate_tests(metafunc):
+ metafunc.parametrize(("a", "b"),
+ [(1,object()), (1.3,object())])
+
+ def test_function(a, b):
+ assert 1
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines("""
+ *test_function*1-b0*
+ *test_function*1.3-b1*
+ """)
+
+ def test_parametrize_with_None_in_ids(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def pytest_generate_tests(metafunc):
+ metafunc.parametrize(("a", "b"), [(1,1), (1,1), (1,2)],
+ ids=["basic", None, "advanced"])
+
+ def test_function(a, b):
+ assert a == b
+ """)
+ result = testdir.runpytest("-v")
+ assert result.ret == 1
+ result.stdout.fnmatch_lines_random([
+ "*test_function*basic*PASSED*",
+ "*test_function*1-1*PASSED*",
+ "*test_function*advanced*FAILED*",
+ ])
+
+ def test_fixture_parametrized_empty_ids(self, testdir):
+ """Fixtures parametrized with empty ids cause an internal error (#1849)."""
+ testdir.makepyfile('''
+ import pytest
+
+ @pytest.fixture(scope="module", ids=[], params=[])
+ def temp(request):
+ return request.param
+
+ def test_temp(temp):
+ pass
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['* 1 skipped *'])
+
+ def test_parametrized_empty_ids(self, testdir):
+ """Tests parametrized with empty ids cause an internal error (#1849)."""
+ testdir.makepyfile('''
+ import pytest
+
+ @pytest.mark.parametrize('temp', [], ids=list())
+ def test_temp(temp):
+ pass
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['* 1 skipped *'])
+
+ def test_parametrized_ids_invalid_type(self, testdir):
+ """Tests parametrized with ids as non-strings (#1857)."""
+ testdir.makepyfile('''
+ import pytest
+
+ @pytest.mark.parametrize("x, expected", [(10, 20), (40, 80)], ids=(None, 2))
+ def test_ids_numbers(x,expected):
+ assert x * 2 == expected
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['*ids must be list of strings, found: 2 (type: int)*'])
+
+ def test_parametrize_with_identical_ids_get_unique_names(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def pytest_generate_tests(metafunc):
+ metafunc.parametrize(("a", "b"), [(1,1), (1,2)],
+ ids=["a", "a"])
+
+ def test_function(a, b):
+ assert a == b
+ """)
+ result = testdir.runpytest("-v")
+ assert result.ret == 1
+ result.stdout.fnmatch_lines_random([
+ "*test_function*a0*PASSED*",
+ "*test_function*a1*FAILED*"
+ ])
+
+ @pytest.mark.parametrize(("scope", "length"),
+ [("module", 2), ("function", 4)])
+ def test_parametrize_scope_overrides(self, testdir, scope, length):
+ testdir.makepyfile("""
+ import pytest
+ values = []
+ def pytest_generate_tests(metafunc):
+ if "arg" in metafunc.funcargnames:
+ metafunc.parametrize("arg", [1,2], indirect=True,
+ scope=%r)
+ @pytest.fixture
+ def arg(request):
+ values.append(request.param)
+ return request.param
+ def test_hello(arg):
+ assert arg in (1,2)
+ def test_world(arg):
+ assert arg in (1,2)
+ def test_checklength():
+ assert len(values) == %d
+ """ % (scope, length))
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=5)
+
+ def test_parametrize_issue323(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(scope='module', params=range(966))
+ def foo(request):
+ return request.param
+
+ def test_it(foo):
+ pass
+ def test_it2(foo):
+ pass
+ """)
+ reprec = testdir.inline_run("--collect-only")
+ assert not reprec.getcalls("pytest_internalerror")
+
+ def test_usefixtures_seen_in_generate_tests(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def pytest_generate_tests(metafunc):
+ assert "abc" in metafunc.fixturenames
+ metafunc.parametrize("abc", [1])
+
+ @pytest.mark.usefixtures("abc")
+ def test_function():
+ pass
+ """)
+ reprec = testdir.runpytest()
+ reprec.assert_outcomes(passed=1)
+
+ def test_generate_tests_only_done_in_subdir(self, testdir):
+ sub1 = testdir.mkpydir("sub1")
+ sub2 = testdir.mkpydir("sub2")
+ sub1.join("conftest.py").write(_pytest._code.Source("""
+ def pytest_generate_tests(metafunc):
+ assert metafunc.function.__name__ == "test_1"
+ """))
+ sub2.join("conftest.py").write(_pytest._code.Source("""
+ def pytest_generate_tests(metafunc):
+ assert metafunc.function.__name__ == "test_2"
+ """))
+ sub1.join("test_in_sub1.py").write("def test_1(): pass")
+ sub2.join("test_in_sub2.py").write("def test_2(): pass")
+ result = testdir.runpytest("--keep-duplicates", "-v", "-s", sub1, sub2, sub1)
+ result.assert_outcomes(passed=3)
+
+ def test_generate_same_function_names_issue403(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ def make_tests():
+ @pytest.mark.parametrize("x", range(2))
+ def test_foo(x):
+ pass
+ return test_foo
+
+ test_x = make_tests()
+ test_y = make_tests()
+ """)
+ reprec = testdir.runpytest()
+ reprec.assert_outcomes(passed=4)
+
+ @pytest.mark.issue463
+ @pytest.mark.parametrize('attr', ['parametrise', 'parameterize',
+ 'parameterise'])
+ def test_parametrize_misspelling(self, testdir, attr):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.{0}("x", range(2))
+ def test_foo(x):
+ pass
+ """.format(attr))
+ reprec = testdir.inline_run('--collectonly')
+ failures = reprec.getfailures()
+ assert len(failures) == 1
+ expectederror = "MarkerError: test_foo has '{0}', spelling should be 'parametrize'".format(attr)
+ assert expectederror in failures[0].longrepr.reprcrash.message
+
+
+class TestMetafuncFunctionalAuto(object):
+ """
+ Tests related to automatically find out the correct scope for parametrized tests (#1832).
+ """
+
+ def test_parametrize_auto_scope(self, testdir):
+ testdir.makepyfile('''
+ import pytest
+
+ @pytest.fixture(scope='session', autouse=True)
+ def fixture():
+ return 1
+
+ @pytest.mark.parametrize('animal', ["dog", "cat"])
+ def test_1(animal):
+ assert animal in ('dog', 'cat')
+
+ @pytest.mark.parametrize('animal', ['fish'])
+ def test_2(animal):
+ assert animal == 'fish'
+
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['* 3 passed *'])
+
+ def test_parametrize_auto_scope_indirect(self, testdir):
+ testdir.makepyfile('''
+ import pytest
+
+ @pytest.fixture(scope='session')
+ def echo(request):
+ return request.param
+
+ @pytest.mark.parametrize('animal, echo', [("dog", 1), ("cat", 2)], indirect=['echo'])
+ def test_1(animal, echo):
+ assert animal in ('dog', 'cat')
+ assert echo in (1, 2, 3)
+
+ @pytest.mark.parametrize('animal, echo', [('fish', 3)], indirect=['echo'])
+ def test_2(animal, echo):
+ assert animal == 'fish'
+ assert echo in (1, 2, 3)
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['* 3 passed *'])
+
+ def test_parametrize_auto_scope_override_fixture(self, testdir):
+ testdir.makepyfile('''
+ import pytest
+
+ @pytest.fixture(scope='session', autouse=True)
+ def animal():
+ return 'fox'
+
+ @pytest.mark.parametrize('animal', ["dog", "cat"])
+ def test_1(animal):
+ assert animal in ('dog', 'cat')
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['* 2 passed *'])
+
+ def test_parametrize_all_indirects(self, testdir):
+ testdir.makepyfile('''
+ import pytest
+
+ @pytest.fixture()
+ def animal(request):
+ return request.param
+
+ @pytest.fixture(scope='session')
+ def echo(request):
+ return request.param
+
+ @pytest.mark.parametrize('animal, echo', [("dog", 1), ("cat", 2)], indirect=True)
+ def test_1(animal, echo):
+ assert animal in ('dog', 'cat')
+ assert echo in (1, 2, 3)
+
+ @pytest.mark.parametrize('animal, echo', [("fish", 3)], indirect=True)
+ def test_2(animal, echo):
+ assert animal == 'fish'
+ assert echo in (1, 2, 3)
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['* 3 passed *'])
+
+ def test_parametrize_issue634(self, testdir):
+ testdir.makepyfile('''
+ import pytest
+
+ @pytest.fixture(scope='module')
+ def foo(request):
+ print('preparing foo-%d' % request.param)
+ return 'foo-%d' % request.param
+
+ def test_one(foo):
+ pass
+
+ def test_two(foo):
+ pass
+
+ test_two.test_with = (2, 3)
+
+ def pytest_generate_tests(metafunc):
+ params = (1, 2, 3, 4)
+ if not 'foo' in metafunc.fixturenames:
+ return
+
+ test_with = getattr(metafunc.function, 'test_with', None)
+ if test_with:
+ params = test_with
+ metafunc.parametrize('foo', params, indirect=True)
+ ''')
+ result = testdir.runpytest("-s")
+ output = result.stdout.str()
+ assert output.count('preparing foo-2') == 1
+ assert output.count('preparing foo-3') == 1
+
+
+@pytest.mark.filterwarnings('ignore:Applying marks directly to parameters')
+@pytest.mark.issue308
+class TestMarkersWithParametrization(object):
+
+ def test_simple_mark(self, testdir):
+ s = """
+ import pytest
+
+ @pytest.mark.foo
+ @pytest.mark.parametrize(("n", "expected"), [
+ (1, 2),
+ pytest.mark.bar((1, 3)),
+ (2, 3),
+ ])
+ def test_increment(n, expected):
+ assert n + 1 == expected
+ """
+ items = testdir.getitems(s)
+ assert len(items) == 3
+ for item in items:
+ assert 'foo' in item.keywords
+ assert 'bar' not in items[0].keywords
+ assert 'bar' in items[1].keywords
+ assert 'bar' not in items[2].keywords
+
+ def test_select_based_on_mark(self, testdir):
+ s = """
+ import pytest
+
+ @pytest.mark.parametrize(("n", "expected"), [
+ (1, 2),
+ pytest.mark.foo((2, 3)),
+ (3, 4),
+ ])
+ def test_increment(n, expected):
+ assert n + 1 == expected
+ """
+ testdir.makepyfile(s)
+ rec = testdir.inline_run("-m", 'foo')
+ passed, skipped, fail = rec.listoutcomes()
+ assert len(passed) == 1
+ assert len(skipped) == 0
+ assert len(fail) == 0
+
+ @pytest.mark.xfail(reason="is this important to support??")
+ def test_nested_marks(self, testdir):
+ s = """
+ import pytest
+ mastermark = pytest.mark.foo(pytest.mark.bar)
+
+ @pytest.mark.parametrize(("n", "expected"), [
+ (1, 2),
+ mastermark((1, 3)),
+ (2, 3),
+ ])
+ def test_increment(n, expected):
+ assert n + 1 == expected
+ """
+ items = testdir.getitems(s)
+ assert len(items) == 3
+ for mark in ['foo', 'bar']:
+ assert mark not in items[0].keywords
+ assert mark in items[1].keywords
+ assert mark not in items[2].keywords
+
+ def test_simple_xfail(self, testdir):
+ s = """
+ import pytest
+
+ @pytest.mark.parametrize(("n", "expected"), [
+ (1, 2),
+ pytest.mark.xfail((1, 3)),
+ (2, 3),
+ ])
+ def test_increment(n, expected):
+ assert n + 1 == expected
+ """
+ testdir.makepyfile(s)
+ reprec = testdir.inline_run()
+ # xfail is skip??
+ reprec.assertoutcome(passed=2, skipped=1)
+
+ def test_simple_xfail_single_argname(self, testdir):
+ s = """
+ import pytest
+
+ @pytest.mark.parametrize("n", [
+ 2,
+ pytest.mark.xfail(3),
+ 4,
+ ])
+ def test_isEven(n):
+ assert n % 2 == 0
+ """
+ testdir.makepyfile(s)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2, skipped=1)
+
+ def test_xfail_with_arg(self, testdir):
+ s = """
+ import pytest
+
+ @pytest.mark.parametrize(("n", "expected"), [
+ (1, 2),
+ pytest.mark.xfail("True")((1, 3)),
+ (2, 3),
+ ])
+ def test_increment(n, expected):
+ assert n + 1 == expected
+ """
+ testdir.makepyfile(s)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2, skipped=1)
+
+ def test_xfail_with_kwarg(self, testdir):
+ s = """
+ import pytest
+
+ @pytest.mark.parametrize(("n", "expected"), [
+ (1, 2),
+ pytest.mark.xfail(reason="some bug")((1, 3)),
+ (2, 3),
+ ])
+ def test_increment(n, expected):
+ assert n + 1 == expected
+ """
+ testdir.makepyfile(s)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2, skipped=1)
+
+ def test_xfail_with_arg_and_kwarg(self, testdir):
+ s = """
+ import pytest
+
+ @pytest.mark.parametrize(("n", "expected"), [
+ (1, 2),
+ pytest.mark.xfail("True", reason="some bug")((1, 3)),
+ (2, 3),
+ ])
+ def test_increment(n, expected):
+ assert n + 1 == expected
+ """
+ testdir.makepyfile(s)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2, skipped=1)
+
+ @pytest.mark.parametrize('strict', [True, False])
+ def test_xfail_passing_is_xpass(self, testdir, strict):
+ s = """
+ import pytest
+
+ @pytest.mark.parametrize(("n", "expected"), [
+ (1, 2),
+ pytest.mark.xfail("sys.version_info > (0, 0, 0)", reason="some bug", strict={strict})((2, 3)),
+ (3, 4),
+ ])
+ def test_increment(n, expected):
+ assert n + 1 == expected
+ """.format(strict=strict)
+ testdir.makepyfile(s)
+ reprec = testdir.inline_run()
+ passed, failed = (2, 1) if strict else (3, 0)
+ reprec.assertoutcome(passed=passed, failed=failed)
+
+ def test_parametrize_called_in_generate_tests(self, testdir):
+ s = """
+ import pytest
+
+
+ def pytest_generate_tests(metafunc):
+ passingTestData = [(1, 2),
+ (2, 3)]
+ failingTestData = [(1, 3),
+ (2, 2)]
+
+ testData = passingTestData + [pytest.mark.xfail(d)
+ for d in failingTestData]
+ metafunc.parametrize(("n", "expected"), testData)
+
+
+ def test_increment(n, expected):
+ assert n + 1 == expected
+ """
+ testdir.makepyfile(s)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2, skipped=2)
+
+ @pytest.mark.issue290
+ def test_parametrize_ID_generation_string_int_works(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def myfixture():
+ return 'example'
+ @pytest.mark.parametrize(
+ 'limit', (0, '0'))
+ def test_limit(limit, myfixture):
+ return
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=2)
+
+ @pytest.mark.parametrize('strict', [True, False])
+ def test_parametrize_marked_value(self, testdir, strict):
+ s = """
+ import pytest
+
+ @pytest.mark.parametrize(("n", "expected"), [
+ pytest.param(
+ 2,3,
+ marks=pytest.mark.xfail("sys.version_info > (0, 0, 0)", reason="some bug", strict={strict}),
+ ),
+ pytest.param(
+ 2,3,
+ marks=[pytest.mark.xfail("sys.version_info > (0, 0, 0)", reason="some bug", strict={strict})],
+ ),
+ ])
+ def test_increment(n, expected):
+ assert n + 1 == expected
+ """.format(strict=strict)
+ testdir.makepyfile(s)
+ reprec = testdir.inline_run()
+ passed, failed = (0, 2) if strict else (2, 0)
+ reprec.assertoutcome(passed=passed, failed=failed)
+
+ def test_pytest_make_parametrize_id(self, testdir):
+ testdir.makeconftest("""
+ def pytest_make_parametrize_id(config, val):
+ return str(val * 2)
+ """)
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.parametrize("x", range(2))
+ def test_func(x):
+ pass
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines([
+ "*test_func*0*PASS*",
+ "*test_func*2*PASS*",
+ ])
+
+ def test_pytest_make_parametrize_id_with_argname(self, testdir):
+ testdir.makeconftest("""
+ def pytest_make_parametrize_id(config, val, argname):
+ return str(val * 2 if argname == 'x' else val * 10)
+ """)
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.parametrize("x", range(2))
+ def test_func_a(x):
+ pass
+
+ @pytest.mark.parametrize("y", [1])
+ def test_func_b(y):
+ pass
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines([
+ "*test_func_a*0*PASS*",
+ "*test_func_a*2*PASS*",
+ "*test_func_b*10*PASS*",
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/raises.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/raises.py
new file mode 100644
index 00000000000..321ee349ee6
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/raises.py
@@ -0,0 +1,134 @@
+import pytest
+import sys
+
+
+class TestRaises(object):
+ def test_raises(self):
+ source = "int('qwe')"
+ excinfo = pytest.raises(ValueError, source)
+ code = excinfo.traceback[-1].frame.code
+ s = str(code.fullsource)
+ assert s == source
+
+ def test_raises_exec(self):
+ pytest.raises(ValueError, "a,x = []")
+
+ def test_raises_syntax_error(self):
+ pytest.raises(SyntaxError, "qwe qwe qwe")
+
+ def test_raises_function(self):
+ pytest.raises(ValueError, int, 'hello')
+
+ def test_raises_callable_no_exception(self):
+ class A(object):
+ def __call__(self):
+ pass
+ try:
+ pytest.raises(ValueError, A())
+ except pytest.raises.Exception:
+ pass
+
+ def test_raises_as_contextmanager(self, testdir):
+ testdir.makepyfile("""
+ from __future__ import with_statement
+ import py, pytest
+ import _pytest._code
+
+ def test_simple():
+ with pytest.raises(ZeroDivisionError) as excinfo:
+ assert isinstance(excinfo, _pytest._code.ExceptionInfo)
+ 1/0
+ print (excinfo)
+ assert excinfo.type == ZeroDivisionError
+ assert isinstance(excinfo.value, ZeroDivisionError)
+
+ def test_noraise():
+ with pytest.raises(pytest.raises.Exception):
+ with pytest.raises(ValueError):
+ int()
+
+ def test_raise_wrong_exception_passes_by():
+ with pytest.raises(ZeroDivisionError):
+ with pytest.raises(ValueError):
+ 1/0
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*3 passed*',
+ ])
+
+ def test_noclass(self):
+ with pytest.raises(TypeError):
+ pytest.raises('wrong', lambda: None)
+
+ def test_tuple(self):
+ with pytest.raises((KeyError, ValueError)):
+ raise KeyError('oops')
+
+ def test_no_raise_message(self):
+ try:
+ pytest.raises(ValueError, int, '0')
+ except pytest.raises.Exception as e:
+ assert e.msg == "DID NOT RAISE {0}".format(repr(ValueError))
+ else:
+ assert False, "Expected pytest.raises.Exception"
+
+ try:
+ with pytest.raises(ValueError):
+ pass
+ except pytest.raises.Exception as e:
+ assert e.msg == "DID NOT RAISE {0}".format(repr(ValueError))
+ else:
+ assert False, "Expected pytest.raises.Exception"
+
+ def test_custom_raise_message(self):
+ message = "TEST_MESSAGE"
+ try:
+ with pytest.raises(ValueError, message=message):
+ pass
+ except pytest.raises.Exception as e:
+ assert e.msg == message
+ else:
+ assert False, "Expected pytest.raises.Exception"
+
+ @pytest.mark.parametrize('method', ['function', 'with'])
+ def test_raises_cyclic_reference(self, method):
+ """
+ Ensure pytest.raises does not leave a reference cycle (#1965).
+ """
+ import gc
+
+ class T(object):
+ def __call__(self):
+ raise ValueError
+
+ t = T()
+ if method == 'function':
+ pytest.raises(ValueError, t)
+ else:
+ with pytest.raises(ValueError):
+ t()
+
+ # ensure both forms of pytest.raises don't leave exceptions in sys.exc_info()
+ assert sys.exc_info() == (None, None, None)
+
+ del t
+
+ # ensure the t instance is not stuck in a cyclic reference
+ for o in gc.get_objects():
+ assert type(o) is not T
+
+ def test_raises_match(self):
+ msg = r"with base \d+"
+ with pytest.raises(ValueError, match=msg):
+ int('asdf')
+
+ msg = "with base 10"
+ with pytest.raises(ValueError, match=msg):
+ int('asdf')
+
+ msg = "with base 16"
+ expr = r"Pattern '{0}' not found in 'invalid literal for int\(\) with base 10: 'asdf''".format(msg)
+ with pytest.raises(AssertionError, match=expr):
+ with pytest.raises(ValueError, match=msg):
+ int('asdf', base=10)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/setup_only.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/setup_only.py
new file mode 100644
index 00000000000..ab34312fcc8
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/setup_only.py
@@ -0,0 +1,243 @@
+import pytest
+
+
+@pytest.fixture(params=['--setup-only', '--setup-plan', '--setup-show'],
+ scope='module')
+def mode(request):
+ return request.param
+
+
+def test_show_only_active_fixtures(testdir, mode):
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture
+ def _arg0():
+ """hidden arg0 fixture"""
+ @pytest.fixture
+ def arg1():
+ """arg1 docstring"""
+ def test_arg1(arg1):
+ pass
+ ''')
+
+ result = testdir.runpytest(mode, p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ '*SETUP F arg1*',
+ '*test_arg1 (fixtures used: arg1)*',
+ '*TEARDOWN F arg1*',
+ ])
+ assert "_arg0" not in result.stdout.str()
+
+
+def test_show_different_scopes(testdir, mode):
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture
+ def arg_function():
+ """function scoped fixture"""
+ @pytest.fixture(scope='session')
+ def arg_session():
+ """session scoped fixture"""
+ def test_arg1(arg_session, arg_function):
+ pass
+ ''')
+
+ result = testdir.runpytest(mode, p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ 'SETUP S arg_session*',
+ '*SETUP F arg_function*',
+ '*test_arg1 (fixtures used: arg_function, arg_session)*',
+ '*TEARDOWN F arg_function*',
+ 'TEARDOWN S arg_session*',
+ ])
+
+
+def test_show_nested_fixtures(testdir, mode):
+ testdir.makeconftest('''
+ import pytest
+ @pytest.fixture(scope='session')
+ def arg_same():
+ """session scoped fixture"""
+ ''')
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture(scope='function')
+ def arg_same(arg_same):
+ """function scoped fixture"""
+ def test_arg1(arg_same):
+ pass
+ ''')
+
+ result = testdir.runpytest(mode, p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ 'SETUP S arg_same*',
+ '*SETUP F arg_same (fixtures used: arg_same)*',
+ '*test_arg1 (fixtures used: arg_same)*',
+ '*TEARDOWN F arg_same*',
+ 'TEARDOWN S arg_same*',
+ ])
+
+
+def test_show_fixtures_with_autouse(testdir, mode):
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture
+ def arg_function():
+ """function scoped fixture"""
+ @pytest.fixture(scope='session', autouse=True)
+ def arg_session():
+ """session scoped fixture"""
+ def test_arg1(arg_function):
+ pass
+ ''')
+
+ result = testdir.runpytest(mode, p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ 'SETUP S arg_session*',
+ '*SETUP F arg_function*',
+ '*test_arg1 (fixtures used: arg_function, arg_session)*',
+ ])
+
+
+def test_show_fixtures_with_parameters(testdir, mode):
+ testdir.makeconftest('''
+ import pytest
+ @pytest.fixture(scope='session', params=['foo', 'bar'])
+ def arg_same():
+ """session scoped fixture"""
+ ''')
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture(scope='function')
+ def arg_other(arg_same):
+ """function scoped fixture"""
+ def test_arg1(arg_other):
+ pass
+ ''')
+
+ result = testdir.runpytest(mode, p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ 'SETUP S arg_same?foo?',
+ 'TEARDOWN S arg_same?foo?',
+ 'SETUP S arg_same?bar?',
+ 'TEARDOWN S arg_same?bar?',
+ ])
+
+
+def test_show_fixtures_with_parameter_ids(testdir, mode):
+ testdir.makeconftest('''
+ import pytest
+ @pytest.fixture(
+ scope='session', params=['foo', 'bar'], ids=['spam', 'ham'])
+ def arg_same():
+ """session scoped fixture"""
+ ''')
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture(scope='function')
+ def arg_other(arg_same):
+ """function scoped fixture"""
+ def test_arg1(arg_other):
+ pass
+ ''')
+
+ result = testdir.runpytest(mode, p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ 'SETUP S arg_same?spam?',
+ 'SETUP S arg_same?ham?',
+ ])
+
+
+def test_show_fixtures_with_parameter_ids_function(testdir, mode):
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture(params=['foo', 'bar'], ids=lambda p: p.upper())
+ def foobar():
+ pass
+ def test_foobar(foobar):
+ pass
+ ''')
+
+ result = testdir.runpytest(mode, p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ '*SETUP F foobar?FOO?',
+ '*SETUP F foobar?BAR?',
+ ])
+
+
+def test_dynamic_fixture_request(testdir):
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture()
+ def dynamically_requested_fixture():
+ pass
+ @pytest.fixture()
+ def dependent_fixture(request):
+ request.getfixturevalue('dynamically_requested_fixture')
+ def test_dyn(dependent_fixture):
+ pass
+ ''')
+
+ result = testdir.runpytest('--setup-only', p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ '*SETUP F dynamically_requested_fixture',
+ '*TEARDOWN F dynamically_requested_fixture'
+ ])
+
+
+def test_capturing(testdir):
+ p = testdir.makepyfile('''
+ import pytest, sys
+ @pytest.fixture()
+ def one():
+ sys.stdout.write('this should be captured')
+ sys.stderr.write('this should also be captured')
+ @pytest.fixture()
+ def two(one):
+ assert 0
+ def test_capturing(two):
+ pass
+ ''')
+
+ result = testdir.runpytest('--setup-only', p)
+ result.stdout.fnmatch_lines([
+ 'this should be captured',
+ 'this should also be captured'
+ ])
+
+
+def test_show_fixtures_and_execute_test(testdir):
+ """ Verifies that setups are shown and tests are executed. """
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture
+ def arg():
+ assert True
+ def test_arg(arg):
+ assert False
+ ''')
+
+ result = testdir.runpytest("--setup-show", p)
+ assert result.ret == 1
+
+ result.stdout.fnmatch_lines([
+ '*SETUP F arg*',
+ '*test_arg (fixtures used: arg)F*',
+ '*TEARDOWN F arg*',
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/setup_plan.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/setup_plan.py
new file mode 100644
index 00000000000..8c98224692a
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/setup_plan.py
@@ -0,0 +1,19 @@
+def test_show_fixtures_and_test(testdir):
+ """ Verifies that fixtures are not executed. """
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture
+ def arg():
+ assert False
+ def test_arg(arg):
+ assert False
+ ''')
+
+ result = testdir.runpytest("--setup-plan", p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ '*SETUP F arg*',
+ '*test_arg (fixtures used: arg)',
+ '*TEARDOWN F arg*',
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/show_fixtures_per_test.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/show_fixtures_per_test.py
new file mode 100644
index 00000000000..741f33946a0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/show_fixtures_per_test.py
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+
+
+def test_no_items_should_not_show_output(testdir):
+ result = testdir.runpytest('--fixtures-per-test')
+ assert 'fixtures used by' not in result.stdout.str()
+ assert result.ret == 0
+
+
+def test_fixtures_in_module(testdir):
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture
+ def _arg0():
+ """hidden arg0 fixture"""
+ @pytest.fixture
+ def arg1():
+ """arg1 docstring"""
+ def test_arg1(arg1):
+ pass
+ ''')
+
+ result = testdir.runpytest("--fixtures-per-test", p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ '*fixtures used by test_arg1*',
+ '*(test_fixtures_in_module.py:9)*',
+ 'arg1',
+ ' arg1 docstring',
+ ])
+ assert "_arg0" not in result.stdout.str()
+
+
+def test_fixtures_in_conftest(testdir):
+ testdir.makeconftest('''
+ import pytest
+ @pytest.fixture
+ def arg1():
+ """arg1 docstring"""
+ @pytest.fixture
+ def arg2():
+ """arg2 docstring"""
+ @pytest.fixture
+ def arg3(arg1, arg2):
+ """arg3
+ docstring
+ """
+ ''')
+ p = testdir.makepyfile('''
+ def test_arg2(arg2):
+ pass
+ def test_arg3(arg3):
+ pass
+ ''')
+ result = testdir.runpytest("--fixtures-per-test", p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ '*fixtures used by test_arg2*',
+ '*(test_fixtures_in_conftest.py:2)*',
+ 'arg2',
+ ' arg2 docstring',
+ '*fixtures used by test_arg3*',
+ '*(test_fixtures_in_conftest.py:4)*',
+ 'arg1',
+ ' arg1 docstring',
+ 'arg2',
+ ' arg2 docstring',
+ 'arg3',
+ ' arg3',
+ ' docstring',
+ ])
+
+
+def test_should_show_fixtures_used_by_test(testdir):
+ testdir.makeconftest('''
+ import pytest
+ @pytest.fixture
+ def arg1():
+ """arg1 from conftest"""
+ @pytest.fixture
+ def arg2():
+ """arg2 from conftest"""
+ ''')
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture
+ def arg1():
+ """arg1 from testmodule"""
+ def test_args(arg1, arg2):
+ pass
+ ''')
+ result = testdir.runpytest("--fixtures-per-test", p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ '*fixtures used by test_args*',
+ '*(test_should_show_fixtures_used_by_test.py:6)*',
+ 'arg1',
+ ' arg1 from testmodule',
+ 'arg2',
+ ' arg2 from conftest',
+ ])
+
+
+def test_verbose_include_private_fixtures_and_loc(testdir):
+ testdir.makeconftest('''
+ import pytest
+ @pytest.fixture
+ def _arg1():
+ """_arg1 from conftest"""
+ @pytest.fixture
+ def arg2(_arg1):
+ """arg2 from conftest"""
+ ''')
+ p = testdir.makepyfile('''
+ import pytest
+ @pytest.fixture
+ def arg3():
+ """arg3 from testmodule"""
+ def test_args(arg2, arg3):
+ pass
+ ''')
+ result = testdir.runpytest("--fixtures-per-test", "-v", p)
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ '*fixtures used by test_args*',
+ '*(test_verbose_include_private_fixtures_and_loc.py:6)*',
+ '_arg1 -- conftest.py:3',
+ ' _arg1 from conftest',
+ 'arg2 -- conftest.py:6',
+ ' arg2 from conftest',
+ 'arg3 -- test_verbose_include_private_fixtures_and_loc.py:3',
+ ' arg3 from testmodule',
+ ])
+
+
+def test_doctest_items(testdir):
+ testdir.makepyfile('''
+ def foo():
+ """
+ >>> 1 + 1
+ 2
+ """
+ ''')
+ testdir.maketxtfile('''
+ >>> 1 + 1
+ 2
+ ''')
+ result = testdir.runpytest("--fixtures-per-test", "--doctest-modules",
+ "--doctest-glob=*.txt", "-v")
+ assert result.ret == 0
+
+ result.stdout.fnmatch_lines([
+ '*collected 2 items*',
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/test_deprecations.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/test_deprecations.py
new file mode 100644
index 00000000000..5001f765f6c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/python/test_deprecations.py
@@ -0,0 +1,22 @@
+import pytest
+
+from _pytest.python import PyCollector
+
+
+class PyCollectorMock(PyCollector):
+ """evil hack"""
+
+ def __init__(self):
+ self.called = False
+
+ def _makeitem(self, *k):
+ """hack to disable the actual behaviour"""
+ self.called = True
+
+
+def test_pycollector_makeitem_is_deprecated():
+
+ collector = PyCollectorMock()
+ with pytest.deprecated_call():
+ collector.makeitem('foo', 'bar')
+ assert collector.called
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_argcomplete.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_argcomplete.py
new file mode 100644
index 00000000000..c9261257743
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_argcomplete.py
@@ -0,0 +1,97 @@
+from __future__ import absolute_import, division, print_function
+import py
+import pytest
+
+# test for _argcomplete but not specific for any application
+
+
+def equal_with_bash(prefix, ffc, fc, out=None):
+ res = ffc(prefix)
+ res_bash = set(fc(prefix))
+ retval = set(res) == res_bash
+ if out:
+ out.write('equal_with_bash %s %s\n' % (retval, res))
+ if not retval:
+ out.write(' python - bash: %s\n' % (set(res) - res_bash))
+ out.write(' bash - python: %s\n' % (res_bash - set(res)))
+ return retval
+
+# copied from argcomplete.completers as import from there
+# also pulls in argcomplete.__init__ which opens filedescriptor 9
+# this gives an IOError at the end of testrun
+
+
+def _wrapcall(*args, **kargs):
+ try:
+ if py.std.sys.version_info > (2, 7):
+ return py.std.subprocess.check_output(*args, **kargs).decode().splitlines()
+ if 'stdout' in kargs:
+ raise ValueError('stdout argument not allowed, it will be overridden.')
+ process = py.std.subprocess.Popen(
+ stdout=py.std.subprocess.PIPE, *args, **kargs)
+ output, unused_err = process.communicate()
+ retcode = process.poll()
+ if retcode:
+ cmd = kargs.get("args")
+ if cmd is None:
+ cmd = args[0]
+ raise py.std.subprocess.CalledProcessError(retcode, cmd)
+ return output.decode().splitlines()
+ except py.std.subprocess.CalledProcessError:
+ return []
+
+
+class FilesCompleter(object):
+ 'File completer class, optionally takes a list of allowed extensions'
+
+ def __init__(self, allowednames=(), directories=True):
+ # Fix if someone passes in a string instead of a list
+ if type(allowednames) is str:
+ allowednames = [allowednames]
+
+ self.allowednames = [x.lstrip('*').lstrip('.') for x in allowednames]
+ self.directories = directories
+
+ def __call__(self, prefix, **kwargs):
+ completion = []
+ if self.allowednames:
+ if self.directories:
+ files = _wrapcall(['bash', '-c',
+ "compgen -A directory -- '{p}'".format(p=prefix)])
+ completion += [f + '/' for f in files]
+ for x in self.allowednames:
+ completion += _wrapcall(['bash', '-c',
+ "compgen -A file -X '!*.{0}' -- '{p}'".format(x, p=prefix)])
+ else:
+ completion += _wrapcall(['bash', '-c',
+ "compgen -A file -- '{p}'".format(p=prefix)])
+
+ anticomp = _wrapcall(['bash', '-c',
+ "compgen -A directory -- '{p}'".format(p=prefix)])
+
+ completion = list(set(completion) - set(anticomp))
+
+ if self.directories:
+ completion += [f + '/' for f in anticomp]
+ return completion
+
+
+class TestArgComplete(object):
+ @pytest.mark.skipif("sys.platform in ('win32', 'darwin')")
+ def test_compare_with_compgen(self):
+ from _pytest._argcomplete import FastFilesCompleter
+ ffc = FastFilesCompleter()
+ fc = FilesCompleter()
+ for x in ['/', '/d', '/data', 'qqq', '']:
+ assert equal_with_bash(x, ffc, fc, out=py.std.sys.stdout)
+
+ @pytest.mark.skipif("sys.platform in ('win32', 'darwin')")
+ def test_remove_dir_prefix(self):
+ """this is not compatible with compgen but it is with bash itself:
+ ls /usr/<TAB>
+ """
+ from _pytest._argcomplete import FastFilesCompleter
+ ffc = FastFilesCompleter()
+ fc = FilesCompleter()
+ for x in '/usr/'.split():
+ assert not equal_with_bash(x, ffc, fc, out=py.std.sys.stdout)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_assertion.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_assertion.py
new file mode 100644
index 00000000000..328fe7fa918
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_assertion.py
@@ -0,0 +1,1066 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+import sys
+import textwrap
+
+import _pytest.assertion as plugin
+import py
+import pytest
+from _pytest.assertion import util
+from _pytest.assertion import truncate
+
+PY3 = sys.version_info >= (3, 0)
+
+
+@pytest.fixture
+def mock_config():
+
+ class Config(object):
+ verbose = False
+
+ def getoption(self, name):
+ if name == 'verbose':
+ return self.verbose
+ raise KeyError('Not mocked out: %s' % name)
+
+ return Config()
+
+
+class TestImportHookInstallation(object):
+
+ @pytest.mark.parametrize('initial_conftest', [True, False])
+ @pytest.mark.parametrize('mode', ['plain', 'rewrite'])
+ def test_conftest_assertion_rewrite(self, testdir, initial_conftest, mode):
+ """Test that conftest files are using assertion rewrite on import.
+ (#1619)
+ """
+ testdir.tmpdir.join('foo/tests').ensure(dir=1)
+ conftest_path = 'conftest.py' if initial_conftest else 'foo/conftest.py'
+ contents = {
+ conftest_path: """
+ import pytest
+ @pytest.fixture
+ def check_first():
+ def check(values, value):
+ assert values.pop(0) == value
+ return check
+ """,
+ 'foo/tests/test_foo.py': """
+ def test(check_first):
+ check_first([10, 30], 30)
+ """
+ }
+ testdir.makepyfile(**contents)
+ result = testdir.runpytest_subprocess('--assert=%s' % mode)
+ if mode == 'plain':
+ expected = 'E AssertionError'
+ elif mode == 'rewrite':
+ expected = '*assert 10 == 30*'
+ else:
+ assert 0
+ result.stdout.fnmatch_lines([expected])
+
+ def test_rewrite_assertions_pytester_plugin(self, testdir):
+ """
+ Assertions in the pytester plugin must also benefit from assertion
+ rewriting (#1920).
+ """
+ testdir.makepyfile("""
+ pytest_plugins = ['pytester']
+ def test_dummy_failure(testdir): # how meta!
+ testdir.makepyfile('def test(): assert 0')
+ r = testdir.inline_run()
+ r.assertoutcome(passed=1)
+ """)
+ result = testdir.runpytest_subprocess()
+ result.stdout.fnmatch_lines([
+ '*assert 1 == 0*',
+ ])
+
+ @pytest.mark.parametrize('mode', ['plain', 'rewrite'])
+ def test_pytest_plugins_rewrite(self, testdir, mode):
+ contents = {
+ 'conftest.py': """
+ pytest_plugins = ['ham']
+ """,
+ 'ham.py': """
+ import pytest
+ @pytest.fixture
+ def check_first():
+ def check(values, value):
+ assert values.pop(0) == value
+ return check
+ """,
+ 'test_foo.py': """
+ def test_foo(check_first):
+ check_first([10, 30], 30)
+ """,
+ }
+ testdir.makepyfile(**contents)
+ result = testdir.runpytest_subprocess('--assert=%s' % mode)
+ if mode == 'plain':
+ expected = 'E AssertionError'
+ elif mode == 'rewrite':
+ expected = '*assert 10 == 30*'
+ else:
+ assert 0
+ result.stdout.fnmatch_lines([expected])
+
+ @pytest.mark.parametrize('mode', ['str', 'list'])
+ def test_pytest_plugins_rewrite_module_names(self, testdir, mode):
+ """Test that pluginmanager correct marks pytest_plugins variables
+ for assertion rewriting if they are defined as plain strings or
+ list of strings (#1888).
+ """
+ plugins = '"ham"' if mode == 'str' else '["ham"]'
+ contents = {
+ 'conftest.py': """
+ pytest_plugins = {plugins}
+ """.format(plugins=plugins),
+ 'ham.py': """
+ import pytest
+ """,
+ 'test_foo.py': """
+ def test_foo(pytestconfig):
+ assert 'ham' in pytestconfig.pluginmanager.rewrite_hook._must_rewrite
+ """,
+ }
+ testdir.makepyfile(**contents)
+ result = testdir.runpytest_subprocess('--assert=rewrite')
+ assert result.ret == 0
+
+ def test_pytest_plugins_rewrite_module_names_correctly(self, testdir):
+ """Test that we match files correctly when they are marked for rewriting (#2939)."""
+ contents = {
+ 'conftest.py': """
+ pytest_plugins = "ham"
+ """,
+ 'ham.py': "",
+ 'hamster.py': "",
+ 'test_foo.py': """
+ def test_foo(pytestconfig):
+ assert pytestconfig.pluginmanager.rewrite_hook.find_module('ham') is not None
+ assert pytestconfig.pluginmanager.rewrite_hook.find_module('hamster') is None
+ """,
+ }
+ testdir.makepyfile(**contents)
+ result = testdir.runpytest_subprocess('--assert=rewrite')
+ assert result.ret == 0
+
+ @pytest.mark.parametrize('mode', ['plain', 'rewrite'])
+ @pytest.mark.parametrize('plugin_state', ['development', 'installed'])
+ def test_installed_plugin_rewrite(self, testdir, mode, plugin_state):
+ # Make sure the hook is installed early enough so that plugins
+ # installed via setuptools are rewritten.
+ testdir.tmpdir.join('hampkg').ensure(dir=1)
+ contents = {
+ 'hampkg/__init__.py': """
+ import pytest
+
+ @pytest.fixture
+ def check_first2():
+ def check(values, value):
+ assert values.pop(0) == value
+ return check
+ """,
+ 'spamplugin.py': """
+ import pytest
+ from hampkg import check_first2
+
+ @pytest.fixture
+ def check_first():
+ def check(values, value):
+ assert values.pop(0) == value
+ return check
+ """,
+ 'mainwrapper.py': """
+ import pytest, pkg_resources
+
+ plugin_state = "{plugin_state}"
+
+ class DummyDistInfo(object):
+ project_name = 'spam'
+ version = '1.0'
+
+ def _get_metadata(self, name):
+ # 'RECORD' meta-data only available in installed plugins
+ if name == 'RECORD' and plugin_state == "installed":
+ return ['spamplugin.py,sha256=abc,123',
+ 'hampkg/__init__.py,sha256=abc,123']
+ # 'SOURCES.txt' meta-data only available for plugins in development mode
+ elif name == 'SOURCES.txt' and plugin_state == "development":
+ return ['spamplugin.py',
+ 'hampkg/__init__.py']
+ return []
+
+ class DummyEntryPoint(object):
+ name = 'spam'
+ module_name = 'spam.py'
+ attrs = ()
+ extras = None
+ dist = DummyDistInfo()
+
+ def load(self, require=True, *args, **kwargs):
+ import spamplugin
+ return spamplugin
+
+ def iter_entry_points(name):
+ yield DummyEntryPoint()
+
+ pkg_resources.iter_entry_points = iter_entry_points
+ pytest.main()
+ """.format(plugin_state=plugin_state),
+ 'test_foo.py': """
+ def test(check_first):
+ check_first([10, 30], 30)
+
+ def test2(check_first2):
+ check_first([10, 30], 30)
+ """,
+ }
+ testdir.makepyfile(**contents)
+ result = testdir.run(sys.executable, 'mainwrapper.py', '-s', '--assert=%s' % mode)
+ if mode == 'plain':
+ expected = 'E AssertionError'
+ elif mode == 'rewrite':
+ expected = '*assert 10 == 30*'
+ else:
+ assert 0
+ result.stdout.fnmatch_lines([expected])
+
+ def test_rewrite_ast(self, testdir):
+ testdir.tmpdir.join('pkg').ensure(dir=1)
+ contents = {
+ 'pkg/__init__.py': """
+ import pytest
+ pytest.register_assert_rewrite('pkg.helper')
+ """,
+ 'pkg/helper.py': """
+ def tool():
+ a, b = 2, 3
+ assert a == b
+ """,
+ 'pkg/plugin.py': """
+ import pytest, pkg.helper
+ @pytest.fixture
+ def tool():
+ return pkg.helper.tool
+ """,
+ 'pkg/other.py': """
+ values = [3, 2]
+ def tool():
+ assert values.pop() == 3
+ """,
+ 'conftest.py': """
+ pytest_plugins = ['pkg.plugin']
+ """,
+ 'test_pkg.py': """
+ import pkg.other
+ def test_tool(tool):
+ tool()
+ def test_other():
+ pkg.other.tool()
+ """,
+ }
+ testdir.makepyfile(**contents)
+ result = testdir.runpytest_subprocess('--assert=rewrite')
+ result.stdout.fnmatch_lines(['>*assert a == b*',
+ 'E*assert 2 == 3*',
+ '>*assert values.pop() == 3*',
+ 'E*AssertionError'])
+
+ def test_register_assert_rewrite_checks_types(self):
+ with pytest.raises(TypeError):
+ pytest.register_assert_rewrite(['pytest_tests_internal_non_existing'])
+ pytest.register_assert_rewrite('pytest_tests_internal_non_existing',
+ 'pytest_tests_internal_non_existing2')
+
+
+class TestBinReprIntegration(object):
+
+ def test_pytest_assertrepr_compare_called(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ values = []
+ def pytest_assertrepr_compare(op, left, right):
+ values.append((op, left, right))
+
+ @pytest.fixture
+ def list(request):
+ return values
+ """)
+ testdir.makepyfile("""
+ def test_hello():
+ assert 0 == 1
+ def test_check(list):
+ assert list == [("==", 0, 1)]
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines([
+ "*test_hello*FAIL*",
+ "*test_check*PASS*",
+ ])
+
+
+def callequal(left, right, verbose=False):
+ config = mock_config()
+ config.verbose = verbose
+ return plugin.pytest_assertrepr_compare(config, '==', left, right)
+
+
+class TestAssert_reprcompare(object):
+ def test_different_types(self):
+ assert callequal([0, 1], 'foo') is None
+
+ def test_summary(self):
+ summary = callequal([0, 1], [0, 2])[0]
+ assert len(summary) < 65
+
+ def test_text_diff(self):
+ diff = callequal('spam', 'eggs')[1:]
+ assert '- spam' in diff
+ assert '+ eggs' in diff
+
+ def test_text_skipping(self):
+ lines = callequal('a' * 50 + 'spam', 'a' * 50 + 'eggs')
+ assert 'Skipping' in lines[1]
+ for line in lines:
+ assert 'a' * 50 not in line
+
+ def test_text_skipping_verbose(self):
+ lines = callequal('a' * 50 + 'spam', 'a' * 50 + 'eggs', verbose=True)
+ assert '- ' + 'a' * 50 + 'spam' in lines
+ assert '+ ' + 'a' * 50 + 'eggs' in lines
+
+ def test_multiline_text_diff(self):
+ left = 'foo\nspam\nbar'
+ right = 'foo\neggs\nbar'
+ diff = callequal(left, right)
+ assert '- spam' in diff
+ assert '+ eggs' in diff
+
+ def test_list(self):
+ expl = callequal([0, 1], [0, 2])
+ assert len(expl) > 1
+
+ @pytest.mark.parametrize(
+ ['left', 'right', 'expected'], [
+ ([0, 1], [0, 2], """
+ Full diff:
+ - [0, 1]
+ ? ^
+ + [0, 2]
+ ? ^
+ """),
+ ({0: 1}, {0: 2}, """
+ Full diff:
+ - {0: 1}
+ ? ^
+ + {0: 2}
+ ? ^
+ """),
+ (set([0, 1]), set([0, 2]), """
+ Full diff:
+ - set([0, 1])
+ ? ^
+ + set([0, 2])
+ ? ^
+ """ if not PY3 else """
+ Full diff:
+ - {0, 1}
+ ? ^
+ + {0, 2}
+ ? ^
+ """)
+ ]
+ )
+ def test_iterable_full_diff(self, left, right, expected):
+ """Test the full diff assertion failure explanation.
+
+ When verbose is False, then just a -v notice to get the diff is rendered,
+ when verbose is True, then ndiff of the pprint is returned.
+ """
+ expl = callequal(left, right, verbose=False)
+ assert expl[-1] == 'Use -v to get the full diff'
+ expl = '\n'.join(callequal(left, right, verbose=True))
+ assert expl.endswith(textwrap.dedent(expected).strip())
+
+ def test_list_different_lengths(self):
+ expl = callequal([0, 1], [0, 1, 2])
+ assert len(expl) > 1
+ expl = callequal([0, 1, 2], [0, 1])
+ assert len(expl) > 1
+
+ def test_dict(self):
+ expl = callequal({'a': 0}, {'a': 1})
+ assert len(expl) > 1
+
+ def test_dict_omitting(self):
+ lines = callequal({'a': 0, 'b': 1}, {'a': 1, 'b': 1})
+ assert lines[1].startswith('Omitting 1 identical item')
+ assert 'Common items' not in lines
+ for line in lines[1:]:
+ assert 'b' not in line
+
+ def test_dict_omitting_with_verbosity_1(self):
+ """ Ensure differing items are visible for verbosity=1 (#1512) """
+ lines = callequal({'a': 0, 'b': 1}, {'a': 1, 'b': 1}, verbose=1)
+ assert lines[1].startswith('Omitting 1 identical item')
+ assert lines[2].startswith('Differing items')
+ assert lines[3] == "{'a': 0} != {'a': 1}"
+ assert 'Common items' not in lines
+
+ def test_dict_omitting_with_verbosity_2(self):
+ lines = callequal({'a': 0, 'b': 1}, {'a': 1, 'b': 1}, verbose=2)
+ assert lines[1].startswith('Common items:')
+ assert 'Omitting' not in lines[1]
+ assert lines[2] == "{'b': 1}"
+
+ def test_set(self):
+ expl = callequal(set([0, 1]), set([0, 2]))
+ assert len(expl) > 1
+
+ def test_frozenzet(self):
+ expl = callequal(frozenset([0, 1]), set([0, 2]))
+ assert len(expl) > 1
+
+ def test_Sequence(self):
+ col = py.builtin._tryimport(
+ "collections.abc",
+ "collections",
+ "sys")
+ if not hasattr(col, "MutableSequence"):
+ pytest.skip("cannot import MutableSequence")
+ MutableSequence = col.MutableSequence
+
+ class TestSequence(MutableSequence): # works with a Sequence subclass
+ def __init__(self, iterable):
+ self.elements = list(iterable)
+
+ def __getitem__(self, item):
+ return self.elements[item]
+
+ def __len__(self):
+ return len(self.elements)
+
+ def __setitem__(self, item, value):
+ pass
+
+ def __delitem__(self, item):
+ pass
+
+ def insert(self, item, index):
+ pass
+
+ expl = callequal(TestSequence([0, 1]), list([0, 2]))
+ assert len(expl) > 1
+
+ def test_list_tuples(self):
+ expl = callequal([], [(1, 2)])
+ assert len(expl) > 1
+ expl = callequal([(1, 2)], [])
+ assert len(expl) > 1
+
+ def test_list_bad_repr(self):
+ class A(object):
+ def __repr__(self):
+ raise ValueError(42)
+ expl = callequal([], [A()])
+ assert 'ValueError' in "".join(expl)
+ expl = callequal({}, {'1': A()})
+ assert 'faulty' in "".join(expl)
+
+ def test_one_repr_empty(self):
+ """
+ the faulty empty string repr did trigger
+ a unbound local error in _diff_text
+ """
+ class A(str):
+ def __repr__(self):
+ return ''
+ expl = callequal(A(), '')
+ assert not expl
+
+ def test_repr_no_exc(self):
+ expl = ' '.join(callequal('foo', 'bar'))
+ assert 'raised in repr()' not in expl
+
+ def test_unicode(self):
+ left = py.builtin._totext('£€', 'utf-8')
+ right = py.builtin._totext('£', 'utf-8')
+ expl = callequal(left, right)
+ assert expl[0] == py.builtin._totext("'£€' == '£'", 'utf-8')
+ assert expl[1] == py.builtin._totext('- £€', 'utf-8')
+ assert expl[2] == py.builtin._totext('+ £', 'utf-8')
+
+ def test_nonascii_text(self):
+ """
+ :issue: 877
+ non ascii python2 str caused a UnicodeDecodeError
+ """
+ class A(str):
+ def __repr__(self):
+ return '\xff'
+ expl = callequal(A(), '1')
+ assert expl
+
+ def test_format_nonascii_explanation(self):
+ assert util.format_explanation('λ')
+
+ def test_mojibake(self):
+ # issue 429
+ left = 'e'
+ right = '\xc3\xa9'
+ if not isinstance(left, py.builtin.bytes):
+ left = py.builtin.bytes(left, 'utf-8')
+ right = py.builtin.bytes(right, 'utf-8')
+ expl = callequal(left, right)
+ for line in expl:
+ assert isinstance(line, py.builtin.text)
+ msg = py.builtin._totext('\n').join(expl)
+ assert msg
+
+
+class TestFormatExplanation(object):
+
+ def test_special_chars_full(self, testdir):
+ # Issue 453, for the bug this would raise IndexError
+ testdir.makepyfile("""
+ def test_foo():
+ assert '\\n}' == ''
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.stdout.fnmatch_lines([
+ "*AssertionError*",
+ ])
+
+ def test_fmt_simple(self):
+ expl = 'assert foo'
+ assert util.format_explanation(expl) == 'assert foo'
+
+ def test_fmt_where(self):
+ expl = '\n'.join(['assert 1',
+ '{1 = foo',
+ '} == 2'])
+ res = '\n'.join(['assert 1 == 2',
+ ' + where 1 = foo'])
+ assert util.format_explanation(expl) == res
+
+ def test_fmt_and(self):
+ expl = '\n'.join(['assert 1',
+ '{1 = foo',
+ '} == 2',
+ '{2 = bar',
+ '}'])
+ res = '\n'.join(['assert 1 == 2',
+ ' + where 1 = foo',
+ ' + and 2 = bar'])
+ assert util.format_explanation(expl) == res
+
+ def test_fmt_where_nested(self):
+ expl = '\n'.join(['assert 1',
+ '{1 = foo',
+ '{foo = bar',
+ '}',
+ '} == 2'])
+ res = '\n'.join(['assert 1 == 2',
+ ' + where 1 = foo',
+ ' + where foo = bar'])
+ assert util.format_explanation(expl) == res
+
+ def test_fmt_newline(self):
+ expl = '\n'.join(['assert "foo" == "bar"',
+ '~- foo',
+ '~+ bar'])
+ res = '\n'.join(['assert "foo" == "bar"',
+ ' - foo',
+ ' + bar'])
+ assert util.format_explanation(expl) == res
+
+ def test_fmt_newline_escaped(self):
+ expl = '\n'.join(['assert foo == bar',
+ 'baz'])
+ res = 'assert foo == bar\\nbaz'
+ assert util.format_explanation(expl) == res
+
+ def test_fmt_newline_before_where(self):
+ expl = '\n'.join(['the assertion message here',
+ '>assert 1',
+ '{1 = foo',
+ '} == 2',
+ '{2 = bar',
+ '}'])
+ res = '\n'.join(['the assertion message here',
+ 'assert 1 == 2',
+ ' + where 1 = foo',
+ ' + and 2 = bar'])
+ assert util.format_explanation(expl) == res
+
+ def test_fmt_multi_newline_before_where(self):
+ expl = '\n'.join(['the assertion',
+ '~message here',
+ '>assert 1',
+ '{1 = foo',
+ '} == 2',
+ '{2 = bar',
+ '}'])
+ res = '\n'.join(['the assertion',
+ ' message here',
+ 'assert 1 == 2',
+ ' + where 1 = foo',
+ ' + and 2 = bar'])
+ assert util.format_explanation(expl) == res
+
+
+class TestTruncateExplanation(object):
+
+ """ Confirm assertion output is truncated as expected """
+
+ # The number of lines in the truncation explanation message. Used
+ # to calculate that results have the expected length.
+ LINES_IN_TRUNCATION_MSG = 2
+
+ def test_doesnt_truncate_when_input_is_empty_list(self):
+ expl = []
+ result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100)
+ assert result == expl
+
+ def test_doesnt_truncate_at_when_input_is_5_lines_and_LT_max_chars(self):
+ expl = ['a' * 100 for x in range(5)]
+ result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80)
+ assert result == expl
+
+ def test_truncates_at_8_lines_when_given_list_of_empty_strings(self):
+ expl = ['' for x in range(50)]
+ result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100)
+ assert result != expl
+ assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG
+ assert "Full output truncated" in result[-1]
+ assert "43 lines hidden" in result[-1]
+ last_line_before_trunc_msg = result[- self.LINES_IN_TRUNCATION_MSG - 1]
+ assert last_line_before_trunc_msg.endswith("...")
+
+ def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self):
+ expl = ['a' for x in range(100)]
+ result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80)
+ assert result != expl
+ assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG
+ assert "Full output truncated" in result[-1]
+ assert "93 lines hidden" in result[-1]
+ last_line_before_trunc_msg = result[- self.LINES_IN_TRUNCATION_MSG - 1]
+ assert last_line_before_trunc_msg.endswith("...")
+
+ def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self):
+ expl = ['a' * 80 for x in range(16)]
+ result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80)
+ assert result != expl
+ assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG
+ assert "Full output truncated" in result[-1]
+ assert "9 lines hidden" in result[-1]
+ last_line_before_trunc_msg = result[- self.LINES_IN_TRUNCATION_MSG - 1]
+ assert last_line_before_trunc_msg.endswith("...")
+
+ def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self):
+ expl = ['a' * 250 for x in range(10)]
+ result = truncate._truncate_explanation(expl, max_lines=8, max_chars=999)
+ assert result != expl
+ assert len(result) == 4 + self.LINES_IN_TRUNCATION_MSG
+ assert "Full output truncated" in result[-1]
+ assert "7 lines hidden" in result[-1]
+ last_line_before_trunc_msg = result[- self.LINES_IN_TRUNCATION_MSG - 1]
+ assert last_line_before_trunc_msg.endswith("...")
+
+ def test_truncates_at_1_line_when_first_line_is_GT_max_chars(self):
+ expl = ['a' * 250 for x in range(1000)]
+ result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100)
+ assert result != expl
+ assert len(result) == 1 + self.LINES_IN_TRUNCATION_MSG
+ assert "Full output truncated" in result[-1]
+ assert "1000 lines hidden" in result[-1]
+ last_line_before_trunc_msg = result[- self.LINES_IN_TRUNCATION_MSG - 1]
+ assert last_line_before_trunc_msg.endswith("...")
+
+ def test_full_output_truncated(self, monkeypatch, testdir):
+ """ Test against full runpytest() output. """
+
+ line_count = 7
+ line_len = 100
+ expected_truncated_lines = 2
+ testdir.makepyfile(r"""
+ def test_many_lines():
+ a = list([str(i)[0] * %d for i in range(%d)])
+ b = a[::2]
+ a = '\n'.join(map(str, a))
+ b = '\n'.join(map(str, b))
+ assert a == b
+ """ % (line_len, line_count))
+ monkeypatch.delenv('CI', raising=False)
+
+ result = testdir.runpytest()
+ # without -vv, truncate the message showing a few diff lines only
+ result.stdout.fnmatch_lines([
+ "*- 1*",
+ "*- 3*",
+ "*- 5*",
+ "*truncated (%d lines hidden)*use*-vv*" % expected_truncated_lines,
+ ])
+
+ result = testdir.runpytest('-vv')
+ result.stdout.fnmatch_lines([
+ "* 6*",
+ ])
+
+ monkeypatch.setenv('CI', '1')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "* 6*",
+ ])
+
+
+def test_python25_compile_issue257(testdir):
+ testdir.makepyfile("""
+ def test_rewritten():
+ assert 1 == 2
+ # some comment
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.stdout.fnmatch_lines("""
+ *E*assert 1 == 2*
+ *1 failed*
+ """)
+
+
+def test_rewritten(testdir):
+ testdir.makepyfile("""
+ def test_rewritten():
+ assert "@py_builtins" in globals()
+ """)
+ assert testdir.runpytest().ret == 0
+
+
+def test_reprcompare_notin(mock_config):
+ detail = plugin.pytest_assertrepr_compare(
+ mock_config, 'not in', 'foo', 'aaafoobbb')[1:]
+ assert detail == ["'foo' is contained here:", ' aaafoobbb', '? +++']
+
+
+def test_pytest_assertrepr_compare_integration(testdir):
+ testdir.makepyfile("""
+ def test_hello():
+ x = set(range(100))
+ y = x.copy()
+ y.remove(50)
+ assert x == y
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*def test_hello():*",
+ "*assert x == y*",
+ "*E*Extra items*left*",
+ "*E*50*",
+ ])
+
+
+def test_sequence_comparison_uses_repr(testdir):
+ testdir.makepyfile("""
+ def test_hello():
+ x = set("hello x")
+ y = set("hello y")
+ assert x == y
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*def test_hello():*",
+ "*assert x == y*",
+ "*E*Extra items*left*",
+ "*E*'x'*",
+ "*E*Extra items*right*",
+ "*E*'y'*",
+ ])
+
+
+def test_assertrepr_loaded_per_dir(testdir):
+ testdir.makepyfile(test_base=['def test_base(): assert 1 == 2'])
+ a = testdir.mkdir('a')
+ a_test = a.join('test_a.py')
+ a_test.write('def test_a(): assert 1 == 2')
+ a_conftest = a.join('conftest.py')
+ a_conftest.write('def pytest_assertrepr_compare(): return ["summary a"]')
+ b = testdir.mkdir('b')
+ b_test = b.join('test_b.py')
+ b_test.write('def test_b(): assert 1 == 2')
+ b_conftest = b.join('conftest.py')
+ b_conftest.write('def pytest_assertrepr_compare(): return ["summary b"]')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*def test_base():*',
+ '*E*assert 1 == 2*',
+ '*def test_a():*',
+ '*E*assert summary a*',
+ '*def test_b():*',
+ '*E*assert summary b*'])
+
+
+def test_assertion_options(testdir):
+ testdir.makepyfile("""
+ def test_hello():
+ x = 3
+ assert x == 4
+ """)
+ result = testdir.runpytest()
+ assert "3 == 4" in result.stdout.str()
+ result = testdir.runpytest_subprocess("--assert=plain")
+ assert "3 == 4" not in result.stdout.str()
+
+
+def test_triple_quoted_string_issue113(testdir):
+ testdir.makepyfile("""
+ def test_hello():
+ assert "" == '''
+ '''""")
+ result = testdir.runpytest("--fulltrace")
+ result.stdout.fnmatch_lines([
+ "*1 failed*",
+ ])
+ assert 'SyntaxError' not in result.stdout.str()
+
+
+def test_traceback_failure(testdir):
+ p1 = testdir.makepyfile("""
+ def g():
+ return 2
+ def f(x):
+ assert x == g()
+ def test_onefails():
+ f(3)
+ """)
+ result = testdir.runpytest(p1, "--tb=long")
+ result.stdout.fnmatch_lines([
+ "*test_traceback_failure.py F*",
+ "====* FAILURES *====",
+ "____*____",
+ "",
+ " def test_onefails():",
+ "> f(3)",
+ "",
+ "*test_*.py:6: ",
+ "_ _ _ *",
+ # "",
+ " def f(x):",
+ "> assert x == g()",
+ "E assert 3 == 2",
+ "E + where 2 = g()",
+ "",
+ "*test_traceback_failure.py:4: AssertionError"
+ ])
+
+ result = testdir.runpytest(p1) # "auto"
+ result.stdout.fnmatch_lines([
+ "*test_traceback_failure.py F*",
+ "====* FAILURES *====",
+ "____*____",
+ "",
+ " def test_onefails():",
+ "> f(3)",
+ "",
+ "*test_*.py:6: ",
+ "",
+ " def f(x):",
+ "> assert x == g()",
+ "E assert 3 == 2",
+ "E + where 2 = g()",
+ "",
+ "*test_traceback_failure.py:4: AssertionError"
+ ])
+
+
+@pytest.mark.skipif(sys.version_info[:2] <= (3, 3), reason='Python 3.4+ shows chained exceptions on multiprocess')
+def test_exception_handling_no_traceback(testdir):
+ """
+ Handle chain exceptions in tasks submitted by the multiprocess module (#1984).
+ """
+ p1 = testdir.makepyfile("""
+ from multiprocessing import Pool
+
+ def process_task(n):
+ assert n == 10
+
+ def multitask_job():
+ tasks = [1]
+ with Pool(processes=1) as pool:
+ pool.map(process_task, tasks)
+
+ def test_multitask_job():
+ multitask_job()
+ """)
+ result = testdir.runpytest(p1, "--tb=long")
+ result.stdout.fnmatch_lines([
+ "====* FAILURES *====",
+ "*multiprocessing.pool.RemoteTraceback:*",
+ "Traceback (most recent call last):",
+ "*assert n == 10",
+ "The above exception was the direct cause of the following exception:",
+ "> * multitask_job()",
+ ])
+
+
+@pytest.mark.skipif("'__pypy__' in sys.builtin_module_names or sys.platform.startswith('java')")
+def test_warn_missing(testdir):
+ testdir.makepyfile("")
+ result = testdir.run(sys.executable, "-OO", "-m", "pytest", "-h")
+ result.stderr.fnmatch_lines([
+ "*WARNING*assert statements are not executed*",
+ ])
+ result = testdir.run(sys.executable, "-OO", "-m", "pytest")
+ result.stderr.fnmatch_lines([
+ "*WARNING*assert statements are not executed*",
+ ])
+
+
+def test_recursion_source_decode(testdir):
+ testdir.makepyfile("""
+ def test_something():
+ pass
+ """)
+ testdir.makeini("""
+ [pytest]
+ python_files = *.py
+ """)
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines("""
+ <Module*>
+ """)
+
+
+def test_AssertionError_message(testdir):
+ testdir.makepyfile("""
+ def test_hello():
+ x,y = 1,2
+ assert 0, (x,y)
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ *def test_hello*
+ *assert 0, (x,y)*
+ *AssertionError: (1, 2)*
+ """)
+
+
+@pytest.mark.skipif(PY3, reason='This bug does not exist on PY3')
+def test_set_with_unsortable_elements():
+ # issue #718
+ class UnsortableKey(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __lt__(self, other):
+ raise RuntimeError()
+
+ def __repr__(self):
+ return 'repr({0})'.format(self.name)
+
+ def __eq__(self, other):
+ return self.name == other.name
+
+ def __hash__(self):
+ return hash(self.name)
+
+ left_set = set(UnsortableKey(str(i)) for i in range(1, 3))
+ right_set = set(UnsortableKey(str(i)) for i in range(2, 4))
+ expl = callequal(left_set, right_set, verbose=True)
+ # skip first line because it contains the "construction" of the set, which does not have a guaranteed order
+ expl = expl[1:]
+ dedent = textwrap.dedent("""
+ Extra items in the left set:
+ repr(1)
+ Extra items in the right set:
+ repr(3)
+ Full diff (fallback to calling repr on each item):
+ - repr(1)
+ repr(2)
+ + repr(3)
+ """).strip()
+ assert '\n'.join(expl) == dedent
+
+
+def test_diff_newline_at_end(monkeypatch, testdir):
+ testdir.makepyfile(r"""
+ def test_diff():
+ assert 'asdf' == 'asdf\n'
+ """)
+
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(r"""
+ *assert 'asdf' == 'asdf\n'
+ * - asdf
+ * + asdf
+ * ? +
+ """)
+
+
+def test_assert_tuple_warning(testdir):
+ testdir.makepyfile("""
+ def test_tuple():
+ assert(False, 'you shall not pass')
+ """)
+ result = testdir.runpytest('-rw')
+ result.stdout.fnmatch_lines([
+ '*test_assert_tuple_warning.py:2',
+ '*assertion is always true*',
+ ])
+
+
+def test_assert_indirect_tuple_no_warning(testdir):
+ testdir.makepyfile("""
+ def test_tuple():
+ tpl = ('foo', 'bar')
+ assert tpl
+ """)
+ result = testdir.runpytest('-rw')
+ output = '\n'.join(result.stdout.lines)
+ assert 'WR1' not in output
+
+
+def test_assert_with_unicode(monkeypatch, testdir):
+ testdir.makepyfile(u"""
+ # -*- coding: utf-8 -*-
+ def test_unicode():
+ assert u'유니코드' == u'Unicode'
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['*AssertionError*'])
+
+
+def test_raise_unprintable_assertion_error(testdir):
+ testdir.makepyfile(r"""
+ def test_raise_assertion_error():
+ raise AssertionError('\xff')
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([r"> raise AssertionError('\xff')", 'E AssertionError: *'])
+
+
+def test_raise_assertion_error_raisin_repr(testdir):
+ testdir.makepyfile(u"""
+ class RaisingRepr(object):
+ def __repr__(self):
+ raise Exception()
+ def test_raising_repr():
+ raise AssertionError(RaisingRepr())
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['E AssertionError: <unprintable AssertionError object>'])
+
+
+def test_issue_1944(testdir):
+ testdir.makepyfile("""
+ def f():
+ return
+
+ assert f() == 10
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["*1 error*"])
+ assert "AttributeError: 'Module' object has no attribute '_obj'" not in result.stdout.str()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_assertrewrite.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_assertrewrite.py
new file mode 100644
index 00000000000..0e22c6dac47
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_assertrewrite.py
@@ -0,0 +1,996 @@
+from __future__ import absolute_import, division, print_function
+
+import glob
+import os
+import py_compile
+import stat
+import sys
+import zipfile
+import py
+import pytest
+
+import _pytest._code
+from _pytest.assertion import util
+from _pytest.assertion.rewrite import rewrite_asserts, PYTEST_TAG, AssertionRewritingHook
+from _pytest.main import EXIT_NOTESTSCOLLECTED
+
+ast = pytest.importorskip("ast")
+if sys.platform.startswith("java"):
+ # XXX should be xfail
+ pytest.skip("assert rewrite does currently not work on jython")
+
+
+def setup_module(mod):
+ mod._old_reprcompare = util._reprcompare
+ _pytest._code._reprcompare = None
+
+
+def teardown_module(mod):
+ util._reprcompare = mod._old_reprcompare
+ del mod._old_reprcompare
+
+
+def rewrite(src):
+ tree = ast.parse(src)
+ rewrite_asserts(tree)
+ return tree
+
+
+def getmsg(f, extra_ns=None, must_pass=False):
+ """Rewrite the assertions in f, run it, and get the failure message."""
+ src = '\n'.join(_pytest._code.Code(f).source().lines)
+ mod = rewrite(src)
+ code = compile(mod, "<test>", "exec")
+ ns = {}
+ if extra_ns is not None:
+ ns.update(extra_ns)
+ py.builtin.exec_(code, ns)
+ func = ns[f.__name__]
+ try:
+ func()
+ except AssertionError:
+ if must_pass:
+ pytest.fail("shouldn't have raised")
+ s = str(sys.exc_info()[1])
+ if not s.startswith("assert"):
+ return "AssertionError: " + s
+ return s
+ else:
+ if not must_pass:
+ pytest.fail("function didn't raise at all")
+
+
+class TestAssertionRewrite(object):
+
+ def test_place_initial_imports(self):
+ s = """'Doc string'\nother = stuff"""
+ m = rewrite(s)
+ # Module docstrings in 3.7 are part of Module node, it's not in the body
+ # so we remove it so the following body items have the same indexes on
+ # all Python versions
+ if sys.version_info < (3, 7):
+ assert isinstance(m.body[0], ast.Expr)
+ assert isinstance(m.body[0].value, ast.Str)
+ del m.body[0]
+ for imp in m.body[0:2]:
+ assert isinstance(imp, ast.Import)
+ assert imp.lineno == 2
+ assert imp.col_offset == 0
+ assert isinstance(m.body[2], ast.Assign)
+ s = """from __future__ import with_statement\nother_stuff"""
+ m = rewrite(s)
+ assert isinstance(m.body[0], ast.ImportFrom)
+ for imp in m.body[1:3]:
+ assert isinstance(imp, ast.Import)
+ assert imp.lineno == 2
+ assert imp.col_offset == 0
+ assert isinstance(m.body[3], ast.Expr)
+ s = """'doc string'\nfrom __future__ import with_statement"""
+ m = rewrite(s)
+ if sys.version_info < (3, 7):
+ assert isinstance(m.body[0], ast.Expr)
+ assert isinstance(m.body[0].value, ast.Str)
+ del m.body[0]
+ assert isinstance(m.body[0], ast.ImportFrom)
+ for imp in m.body[1:3]:
+ assert isinstance(imp, ast.Import)
+ assert imp.lineno == 2
+ assert imp.col_offset == 0
+ s = """'doc string'\nfrom __future__ import with_statement\nother"""
+ m = rewrite(s)
+ if sys.version_info < (3, 7):
+ assert isinstance(m.body[0], ast.Expr)
+ assert isinstance(m.body[0].value, ast.Str)
+ del m.body[0]
+ assert isinstance(m.body[0], ast.ImportFrom)
+ for imp in m.body[1:3]:
+ assert isinstance(imp, ast.Import)
+ assert imp.lineno == 3
+ assert imp.col_offset == 0
+ assert isinstance(m.body[3], ast.Expr)
+ s = """from . import relative\nother_stuff"""
+ m = rewrite(s)
+ for imp in m.body[0:2]:
+ assert isinstance(imp, ast.Import)
+ assert imp.lineno == 1
+ assert imp.col_offset == 0
+ assert isinstance(m.body[3], ast.Expr)
+
+ def test_dont_rewrite(self):
+ s = """'PYTEST_DONT_REWRITE'\nassert 14"""
+ m = rewrite(s)
+ if sys.version_info < (3, 7):
+ assert len(m.body) == 2
+ assert isinstance(m.body[0], ast.Expr)
+ assert isinstance(m.body[0].value, ast.Str)
+ del m.body[0]
+ else:
+ assert len(m.body) == 1
+ assert m.body[0].msg is None
+
+ def test_name(self):
+ def f():
+ assert False
+ assert getmsg(f) == "assert False"
+
+ def f():
+ f = False
+ assert f
+
+ assert getmsg(f) == "assert False"
+
+ def f():
+ assert a_global # noqa
+
+ assert getmsg(f, {"a_global": False}) == "assert False"
+
+ def f():
+ assert sys == 42
+
+ assert getmsg(f, {"sys": sys}) == "assert sys == 42"
+
+ def f():
+ assert cls == 42 # noqa
+
+ class X(object):
+ pass
+
+ assert getmsg(f, {"cls": X}) == "assert cls == 42"
+
+ def test_assert_already_has_message(self):
+ def f():
+ assert False, "something bad!"
+ assert getmsg(f) == "AssertionError: something bad!\nassert False"
+
+ def test_assertion_message(self, testdir):
+ testdir.makepyfile("""
+ def test_foo():
+ assert 1 == 2, "The failure message"
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.stdout.fnmatch_lines([
+ "*AssertionError*The failure message*",
+ "*assert 1 == 2*",
+ ])
+
+ def test_assertion_message_multiline(self, testdir):
+ testdir.makepyfile("""
+ def test_foo():
+ assert 1 == 2, "A multiline\\nfailure message"
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.stdout.fnmatch_lines([
+ "*AssertionError*A multiline*",
+ "*failure message*",
+ "*assert 1 == 2*",
+ ])
+
+ def test_assertion_message_tuple(self, testdir):
+ testdir.makepyfile("""
+ def test_foo():
+ assert 1 == 2, (1, 2)
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.stdout.fnmatch_lines([
+ "*AssertionError*%s*" % repr((1, 2)),
+ "*assert 1 == 2*",
+ ])
+
+ def test_assertion_message_expr(self, testdir):
+ testdir.makepyfile("""
+ def test_foo():
+ assert 1 == 2, 1 + 2
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.stdout.fnmatch_lines([
+ "*AssertionError*3*",
+ "*assert 1 == 2*",
+ ])
+
+ def test_assertion_message_escape(self, testdir):
+ testdir.makepyfile("""
+ def test_foo():
+ assert 1 == 2, 'To be escaped: %'
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 1
+ result.stdout.fnmatch_lines([
+ "*AssertionError: To be escaped: %",
+ "*assert 1 == 2",
+ ])
+
+ def test_boolop(self):
+ def f():
+ f = g = False
+ assert f and g
+
+ assert getmsg(f) == "assert (False)"
+
+ def f():
+ f = True
+ g = False
+ assert f and g
+
+ assert getmsg(f) == "assert (True and False)"
+
+ def f():
+ f = False
+ g = True
+ assert f and g
+
+ assert getmsg(f) == "assert (False)"
+
+ def f():
+ f = g = False
+ assert f or g
+
+ assert getmsg(f) == "assert (False or False)"
+
+ def f():
+ f = g = False
+ assert not f and not g
+
+ getmsg(f, must_pass=True)
+
+ def x():
+ return False
+
+ def f():
+ assert x() and x()
+
+ assert getmsg(f, {"x": x}) == """assert (False)
+ + where False = x()"""
+
+ def f():
+ assert False or x()
+
+ assert getmsg(f, {"x": x}) == """assert (False or False)
+ + where False = x()"""
+
+ def f():
+ assert 1 in {} and 2 in {}
+
+ assert getmsg(f) == "assert (1 in {})"
+
+ def f():
+ x = 1
+ y = 2
+ assert x in {1: None} and y in {}
+
+ assert getmsg(f) == "assert (1 in {1: None} and 2 in {})"
+
+ def f():
+ f = True
+ g = False
+ assert f or g
+
+ getmsg(f, must_pass=True)
+
+ def f():
+ f = g = h = lambda: True
+ assert f() and g() and h()
+
+ getmsg(f, must_pass=True)
+
+ def test_short_circuit_evaluation(self):
+ def f():
+ assert True or explode # noqa
+
+ getmsg(f, must_pass=True)
+
+ def f():
+ x = 1
+ assert x == 1 or x == 2
+
+ getmsg(f, must_pass=True)
+
+ def test_unary_op(self):
+ def f():
+ x = True
+ assert not x
+
+ assert getmsg(f) == "assert not True"
+
+ def f():
+ x = 0
+ assert ~x + 1
+
+ assert getmsg(f) == "assert (~0 + 1)"
+
+ def f():
+ x = 3
+ assert -x + x
+
+ assert getmsg(f) == "assert (-3 + 3)"
+
+ def f():
+ x = 0
+ assert +x + x
+
+ assert getmsg(f) == "assert (+0 + 0)"
+
+ def test_binary_op(self):
+ def f():
+ x = 1
+ y = -1
+ assert x + y
+
+ assert getmsg(f) == "assert (1 + -1)"
+
+ def f():
+ assert not 5 % 4
+ assert getmsg(f) == "assert not (5 % 4)"
+
+ def test_boolop_percent(self):
+ def f():
+ assert 3 % 2 and False
+
+ assert getmsg(f) == "assert ((3 % 2) and False)"
+
+ def f():
+ assert False or 4 % 2
+ assert getmsg(f) == "assert (False or (4 % 2))"
+
+ @pytest.mark.skipif("sys.version_info < (3,5)")
+ def test_at_operator_issue1290(self, testdir):
+ testdir.makepyfile("""
+ class Matrix(object):
+ def __init__(self, num):
+ self.num = num
+ def __matmul__(self, other):
+ return self.num * other.num
+
+ def test_multmat_operator():
+ assert Matrix(2) @ Matrix(3) == 6""")
+ testdir.runpytest().assert_outcomes(passed=1)
+
+ def test_call(self):
+ def g(a=42, *args, **kwargs):
+ return False
+
+ ns = {"g": g}
+
+ def f():
+ assert g()
+
+ assert getmsg(f, ns) == """assert False
+ + where False = g()"""
+
+ def f():
+ assert g(1)
+
+ assert getmsg(f, ns) == """assert False
+ + where False = g(1)"""
+
+ def f():
+ assert g(1, 2)
+
+ assert getmsg(f, ns) == """assert False
+ + where False = g(1, 2)"""
+
+ def f():
+ assert g(1, g=42)
+
+ assert getmsg(f, ns) == """assert False
+ + where False = g(1, g=42)"""
+
+ def f():
+ assert g(1, 3, g=23)
+
+ assert getmsg(f, ns) == """assert False
+ + where False = g(1, 3, g=23)"""
+
+ def f():
+ seq = [1, 2, 3]
+ assert g(*seq)
+
+ assert getmsg(f, ns) == """assert False
+ + where False = g(*[1, 2, 3])"""
+
+ def f():
+ x = "a"
+ assert g(**{x: 2})
+
+ assert getmsg(f, ns) == """assert False
+ + where False = g(**{'a': 2})"""
+
+ def test_attribute(self):
+ class X(object):
+ g = 3
+
+ ns = {"x": X}
+
+ def f():
+ assert not x.g # noqa
+
+ assert getmsg(f, ns) == """assert not 3
+ + where 3 = x.g"""
+
+ def f():
+ x.a = False # noqa
+ assert x.a # noqa
+
+ assert getmsg(f, ns) == """assert False
+ + where False = x.a"""
+
+ def test_comparisons(self):
+
+ def f():
+ a, b = range(2)
+ assert b < a
+
+ assert getmsg(f) == """assert 1 < 0"""
+
+ def f():
+ a, b, c = range(3)
+ assert a > b > c
+
+ assert getmsg(f) == """assert 0 > 1"""
+
+ def f():
+ a, b, c = range(3)
+ assert a < b > c
+
+ assert getmsg(f) == """assert 1 > 2"""
+
+ def f():
+ a, b, c = range(3)
+ assert a < b <= c
+
+ getmsg(f, must_pass=True)
+
+ def f():
+ a, b, c = range(3)
+ assert a < b
+ assert b < c
+
+ getmsg(f, must_pass=True)
+
+ def test_len(self):
+
+ def f():
+ values = list(range(10))
+ assert len(values) == 11
+
+ assert getmsg(f).startswith("""assert 10 == 11
+ + where 10 = len([""")
+
+ def test_custom_reprcompare(self, monkeypatch):
+ def my_reprcompare(op, left, right):
+ return "42"
+
+ monkeypatch.setattr(util, "_reprcompare", my_reprcompare)
+
+ def f():
+ assert 42 < 3
+
+ assert getmsg(f) == "assert 42"
+
+ def my_reprcompare(op, left, right):
+ return "%s %s %s" % (left, op, right)
+
+ monkeypatch.setattr(util, "_reprcompare", my_reprcompare)
+
+ def f():
+ assert 1 < 3 < 5 <= 4 < 7
+
+ assert getmsg(f) == "assert 5 <= 4"
+
+ def test_assert_raising_nonzero_in_comparison(self):
+ def f():
+ class A(object):
+
+ def __nonzero__(self):
+ raise ValueError(42)
+
+ def __lt__(self, other):
+ return A()
+
+ def __repr__(self):
+ return "<MY42 object>"
+
+ def myany(x):
+ return False
+
+ assert myany(A() < 0)
+
+ assert "<MY42 object> < 0" in getmsg(f)
+
+ def test_formatchar(self):
+ def f():
+ assert "%test" == "test"
+
+ assert getmsg(f).startswith("assert '%test' == 'test'")
+
+ def test_custom_repr(self):
+ def f():
+ class Foo(object):
+ a = 1
+
+ def __repr__(self):
+ return "\n{ \n~ \n}"
+
+ f = Foo()
+ assert 0 == f.a
+
+ assert r"where 1 = \n{ \n~ \n}.a" in util._format_lines([getmsg(f)])[0]
+
+
+class TestRewriteOnImport(object):
+
+ def test_pycache_is_a_file(self, testdir):
+ testdir.tmpdir.join("__pycache__").write("Hello")
+ testdir.makepyfile("""
+ def test_rewritten():
+ assert "@py_builtins" in globals()""")
+ assert testdir.runpytest().ret == 0
+
+ def test_pycache_is_readonly(self, testdir):
+ cache = testdir.tmpdir.mkdir("__pycache__")
+ old_mode = cache.stat().mode
+ cache.chmod(old_mode ^ stat.S_IWRITE)
+ testdir.makepyfile("""
+ def test_rewritten():
+ assert "@py_builtins" in globals()""")
+ try:
+ assert testdir.runpytest().ret == 0
+ finally:
+ cache.chmod(old_mode)
+
+ def test_zipfile(self, testdir):
+ z = testdir.tmpdir.join("myzip.zip")
+ z_fn = str(z)
+ f = zipfile.ZipFile(z_fn, "w")
+ try:
+ f.writestr("test_gum/__init__.py", "")
+ f.writestr("test_gum/test_lizard.py", "")
+ finally:
+ f.close()
+ z.chmod(256)
+ testdir.makepyfile("""
+ import sys
+ sys.path.append(%r)
+ import test_gum.test_lizard""" % (z_fn,))
+ assert testdir.runpytest().ret == EXIT_NOTESTSCOLLECTED
+
+ def test_readonly(self, testdir):
+ sub = testdir.mkdir("testing")
+ sub.join("test_readonly.py").write(
+ py.builtin._totext("""
+def test_rewritten():
+ assert "@py_builtins" in globals()
+ """).encode("utf-8"), "wb")
+ old_mode = sub.stat().mode
+ sub.chmod(320)
+ try:
+ assert testdir.runpytest().ret == 0
+ finally:
+ sub.chmod(old_mode)
+
+ def test_dont_write_bytecode(self, testdir, monkeypatch):
+ testdir.makepyfile("""
+ import os
+ def test_no_bytecode():
+ assert "__pycache__" in __cached__
+ assert not os.path.exists(__cached__)
+ assert not os.path.exists(os.path.dirname(__cached__))""")
+ monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
+ assert testdir.runpytest_subprocess().ret == 0
+
+ def test_orphaned_pyc_file(self, testdir):
+ if sys.version_info < (3, 0) and hasattr(sys, 'pypy_version_info'):
+ pytest.skip("pypy2 doesn't run orphaned pyc files")
+
+ testdir.makepyfile("""
+ import orphan
+ def test_it():
+ assert orphan.value == 17
+ """)
+ testdir.makepyfile(orphan="""
+ value = 17
+ """)
+ py_compile.compile("orphan.py")
+ os.remove("orphan.py")
+
+ # Python 3 puts the .pyc files in a __pycache__ directory, and will
+ # not import from there without source. It will import a .pyc from
+ # the source location though.
+ if not os.path.exists("orphan.pyc"):
+ pycs = glob.glob("__pycache__/orphan.*.pyc")
+ assert len(pycs) == 1
+ os.rename(pycs[0], "orphan.pyc")
+
+ assert testdir.runpytest().ret == 0
+
+ @pytest.mark.skipif('"__pypy__" in sys.modules')
+ def test_pyc_vs_pyo(self, testdir, monkeypatch):
+ testdir.makepyfile("""
+ import pytest
+ def test_optimized():
+ "hello"
+ assert test_optimized.__doc__ is None"""
+ )
+ p = py.path.local.make_numbered_dir(prefix="runpytest-", keep=None,
+ rootdir=testdir.tmpdir)
+ tmp = "--basetemp=%s" % p
+ monkeypatch.setenv("PYTHONOPTIMIZE", "2")
+ monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
+ assert testdir.runpytest_subprocess(tmp).ret == 0
+ tagged = "test_pyc_vs_pyo." + PYTEST_TAG
+ assert tagged + ".pyo" in os.listdir("__pycache__")
+ monkeypatch.undo()
+ monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
+ assert testdir.runpytest_subprocess(tmp).ret == 1
+ assert tagged + ".pyc" in os.listdir("__pycache__")
+
+ def test_package(self, testdir):
+ pkg = testdir.tmpdir.join("pkg")
+ pkg.mkdir()
+ pkg.join("__init__.py").ensure()
+ pkg.join("test_blah.py").write("""
+def test_rewritten():
+ assert "@py_builtins" in globals()""")
+ assert testdir.runpytest().ret == 0
+
+ def test_translate_newlines(self, testdir):
+ content = "def test_rewritten():\r\n assert '@py_builtins' in globals()"
+ b = content.encode("utf-8")
+ testdir.tmpdir.join("test_newlines.py").write(b, "wb")
+ assert testdir.runpytest().ret == 0
+
+ @pytest.mark.skipif(sys.version_info < (3, 4),
+ reason='packages without __init__.py not supported on python 2')
+ def test_package_without__init__py(self, testdir):
+ pkg = testdir.mkdir('a_package_without_init_py')
+ pkg.join('module.py').ensure()
+ testdir.makepyfile("import a_package_without_init_py.module")
+ assert testdir.runpytest().ret == EXIT_NOTESTSCOLLECTED
+
+ def test_rewrite_warning(self, pytestconfig, monkeypatch):
+ hook = AssertionRewritingHook(pytestconfig)
+ warnings = []
+
+ def mywarn(code, msg):
+ warnings.append((code, msg))
+
+ monkeypatch.setattr(hook.config, 'warn', mywarn)
+ hook.mark_rewrite('_pytest')
+ assert '_pytest' in warnings[0][1]
+
+ def test_rewrite_module_imported_from_conftest(self, testdir):
+ testdir.makeconftest('''
+ import test_rewrite_module_imported
+ ''')
+ testdir.makepyfile(test_rewrite_module_imported='''
+ def test_rewritten():
+ assert "@py_builtins" in globals()
+ ''')
+ assert testdir.runpytest_subprocess().ret == 0
+
+ def test_remember_rewritten_modules(self, pytestconfig, testdir, monkeypatch):
+ """
+ AssertionRewriteHook should remember rewritten modules so it
+ doesn't give false positives (#2005).
+ """
+ monkeypatch.syspath_prepend(testdir.tmpdir)
+ testdir.makepyfile(test_remember_rewritten_modules='')
+ warnings = []
+ hook = AssertionRewritingHook(pytestconfig)
+ monkeypatch.setattr(hook.config, 'warn', lambda code, msg: warnings.append(msg))
+ hook.find_module('test_remember_rewritten_modules')
+ hook.load_module('test_remember_rewritten_modules')
+ hook.mark_rewrite('test_remember_rewritten_modules')
+ hook.mark_rewrite('test_remember_rewritten_modules')
+ assert warnings == []
+
+ def test_rewrite_warning_using_pytest_plugins(self, testdir):
+ testdir.makepyfile(**{
+ 'conftest.py': "pytest_plugins = ['core', 'gui', 'sci']",
+ 'core.py': "",
+ 'gui.py': "pytest_plugins = ['core', 'sci']",
+ 'sci.py': "pytest_plugins = ['core']",
+ 'test_rewrite_warning_pytest_plugins.py': "def test(): pass",
+ })
+ testdir.chdir()
+ result = testdir.runpytest_subprocess()
+ result.stdout.fnmatch_lines(['*= 1 passed in *=*'])
+ assert 'pytest-warning summary' not in result.stdout.str()
+
+ def test_rewrite_warning_using_pytest_plugins_env_var(self, testdir, monkeypatch):
+ monkeypatch.setenv('PYTEST_PLUGINS', 'plugin')
+ testdir.makepyfile(**{
+ 'plugin.py': "",
+ 'test_rewrite_warning_using_pytest_plugins_env_var.py': """
+ import plugin
+ pytest_plugins = ['plugin']
+ def test():
+ pass
+ """,
+ })
+ testdir.chdir()
+ result = testdir.runpytest_subprocess()
+ result.stdout.fnmatch_lines(['*= 1 passed in *=*'])
+ assert 'pytest-warning summary' not in result.stdout.str()
+
+ @pytest.mark.skipif(sys.version_info[0] > 2, reason='python 2 only')
+ def test_rewrite_future_imports(self, testdir):
+ """Test that rewritten modules don't inherit the __future__ flags
+ from the assertrewrite module.
+
+ assertion.rewrite imports __future__.division (and others), so
+ ensure rewritten modules don't inherit those flags.
+
+ The test below will fail if __future__.division is enabled
+ """
+ testdir.makepyfile('''
+ def test():
+ x = 1 / 2
+ assert type(x) is int
+ ''')
+ result = testdir.runpytest()
+ assert result.ret == 0
+
+
+class TestAssertionRewriteHookDetails(object):
+ def test_loader_is_package_false_for_module(self, testdir):
+ testdir.makepyfile(test_fun="""
+ def test_loader():
+ assert not __loader__.is_package(__name__)
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "* 1 passed*",
+ ])
+
+ def test_loader_is_package_true_for_package(self, testdir):
+ testdir.makepyfile(test_fun="""
+ def test_loader():
+ assert not __loader__.is_package(__name__)
+
+ def test_fun():
+ assert __loader__.is_package('fun')
+
+ def test_missing():
+ assert not __loader__.is_package('pytest_not_there')
+ """)
+ testdir.mkpydir('fun')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '* 3 passed*',
+ ])
+
+ @pytest.mark.skipif("sys.version_info[0] >= 3")
+ @pytest.mark.xfail("hasattr(sys, 'pypy_translation_info')")
+ def test_assume_ascii(self, testdir):
+ content = "u'\xe2\x99\xa5\x01\xfe'"
+ testdir.tmpdir.join("test_encoding.py").write(content, "wb")
+ res = testdir.runpytest()
+ assert res.ret != 0
+ assert "SyntaxError: Non-ASCII character" in res.stdout.str()
+
+ @pytest.mark.skipif("sys.version_info[0] >= 3")
+ def test_detect_coding_cookie(self, testdir):
+ testdir.makepyfile(test_cookie="""
+ # -*- coding: utf-8 -*-
+ u"St\xc3\xa4d"
+ def test_rewritten():
+ assert "@py_builtins" in globals()""")
+ assert testdir.runpytest().ret == 0
+
+ @pytest.mark.skipif("sys.version_info[0] >= 3")
+ def test_detect_coding_cookie_second_line(self, testdir):
+ testdir.makepyfile(test_cookie="""
+ # -*- coding: utf-8 -*-
+ u"St\xc3\xa4d"
+ def test_rewritten():
+ assert "@py_builtins" in globals()""")
+ assert testdir.runpytest().ret == 0
+
+ @pytest.mark.skipif("sys.version_info[0] >= 3")
+ def test_detect_coding_cookie_crlf(self, testdir):
+ testdir.makepyfile(test_cookie="""
+ # -*- coding: utf-8 -*-
+ u"St\xc3\xa4d"
+ def test_rewritten():
+ assert "@py_builtins" in globals()""")
+ assert testdir.runpytest().ret == 0
+
+ def test_sys_meta_path_munged(self, testdir):
+ testdir.makepyfile("""
+ def test_meta_path():
+ import sys; sys.meta_path = []""")
+ assert testdir.runpytest().ret == 0
+
+ def test_write_pyc(self, testdir, tmpdir, monkeypatch):
+ from _pytest.assertion.rewrite import _write_pyc
+ from _pytest.assertion import AssertionState
+ try:
+ import __builtin__ as b
+ except ImportError:
+ import builtins as b
+ config = testdir.parseconfig([])
+ state = AssertionState(config, "rewrite")
+ source_path = tmpdir.ensure("source.py")
+ pycpath = tmpdir.join("pyc").strpath
+ assert _write_pyc(state, [1], source_path.stat(), pycpath)
+
+ def open(*args):
+ e = IOError()
+ e.errno = 10
+ raise e
+
+ monkeypatch.setattr(b, "open", open)
+ assert not _write_pyc(state, [1], source_path.stat(), pycpath)
+
+ def test_resources_provider_for_loader(self, testdir):
+ """
+ Attempts to load resources from a package should succeed normally,
+ even when the AssertionRewriteHook is used to load the modules.
+
+ See #366 for details.
+ """
+ pytest.importorskip("pkg_resources")
+
+ testdir.mkpydir('testpkg')
+ contents = {
+ 'testpkg/test_pkg': """
+ import pkg_resources
+
+ import pytest
+ from _pytest.assertion.rewrite import AssertionRewritingHook
+
+ def test_load_resource():
+ assert isinstance(__loader__, AssertionRewritingHook)
+ res = pkg_resources.resource_string(__name__, 'resource.txt')
+ res = res.decode('ascii')
+ assert res == 'Load me please.'
+ """,
+ }
+ testdir.makepyfile(**contents)
+ testdir.maketxtfile(**{'testpkg/resource': "Load me please."})
+
+ result = testdir.runpytest_subprocess()
+ result.assert_outcomes(passed=1)
+
+ def test_read_pyc(self, tmpdir):
+ """
+ Ensure that the `_read_pyc` can properly deal with corrupted pyc files.
+ In those circumstances it should just give up instead of generating
+ an exception that is propagated to the caller.
+ """
+ import py_compile
+ from _pytest.assertion.rewrite import _read_pyc
+
+ source = tmpdir.join('source.py')
+ pyc = source + 'c'
+
+ source.write('def test(): pass')
+ py_compile.compile(str(source), str(pyc))
+
+ contents = pyc.read(mode='rb')
+ strip_bytes = 20 # header is around 8 bytes, strip a little more
+ assert len(contents) > strip_bytes
+ pyc.write(contents[:strip_bytes], mode='wb')
+
+ assert _read_pyc(source, str(pyc)) is None # no error
+
+ def test_reload_is_same(self, testdir):
+ # A file that will be picked up during collecting.
+ testdir.tmpdir.join("file.py").ensure()
+ testdir.tmpdir.join("pytest.ini").write(py.std.textwrap.dedent("""
+ [pytest]
+ python_files = *.py
+ """))
+
+ testdir.makepyfile(test_fun="""
+ import sys
+ try:
+ from imp import reload
+ except ImportError:
+ pass
+
+ def test_loader():
+ import file
+ assert sys.modules["file"] is reload(file)
+ """)
+ result = testdir.runpytest('-s')
+ result.stdout.fnmatch_lines([
+ "* 1 passed*",
+ ])
+
+ def test_get_data_support(self, testdir):
+ """Implement optional PEP302 api (#808).
+ """
+ path = testdir.mkpydir("foo")
+ path.join("test_foo.py").write(_pytest._code.Source("""
+ class Test(object):
+ def test_foo(self):
+ import pkgutil
+ data = pkgutil.get_data('foo.test_foo', 'data.txt')
+ assert data == b'Hey'
+ """))
+ path.join('data.txt').write('Hey')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('*1 passed*')
+
+
+def test_issue731(testdir):
+ testdir.makepyfile("""
+ class LongReprWithBraces(object):
+ def __repr__(self):
+ return 'LongReprWithBraces({' + ('a' * 80) + '}' + ('a' * 120) + ')'
+
+ def some_method(self):
+ return False
+
+ def test_long_repr():
+ obj = LongReprWithBraces()
+ assert obj.some_method()
+ """)
+ result = testdir.runpytest()
+ assert 'unbalanced braces' not in result.stdout.str()
+
+
+class TestIssue925(object):
+ def test_simple_case(self, testdir):
+ testdir.makepyfile("""
+ def test_ternary_display():
+ assert (False == False) == False
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('*E*assert (False == False) == False')
+
+ def test_long_case(self, testdir):
+ testdir.makepyfile("""
+ def test_ternary_display():
+ assert False == (False == True) == True
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('*E*assert (False == True) == True')
+
+ def test_many_brackets(self, testdir):
+ testdir.makepyfile("""
+ def test_ternary_display():
+ assert True == ((False == True) == True)
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('*E*assert True == ((False == True) == True)')
+
+
+class TestIssue2121():
+ def test_simple(self, testdir):
+ testdir.tmpdir.join("tests/file.py").ensure().write("""
+def test_simple_failure():
+ assert 1 + 1 == 3
+""")
+ testdir.tmpdir.join("pytest.ini").write(py.std.textwrap.dedent("""
+ [pytest]
+ python_files = tests/**.py
+ """))
+
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('*E*assert (1 + 1) == 3')
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_cache.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_cache.py
new file mode 100755
index 00000000000..a37170cdd2b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_cache.py
@@ -0,0 +1,605 @@
+from __future__ import absolute_import, division, print_function
+import sys
+import py
+import _pytest
+import pytest
+import os
+import shutil
+
+pytest_plugins = "pytester",
+
+
+class TestNewAPI(object):
+ def test_config_cache_makedir(self, testdir):
+ testdir.makeini("[pytest]")
+ config = testdir.parseconfigure()
+ with pytest.raises(ValueError):
+ config.cache.makedir("key/name")
+
+ p = config.cache.makedir("name")
+ assert p.check()
+
+ def test_config_cache_dataerror(self, testdir):
+ testdir.makeini("[pytest]")
+ config = testdir.parseconfigure()
+ cache = config.cache
+ pytest.raises(TypeError, lambda: cache.set("key/name", cache))
+ config.cache.set("key/name", 0)
+ config.cache._getvaluepath("key/name").write("123invalid")
+ val = config.cache.get("key/name", -2)
+ assert val == -2
+
+ def test_cache_writefail_cachfile_silent(self, testdir):
+ testdir.makeini("[pytest]")
+ testdir.tmpdir.join('.cache').write('gone wrong')
+ config = testdir.parseconfigure()
+ cache = config.cache
+ cache.set('test/broken', [])
+
+ @pytest.mark.skipif(sys.platform.startswith('win'), reason='no chmod on windows')
+ def test_cache_writefail_permissions(self, testdir):
+ testdir.makeini("[pytest]")
+ testdir.tmpdir.ensure_dir('.cache').chmod(0)
+ config = testdir.parseconfigure()
+ cache = config.cache
+ cache.set('test/broken', [])
+
+ @pytest.mark.skipif(sys.platform.startswith('win'), reason='no chmod on windows')
+ def test_cache_failure_warns(self, testdir):
+ testdir.tmpdir.ensure_dir('.cache').chmod(0)
+ testdir.makepyfile("""
+ def test_error():
+ raise Exception
+
+ """)
+ result = testdir.runpytest('-rw')
+ assert result.ret == 1
+ result.stdout.fnmatch_lines([
+ "*could not create cache path*",
+ "*1 warnings*",
+ ])
+
+ def test_config_cache(self, testdir):
+ testdir.makeconftest("""
+ def pytest_configure(config):
+ # see that we get cache information early on
+ assert hasattr(config, "cache")
+ """)
+ testdir.makepyfile("""
+ def test_session(pytestconfig):
+ assert hasattr(pytestconfig, "cache")
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 0
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_cachefuncarg(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def test_cachefuncarg(cache):
+ val = cache.get("some/thing", None)
+ assert val is None
+ cache.set("some/thing", [1])
+ pytest.raises(TypeError, lambda: cache.get("some/thing"))
+ val = cache.get("some/thing", [])
+ assert val == [1]
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 0
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_custom_rel_cache_dir(self, testdir):
+ rel_cache_dir = os.path.join('custom_cache_dir', 'subdir')
+ testdir.makeini("""
+ [pytest]
+ cache_dir = {cache_dir}
+ """.format(cache_dir=rel_cache_dir))
+ testdir.makepyfile(test_errored='def test_error():\n assert False')
+ testdir.runpytest()
+ assert testdir.tmpdir.join(rel_cache_dir).isdir()
+
+ def test_custom_abs_cache_dir(self, testdir, tmpdir_factory):
+ tmp = str(tmpdir_factory.mktemp('tmp'))
+ abs_cache_dir = os.path.join(tmp, 'custom_cache_dir')
+ testdir.makeini("""
+ [pytest]
+ cache_dir = {cache_dir}
+ """.format(cache_dir=abs_cache_dir))
+ testdir.makepyfile(test_errored='def test_error():\n assert False')
+ testdir.runpytest()
+ assert py.path.local(abs_cache_dir).isdir()
+
+ def test_custom_cache_dir_with_env_var(self, testdir, monkeypatch):
+ monkeypatch.setenv('env_var', 'custom_cache_dir')
+ testdir.makeini("""
+ [pytest]
+ cache_dir = {cache_dir}
+ """.format(cache_dir='$env_var'))
+ testdir.makepyfile(test_errored='def test_error():\n assert False')
+ testdir.runpytest()
+ assert testdir.tmpdir.join('custom_cache_dir').isdir()
+
+
+def test_cache_reportheader(testdir):
+ testdir.makepyfile("""
+ def test_hello():
+ pass
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines([
+ "cachedir: .cache"
+ ])
+
+
+def test_cache_show(testdir):
+ result = testdir.runpytest("--cache-show")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*cache is empty*"
+ ])
+ testdir.makeconftest("""
+ def pytest_configure(config):
+ config.cache.set("my/name", [1,2,3])
+ config.cache.set("other/some", {1:2})
+ dp = config.cache.makedir("mydb")
+ dp.ensure("hello")
+ dp.ensure("world")
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 5 # no tests executed
+ result = testdir.runpytest("--cache-show")
+ result.stdout.fnmatch_lines_random([
+ "*cachedir:*",
+ "-*cache values*-",
+ "*my/name contains:",
+ " [1, 2, 3]",
+ "*other/some contains*",
+ " {*1*: 2}",
+ "-*cache directories*-",
+ "*mydb/hello*length 0*",
+ "*mydb/world*length 0*",
+ ])
+
+
+class TestLastFailed(object):
+
+ def test_lastfailed_usecase(self, testdir, monkeypatch):
+ monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
+ p = testdir.makepyfile("""
+ def test_1():
+ assert 0
+ def test_2():
+ assert 0
+ def test_3():
+ assert 1
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*2 failed*",
+ ])
+ p.write(_pytest._code.Source("""
+ def test_1():
+ assert 1
+
+ def test_2():
+ assert 1
+
+ def test_3():
+ assert 0
+ """))
+ result = testdir.runpytest("--lf")
+ result.stdout.fnmatch_lines([
+ "*2 passed*1 desel*",
+ ])
+ result = testdir.runpytest("--lf")
+ result.stdout.fnmatch_lines([
+ "*1 failed*2 passed*",
+ ])
+ result = testdir.runpytest("--lf", "--cache-clear")
+ result.stdout.fnmatch_lines([
+ "*1 failed*2 passed*",
+ ])
+
+ # Run this again to make sure clear-cache is robust
+ if os.path.isdir('.cache'):
+ shutil.rmtree('.cache')
+ result = testdir.runpytest("--lf", "--cache-clear")
+ result.stdout.fnmatch_lines([
+ "*1 failed*2 passed*",
+ ])
+
+ def test_failedfirst_order(self, testdir):
+ testdir.tmpdir.join('test_a.py').write(_pytest._code.Source("""
+ def test_always_passes():
+ assert 1
+ """))
+ testdir.tmpdir.join('test_b.py').write(_pytest._code.Source("""
+ def test_always_fails():
+ assert 0
+ """))
+ result = testdir.runpytest()
+ # Test order will be collection order; alphabetical
+ result.stdout.fnmatch_lines([
+ "test_a.py*",
+ "test_b.py*",
+ ])
+ result = testdir.runpytest("--ff")
+ # Test order will be failing tests firs
+ result.stdout.fnmatch_lines([
+ "test_b.py*",
+ "test_a.py*",
+ ])
+
+ def test_lastfailed_failedfirst_order(self, testdir):
+ testdir.makepyfile(**{
+ 'test_a.py': """
+ def test_always_passes():
+ assert 1
+ """,
+ 'test_b.py': """
+ def test_always_fails():
+ assert 0
+ """,
+ })
+ result = testdir.runpytest()
+ # Test order will be collection order; alphabetical
+ result.stdout.fnmatch_lines([
+ "test_a.py*",
+ "test_b.py*",
+ ])
+ result = testdir.runpytest("--lf", "--ff")
+ # Test order will be failing tests firs
+ result.stdout.fnmatch_lines([
+ "test_b.py*",
+ ])
+ assert 'test_a.py' not in result.stdout.str()
+
+ def test_lastfailed_difference_invocations(self, testdir, monkeypatch):
+ monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
+ testdir.makepyfile(test_a="""
+ def test_a1():
+ assert 0
+ def test_a2():
+ assert 1
+ """, test_b="""
+ def test_b1():
+ assert 0
+ """)
+ p = testdir.tmpdir.join("test_a.py")
+ p2 = testdir.tmpdir.join("test_b.py")
+
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*2 failed*",
+ ])
+ result = testdir.runpytest("--lf", p2)
+ result.stdout.fnmatch_lines([
+ "*1 failed*",
+ ])
+ p2.write(_pytest._code.Source("""
+ def test_b1():
+ assert 1
+ """))
+ result = testdir.runpytest("--lf", p2)
+ result.stdout.fnmatch_lines([
+ "*1 passed*",
+ ])
+ result = testdir.runpytest("--lf", p)
+ result.stdout.fnmatch_lines([
+ "*1 failed*1 desel*",
+ ])
+
+ def test_lastfailed_usecase_splice(self, testdir, monkeypatch):
+ monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
+ testdir.makepyfile("""
+ def test_1():
+ assert 0
+ """)
+ p2 = testdir.tmpdir.join("test_something.py")
+ p2.write(_pytest._code.Source("""
+ def test_2():
+ assert 0
+ """))
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*2 failed*",
+ ])
+ result = testdir.runpytest("--lf", p2)
+ result.stdout.fnmatch_lines([
+ "*1 failed*",
+ ])
+ result = testdir.runpytest("--lf")
+ result.stdout.fnmatch_lines([
+ "*2 failed*",
+ ])
+
+ def test_lastfailed_xpass(self, testdir):
+ testdir.inline_runsource("""
+ import pytest
+ @pytest.mark.xfail
+ def test_hello():
+ assert 1
+ """)
+ config = testdir.parseconfigure()
+ lastfailed = config.cache.get("cache/lastfailed", -1)
+ assert lastfailed == -1
+
+ def test_non_serializable_parametrize(self, testdir):
+ """Test that failed parametrized tests with unmarshable parameters
+ don't break pytest-cache.
+ """
+ testdir.makepyfile(r"""
+ import pytest
+
+ @pytest.mark.parametrize('val', [
+ b'\xac\x10\x02G',
+ ])
+ def test_fail(val):
+ assert False
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('*1 failed in*')
+
+ def test_terminal_report_lastfailed(self, testdir):
+ test_a = testdir.makepyfile(test_a="""
+ def test_a1():
+ pass
+ def test_a2():
+ pass
+ """)
+ test_b = testdir.makepyfile(test_b="""
+ def test_b1():
+ assert 0
+ def test_b2():
+ assert 0
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ 'collected 4 items',
+ '*2 failed, 2 passed in*',
+ ])
+
+ result = testdir.runpytest('--lf')
+ result.stdout.fnmatch_lines([
+ 'collected 4 items',
+ 'run-last-failure: rerun previous 2 failures',
+ '*2 failed, 2 deselected in*',
+ ])
+
+ result = testdir.runpytest(test_a, '--lf')
+ result.stdout.fnmatch_lines([
+ 'collected 2 items',
+ 'run-last-failure: run all (no recorded failures)',
+ '*2 passed in*',
+ ])
+
+ result = testdir.runpytest(test_b, '--lf')
+ result.stdout.fnmatch_lines([
+ 'collected 2 items',
+ 'run-last-failure: rerun previous 2 failures',
+ '*2 failed in*',
+ ])
+
+ result = testdir.runpytest('test_b.py::test_b1', '--lf')
+ result.stdout.fnmatch_lines([
+ 'collected 1 item',
+ 'run-last-failure: rerun previous 1 failure',
+ '*1 failed in*',
+ ])
+
+ def test_terminal_report_failedfirst(self, testdir):
+ testdir.makepyfile(test_a="""
+ def test_a1():
+ assert 0
+ def test_a2():
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ 'collected 2 items',
+ '*1 failed, 1 passed in*',
+ ])
+
+ result = testdir.runpytest('--ff')
+ result.stdout.fnmatch_lines([
+ 'collected 2 items',
+ 'run-last-failure: rerun previous 1 failure first',
+ '*1 failed, 1 passed in*',
+ ])
+
+ def test_lastfailed_collectfailure(self, testdir, monkeypatch):
+
+ testdir.makepyfile(test_maybe="""
+ import py
+ env = py.std.os.environ
+ if '1' == env['FAILIMPORT']:
+ raise ImportError('fail')
+ def test_hello():
+ assert '0' == env['FAILTEST']
+ """)
+
+ def rlf(fail_import, fail_run):
+ monkeypatch.setenv('FAILIMPORT', fail_import)
+ monkeypatch.setenv('FAILTEST', fail_run)
+
+ testdir.runpytest('-q')
+ config = testdir.parseconfigure()
+ lastfailed = config.cache.get("cache/lastfailed", -1)
+ return lastfailed
+
+ lastfailed = rlf(fail_import=0, fail_run=0)
+ assert lastfailed == -1
+
+ lastfailed = rlf(fail_import=1, fail_run=0)
+ assert list(lastfailed) == ['test_maybe.py']
+
+ lastfailed = rlf(fail_import=0, fail_run=1)
+ assert list(lastfailed) == ['test_maybe.py::test_hello']
+
+ def test_lastfailed_failure_subset(self, testdir, monkeypatch):
+
+ testdir.makepyfile(test_maybe="""
+ import py
+ env = py.std.os.environ
+ if '1' == env['FAILIMPORT']:
+ raise ImportError('fail')
+ def test_hello():
+ assert '0' == env['FAILTEST']
+ """)
+
+ testdir.makepyfile(test_maybe2="""
+ import py
+ env = py.std.os.environ
+ if '1' == env['FAILIMPORT']:
+ raise ImportError('fail')
+ def test_hello():
+ assert '0' == env['FAILTEST']
+
+ def test_pass():
+ pass
+ """)
+
+ def rlf(fail_import, fail_run, args=()):
+ monkeypatch.setenv('FAILIMPORT', fail_import)
+ monkeypatch.setenv('FAILTEST', fail_run)
+
+ result = testdir.runpytest('-q', '--lf', *args)
+ config = testdir.parseconfigure()
+ lastfailed = config.cache.get("cache/lastfailed", -1)
+ return result, lastfailed
+
+ result, lastfailed = rlf(fail_import=0, fail_run=0)
+ assert lastfailed == -1
+ result.stdout.fnmatch_lines([
+ '*3 passed*',
+ ])
+
+ result, lastfailed = rlf(fail_import=1, fail_run=0)
+ assert sorted(list(lastfailed)) == ['test_maybe.py', 'test_maybe2.py']
+
+ result, lastfailed = rlf(fail_import=0, fail_run=0,
+ args=('test_maybe2.py',))
+ assert list(lastfailed) == ['test_maybe.py']
+
+ # edge case of test selection - even if we remember failures
+ # from other tests we still need to run all tests if no test
+ # matches the failures
+ result, lastfailed = rlf(fail_import=0, fail_run=0,
+ args=('test_maybe2.py',))
+ assert list(lastfailed) == ['test_maybe.py']
+ result.stdout.fnmatch_lines([
+ '*2 passed*',
+ ])
+
+ def test_lastfailed_creates_cache_when_needed(self, testdir):
+ # Issue #1342
+ testdir.makepyfile(test_empty='')
+ testdir.runpytest('-q', '--lf')
+ assert not os.path.exists('.cache')
+
+ testdir.makepyfile(test_successful='def test_success():\n assert True')
+ testdir.runpytest('-q', '--lf')
+ assert not os.path.exists('.cache')
+
+ testdir.makepyfile(test_errored='def test_error():\n assert False')
+ testdir.runpytest('-q', '--lf')
+ assert os.path.exists('.cache')
+
+ def test_xfail_not_considered_failure(self, testdir):
+ testdir.makepyfile('''
+ import pytest
+ @pytest.mark.xfail
+ def test():
+ assert 0
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('*1 xfailed*')
+ assert self.get_cached_last_failed(testdir) == []
+
+ def test_xfail_strict_considered_failure(self, testdir):
+ testdir.makepyfile('''
+ import pytest
+ @pytest.mark.xfail(strict=True)
+ def test():
+ pass
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('*1 failed*')
+ assert self.get_cached_last_failed(testdir) == ['test_xfail_strict_considered_failure.py::test']
+
+ @pytest.mark.parametrize('mark', ['mark.xfail', 'mark.skip'])
+ def test_failed_changed_to_xfail_or_skip(self, testdir, mark):
+ testdir.makepyfile('''
+ import pytest
+ def test():
+ assert 0
+ ''')
+ result = testdir.runpytest()
+ assert self.get_cached_last_failed(testdir) == ['test_failed_changed_to_xfail_or_skip.py::test']
+ assert result.ret == 1
+
+ testdir.makepyfile('''
+ import pytest
+ @pytest.{mark}
+ def test():
+ assert 0
+ '''.format(mark=mark))
+ result = testdir.runpytest()
+ assert result.ret == 0
+ assert self.get_cached_last_failed(testdir) == []
+ assert result.ret == 0
+
+ def get_cached_last_failed(self, testdir):
+ config = testdir.parseconfigure()
+ return sorted(config.cache.get("cache/lastfailed", {}))
+
+ def test_cache_cumulative(self, testdir):
+ """
+ Test workflow where user fixes errors gradually file by file using --lf.
+ """
+ # 1. initial run
+ test_bar = testdir.makepyfile(test_bar="""
+ def test_bar_1():
+ pass
+ def test_bar_2():
+ assert 0
+ """)
+ test_foo = testdir.makepyfile(test_foo="""
+ def test_foo_3():
+ pass
+ def test_foo_4():
+ assert 0
+ """)
+ testdir.runpytest()
+ assert self.get_cached_last_failed(testdir) == ['test_bar.py::test_bar_2', 'test_foo.py::test_foo_4']
+
+ # 2. fix test_bar_2, run only test_bar.py
+ testdir.makepyfile(test_bar="""
+ def test_bar_1():
+ pass
+ def test_bar_2():
+ pass
+ """)
+ result = testdir.runpytest(test_bar)
+ result.stdout.fnmatch_lines('*2 passed*')
+ # ensure cache does not forget that test_foo_4 failed once before
+ assert self.get_cached_last_failed(testdir) == ['test_foo.py::test_foo_4']
+
+ result = testdir.runpytest('--last-failed')
+ result.stdout.fnmatch_lines('*1 failed, 3 deselected*')
+ assert self.get_cached_last_failed(testdir) == ['test_foo.py::test_foo_4']
+
+ # 3. fix test_foo_4, run only test_foo.py
+ test_foo = testdir.makepyfile(test_foo="""
+ def test_foo_3():
+ pass
+ def test_foo_4():
+ pass
+ """)
+ result = testdir.runpytest(test_foo, '--last-failed')
+ result.stdout.fnmatch_lines('*1 passed, 1 deselected*')
+ assert self.get_cached_last_failed(testdir) == []
+
+ result = testdir.runpytest('--last-failed')
+ result.stdout.fnmatch_lines('*4 passed*')
+ assert self.get_cached_last_failed(testdir) == []
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_capture.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_capture.py
new file mode 100644
index 00000000000..f769a725dc4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_capture.py
@@ -0,0 +1,1274 @@
+from __future__ import absolute_import, division, print_function
+# note: py.io capture tests where copied from
+# pylib 1.4.20.dev2 (rev 13d9af95547e)
+from __future__ import with_statement
+import pickle
+import os
+import sys
+from io import UnsupportedOperation
+
+import _pytest._code
+import py
+import pytest
+import contextlib
+
+from _pytest import capture
+from _pytest.capture import CaptureManager
+from _pytest.main import EXIT_NOTESTSCOLLECTED
+
+
+needsosdup = pytest.mark.xfail("not hasattr(os, 'dup')")
+
+if sys.version_info >= (3, 0):
+ def tobytes(obj):
+ if isinstance(obj, str):
+ obj = obj.encode('UTF-8')
+ assert isinstance(obj, bytes)
+ return obj
+
+ def totext(obj):
+ if isinstance(obj, bytes):
+ obj = str(obj, 'UTF-8')
+ assert isinstance(obj, str)
+ return obj
+else:
+ def tobytes(obj):
+ if isinstance(obj, unicode):
+ obj = obj.encode('UTF-8')
+ assert isinstance(obj, str)
+ return obj
+
+ def totext(obj):
+ if isinstance(obj, str):
+ obj = unicode(obj, 'UTF-8')
+ assert isinstance(obj, unicode)
+ return obj
+
+
+def oswritebytes(fd, obj):
+ os.write(fd, tobytes(obj))
+
+
+def StdCaptureFD(out=True, err=True, in_=True):
+ return capture.MultiCapture(out, err, in_, Capture=capture.FDCapture)
+
+
+def StdCapture(out=True, err=True, in_=True):
+ return capture.MultiCapture(out, err, in_, Capture=capture.SysCapture)
+
+
+class TestCaptureManager(object):
+ def test_getmethod_default_no_fd(self, monkeypatch):
+ from _pytest.capture import pytest_addoption
+ from _pytest.config import Parser
+ parser = Parser()
+ pytest_addoption(parser)
+ default = parser._groups[0].options[0].default
+ assert default == "fd" if hasattr(os, "dup") else "sys"
+ parser = Parser()
+ monkeypatch.delattr(os, 'dup', raising=False)
+ pytest_addoption(parser)
+ assert parser._groups[0].options[0].default == "sys"
+
+ @needsosdup
+ @pytest.mark.parametrize("method",
+ ['no', 'sys', pytest.mark.skipif('not hasattr(os, "dup")', 'fd')])
+ def test_capturing_basic_api(self, method):
+ capouter = StdCaptureFD()
+ old = sys.stdout, sys.stderr, sys.stdin
+ try:
+ capman = CaptureManager(method)
+ capman.start_global_capturing()
+ outerr = capman.suspend_global_capture()
+ assert outerr == ("", "")
+ outerr = capman.suspend_global_capture()
+ assert outerr == ("", "")
+ print("hello")
+ out, err = capman.suspend_global_capture()
+ if method == "no":
+ assert old == (sys.stdout, sys.stderr, sys.stdin)
+ else:
+ assert not out
+ capman.resume_global_capture()
+ print("hello")
+ out, err = capman.suspend_global_capture()
+ if method != "no":
+ assert out == "hello\n"
+ capman.stop_global_capturing()
+ finally:
+ capouter.stop_capturing()
+
+ @needsosdup
+ def test_init_capturing(self):
+ capouter = StdCaptureFD()
+ try:
+ capman = CaptureManager("fd")
+ capman.start_global_capturing()
+ pytest.raises(AssertionError, "capman.start_global_capturing()")
+ capman.stop_global_capturing()
+ finally:
+ capouter.stop_capturing()
+
+
+@pytest.mark.parametrize("method", ['fd', 'sys'])
+def test_capturing_unicode(testdir, method):
+ if hasattr(sys, "pypy_version_info") and sys.pypy_version_info < (2, 2):
+ pytest.xfail("does not work on pypy < 2.2")
+ if sys.version_info >= (3, 0):
+ obj = "'b\u00f6y'"
+ else:
+ obj = "u'\u00f6y'"
+ testdir.makepyfile("""
+ # coding=utf8
+ # taken from issue 227 from nosetests
+ def test_unicode():
+ import sys
+ print (sys.stdout)
+ print (%s)
+ """ % obj)
+ result = testdir.runpytest("--capture=%s" % method)
+ result.stdout.fnmatch_lines([
+ "*1 passed*"
+ ])
+
+
+@pytest.mark.parametrize("method", ['fd', 'sys'])
+def test_capturing_bytes_in_utf8_encoding(testdir, method):
+ testdir.makepyfile("""
+ def test_unicode():
+ print ('b\\u00f6y')
+ """)
+ result = testdir.runpytest("--capture=%s" % method)
+ result.stdout.fnmatch_lines([
+ "*1 passed*"
+ ])
+
+
+def test_collect_capturing(testdir):
+ p = testdir.makepyfile("""
+ print ("collect %s failure" % 13)
+ import xyz42123
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*Captured stdout*",
+ "*collect 13 failure*",
+ ])
+
+
+class TestPerTestCapturing(object):
+ def test_capture_and_fixtures(self, testdir):
+ p = testdir.makepyfile("""
+ def setup_module(mod):
+ print ("setup module")
+ def setup_function(function):
+ print ("setup " + function.__name__)
+ def test_func1():
+ print ("in func1")
+ assert 0
+ def test_func2():
+ print ("in func2")
+ assert 0
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "setup module*",
+ "setup test_func1*",
+ "in func1*",
+ "setup test_func2*",
+ "in func2*",
+ ])
+
+ @pytest.mark.xfail(reason="unimplemented feature")
+ def test_capture_scope_cache(self, testdir):
+ p = testdir.makepyfile("""
+ import sys
+ def setup_module(func):
+ print ("module-setup")
+ def setup_function(func):
+ print ("function-setup")
+ def test_func():
+ print ("in function")
+ assert 0
+ def teardown_function(func):
+ print ("in teardown")
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*test_func():*",
+ "*Captured stdout during setup*",
+ "module-setup*",
+ "function-setup*",
+ "*Captured stdout*",
+ "in teardown*",
+ ])
+
+ def test_no_carry_over(self, testdir):
+ p = testdir.makepyfile("""
+ def test_func1():
+ print ("in func1")
+ def test_func2():
+ print ("in func2")
+ assert 0
+ """)
+ result = testdir.runpytest(p)
+ s = result.stdout.str()
+ assert "in func1" not in s
+ assert "in func2" in s
+
+ def test_teardown_capturing(self, testdir):
+ p = testdir.makepyfile("""
+ def setup_function(function):
+ print ("setup func1")
+ def teardown_function(function):
+ print ("teardown func1")
+ assert 0
+ def test_func1():
+ print ("in func1")
+ pass
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ '*teardown_function*',
+ '*Captured stdout*',
+ "setup func1*",
+ "in func1*",
+ "teardown func1*",
+ # "*1 fixture failure*"
+ ])
+
+ def test_teardown_capturing_final(self, testdir):
+ p = testdir.makepyfile("""
+ def teardown_module(mod):
+ print ("teardown module")
+ assert 0
+ def test_func():
+ pass
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*def teardown_module(mod):*",
+ "*Captured stdout*",
+ "*teardown module*",
+ "*1 error*",
+ ])
+
+ def test_capturing_outerr(self, testdir):
+ p1 = testdir.makepyfile("""
+ import sys
+ def test_capturing():
+ print (42)
+ sys.stderr.write(str(23))
+ def test_capturing_error():
+ print (1)
+ sys.stderr.write(str(2))
+ raise ValueError
+ """)
+ result = testdir.runpytest(p1)
+ result.stdout.fnmatch_lines([
+ "*test_capturing_outerr.py .F*",
+ "====* FAILURES *====",
+ "____*____",
+ "*test_capturing_outerr.py:8: ValueError",
+ "*--- Captured stdout *call*",
+ "1",
+ "*--- Captured stderr *call*",
+ "2",
+ ])
+
+
+class TestLoggingInteraction(object):
+ def test_logging_stream_ownership(self, testdir):
+ p = testdir.makepyfile("""
+ def test_logging():
+ import logging
+ import pytest
+ stream = capture.CaptureIO()
+ logging.basicConfig(stream=stream)
+ stream.close() # to free memory/release resources
+ """)
+ result = testdir.runpytest_subprocess(p)
+ assert result.stderr.str().find("atexit") == -1
+
+ def test_logging_and_immediate_setupteardown(self, testdir):
+ p = testdir.makepyfile("""
+ import logging
+ def setup_function(function):
+ logging.warn("hello1")
+
+ def test_logging():
+ logging.warn("hello2")
+ assert 0
+
+ def teardown_function(function):
+ logging.warn("hello3")
+ assert 0
+ """)
+ for optargs in (('--capture=sys',), ('--capture=fd',)):
+ print(optargs)
+ result = testdir.runpytest_subprocess(p, *optargs)
+ s = result.stdout.str()
+ result.stdout.fnmatch_lines([
+ "*WARN*hello3", # errors show first!
+ "*WARN*hello1",
+ "*WARN*hello2",
+ ])
+ # verify proper termination
+ assert "closed" not in s
+
+ def test_logging_and_crossscope_fixtures(self, testdir):
+ p = testdir.makepyfile("""
+ import logging
+ def setup_module(function):
+ logging.warn("hello1")
+
+ def test_logging():
+ logging.warn("hello2")
+ assert 0
+
+ def teardown_module(function):
+ logging.warn("hello3")
+ assert 0
+ """)
+ for optargs in (('--capture=sys',), ('--capture=fd',)):
+ print(optargs)
+ result = testdir.runpytest_subprocess(p, *optargs)
+ s = result.stdout.str()
+ result.stdout.fnmatch_lines([
+ "*WARN*hello3", # errors come first
+ "*WARN*hello1",
+ "*WARN*hello2",
+ ])
+ # verify proper termination
+ assert "closed" not in s
+
+ def test_conftestlogging_is_shown(self, testdir):
+ testdir.makeconftest("""
+ import logging
+ logging.basicConfig()
+ logging.warn("hello435")
+ """)
+ # make sure that logging is still captured in tests
+ result = testdir.runpytest_subprocess("-s", "-p", "no:capturelog")
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ result.stderr.fnmatch_lines([
+ "WARNING*hello435*",
+ ])
+ assert 'operation on closed file' not in result.stderr.str()
+
+ def test_conftestlogging_and_test_logging(self, testdir):
+ testdir.makeconftest("""
+ import logging
+ logging.basicConfig()
+ """)
+ # make sure that logging is still captured in tests
+ p = testdir.makepyfile("""
+ def test_hello():
+ import logging
+ logging.warn("hello433")
+ assert 0
+ """)
+ result = testdir.runpytest_subprocess(p, "-p", "no:capturelog")
+ assert result.ret != 0
+ result.stdout.fnmatch_lines([
+ "WARNING*hello433*",
+ ])
+ assert 'something' not in result.stderr.str()
+ assert 'operation on closed file' not in result.stderr.str()
+
+
+class TestCaptureFixture(object):
+ @pytest.mark.parametrize("opt", [[], ["-s"]])
+ def test_std_functional(self, testdir, opt):
+ reprec = testdir.inline_runsource("""
+ def test_hello(capsys):
+ print (42)
+ out, err = capsys.readouterr()
+ assert out.startswith("42")
+ """, *opt)
+ reprec.assertoutcome(passed=1)
+
+ def test_capsyscapfd(self, testdir):
+ p = testdir.makepyfile("""
+ def test_one(capsys, capfd):
+ pass
+ def test_two(capfd, capsys):
+ pass
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*ERROR*setup*test_one*",
+ "E*capfd*capsys*same*time*",
+ "*ERROR*setup*test_two*",
+ "E*capsys*capfd*same*time*",
+ "*2 error*"])
+
+ def test_capturing_getfixturevalue(self, testdir):
+ """Test that asking for "capfd" and "capsys" using request.getfixturevalue
+ in the same test is an error.
+ """
+ testdir.makepyfile("""
+ def test_one(capsys, request):
+ request.getfixturevalue("capfd")
+ def test_two(capfd, request):
+ request.getfixturevalue("capsys")
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*test_one*",
+ "*capsys*capfd*same*time*",
+ "*test_two*",
+ "*capfd*capsys*same*time*",
+ "*2 failed in*",
+ ])
+
+ def test_capsyscapfdbinary(self, testdir):
+ p = testdir.makepyfile("""
+ def test_one(capsys, capfdbinary):
+ pass
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*ERROR*setup*test_one*",
+ "E*capfdbinary*capsys*same*time*",
+ "*1 error*"])
+
+ @pytest.mark.parametrize("method", ["sys", "fd"])
+ def test_capture_is_represented_on_failure_issue128(self, testdir, method):
+ p = testdir.makepyfile("""
+ def test_hello(cap%s):
+ print ("xxx42xxx")
+ assert 0
+ """ % method)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "xxx42xxx",
+ ])
+
+ @needsosdup
+ def test_stdfd_functional(self, testdir):
+ reprec = testdir.inline_runsource("""
+ def test_hello(capfd):
+ import os
+ os.write(1, "42".encode('ascii'))
+ out, err = capfd.readouterr()
+ assert out.startswith("42")
+ capfd.close()
+ """)
+ reprec.assertoutcome(passed=1)
+
+ @needsosdup
+ def test_capfdbinary(self, testdir):
+ reprec = testdir.inline_runsource("""
+ def test_hello(capfdbinary):
+ import os
+ # some likely un-decodable bytes
+ os.write(1, b'\\xfe\\x98\\x20')
+ out, err = capfdbinary.readouterr()
+ assert out == b'\\xfe\\x98\\x20'
+ assert err == b''
+ """)
+ reprec.assertoutcome(passed=1)
+
+ @pytest.mark.skipif(
+ sys.version_info < (3,),
+ reason='only have capsysbinary in python 3',
+ )
+ def test_capsysbinary(self, testdir):
+ reprec = testdir.inline_runsource("""
+ def test_hello(capsysbinary):
+ import sys
+ # some likely un-decodable bytes
+ sys.stdout.buffer.write(b'\\xfe\\x98\\x20')
+ out, err = capsysbinary.readouterr()
+ assert out == b'\\xfe\\x98\\x20'
+ assert err == b''
+ """)
+ reprec.assertoutcome(passed=1)
+
+ @pytest.mark.skipif(
+ sys.version_info >= (3,),
+ reason='only have capsysbinary in python 3',
+ )
+ def test_capsysbinary_forbidden_in_python2(self, testdir):
+ testdir.makepyfile("""
+ def test_hello(capsysbinary):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*test_hello*",
+ "*capsysbinary is only supported on python 3*",
+ "*1 error in*",
+ ])
+
+ def test_partial_setup_failure(self, testdir):
+ p = testdir.makepyfile("""
+ def test_hello(capsys, missingarg):
+ pass
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*test_partial_setup_failure*",
+ "*1 error*",
+ ])
+
+ @needsosdup
+ def test_keyboardinterrupt_disables_capturing(self, testdir):
+ p = testdir.makepyfile("""
+ def test_hello(capfd):
+ import os
+ os.write(1, str(42).encode('ascii'))
+ raise KeyboardInterrupt()
+ """)
+ result = testdir.runpytest_subprocess(p)
+ result.stdout.fnmatch_lines([
+ "*KeyboardInterrupt*"
+ ])
+ assert result.ret == 2
+
+ @pytest.mark.issue14
+ def test_capture_and_logging(self, testdir):
+ p = testdir.makepyfile("""
+ import logging
+ def test_log(capsys):
+ logging.error('x')
+ """)
+ result = testdir.runpytest_subprocess(p)
+ assert 'closed' not in result.stderr.str()
+
+ @pytest.mark.parametrize('fixture', ['capsys', 'capfd'])
+ @pytest.mark.parametrize('no_capture', [True, False])
+ def test_disabled_capture_fixture(self, testdir, fixture, no_capture):
+ testdir.makepyfile("""
+ def test_disabled({fixture}):
+ print('captured before')
+ with {fixture}.disabled():
+ print('while capture is disabled')
+ print('captured after')
+ assert {fixture}.readouterr() == ('captured before\\ncaptured after\\n', '')
+
+ def test_normal():
+ print('test_normal executed')
+ """.format(fixture=fixture))
+ args = ('-s',) if no_capture else ()
+ result = testdir.runpytest_subprocess(*args)
+ result.stdout.fnmatch_lines("""
+ *while capture is disabled*
+ """)
+ assert 'captured before' not in result.stdout.str()
+ assert 'captured after' not in result.stdout.str()
+ if no_capture:
+ assert 'test_normal executed' in result.stdout.str()
+ else:
+ assert 'test_normal executed' not in result.stdout.str()
+
+ @pytest.mark.parametrize('fixture', ['capsys', 'capfd'])
+ def test_fixture_use_by_other_fixtures(self, testdir, fixture):
+ """
+ Ensure that capsys and capfd can be used by other fixtures during setup and teardown.
+ """
+ testdir.makepyfile("""
+ from __future__ import print_function
+ import sys
+ import pytest
+
+ @pytest.fixture
+ def captured_print({fixture}):
+ print('stdout contents begin')
+ print('stderr contents begin', file=sys.stderr)
+ out, err = {fixture}.readouterr()
+
+ yield out, err
+
+ print('stdout contents end')
+ print('stderr contents end', file=sys.stderr)
+ out, err = {fixture}.readouterr()
+ assert out == 'stdout contents end\\n'
+ assert err == 'stderr contents end\\n'
+
+ def test_captured_print(captured_print):
+ out, err = captured_print
+ assert out == 'stdout contents begin\\n'
+ assert err == 'stderr contents begin\\n'
+ """.format(fixture=fixture))
+ result = testdir.runpytest_subprocess()
+ result.stdout.fnmatch_lines("*1 passed*")
+ assert 'stdout contents begin' not in result.stdout.str()
+ assert 'stderr contents begin' not in result.stdout.str()
+
+
+def test_setup_failure_does_not_kill_capturing(testdir):
+ sub1 = testdir.mkpydir("sub1")
+ sub1.join("conftest.py").write(_pytest._code.Source("""
+ def pytest_runtest_setup(item):
+ raise ValueError(42)
+ """))
+ sub1.join("test_mod.py").write("def test_func1(): pass")
+ result = testdir.runpytest(testdir.tmpdir, '--traceconfig')
+ result.stdout.fnmatch_lines([
+ "*ValueError(42)*",
+ "*1 error*"
+ ])
+
+
+def test_fdfuncarg_skips_on_no_osdup(testdir):
+ testdir.makepyfile("""
+ import os
+ if hasattr(os, 'dup'):
+ del os.dup
+ def test_hello(capfd):
+ pass
+ """)
+ result = testdir.runpytest_subprocess("--capture=no")
+ result.stdout.fnmatch_lines([
+ "*1 skipped*"
+ ])
+
+
+def test_capture_conftest_runtest_setup(testdir):
+ testdir.makeconftest("""
+ def pytest_runtest_setup():
+ print ("hello19")
+ """)
+ testdir.makepyfile("def test_func(): pass")
+ result = testdir.runpytest()
+ assert result.ret == 0
+ assert 'hello19' not in result.stdout.str()
+
+
+def test_capture_badoutput_issue412(testdir):
+ testdir.makepyfile("""
+ import os
+
+ def test_func():
+ omg = bytearray([1,129,1])
+ os.write(1, omg)
+ assert 0
+ """)
+ result = testdir.runpytest('--cap=fd')
+ result.stdout.fnmatch_lines('''
+ *def test_func*
+ *assert 0*
+ *Captured*
+ *1 failed*
+ ''')
+
+
+def test_capture_early_option_parsing(testdir):
+ testdir.makeconftest("""
+ def pytest_runtest_setup():
+ print ("hello19")
+ """)
+ testdir.makepyfile("def test_func(): pass")
+ result = testdir.runpytest("-vs")
+ assert result.ret == 0
+ assert 'hello19' in result.stdout.str()
+
+
+def test_capture_binary_output(testdir):
+ testdir.makepyfile(r"""
+ import pytest
+
+ def test_a():
+ import sys
+ import subprocess
+ subprocess.call([sys.executable, __file__])
+
+ def test_foo():
+ import os;os.write(1, b'\xc3')
+
+ if __name__ == '__main__':
+ test_foo()
+ """)
+ result = testdir.runpytest('--assert=plain')
+ result.assert_outcomes(passed=2)
+
+
+def test_error_during_readouterr(testdir):
+ """Make sure we suspend capturing if errors occur during readouterr"""
+ testdir.makepyfile(pytest_xyz="""
+ from _pytest.capture import FDCapture
+ def bad_snap(self):
+ raise Exception('boom')
+ assert FDCapture.snap
+ FDCapture.snap = bad_snap
+ """)
+ result = testdir.runpytest_subprocess(
+ "-p", "pytest_xyz", "--version", syspathinsert=True
+ )
+ result.stderr.fnmatch_lines([
+ "*in bad_snap",
+ " raise Exception('boom')",
+ "Exception: boom",
+ ])
+
+
+class TestCaptureIO(object):
+ def test_text(self):
+ f = capture.CaptureIO()
+ f.write("hello")
+ s = f.getvalue()
+ assert s == "hello"
+ f.close()
+
+ def test_unicode_and_str_mixture(self):
+ f = capture.CaptureIO()
+ if sys.version_info >= (3, 0):
+ f.write("\u00f6")
+ pytest.raises(TypeError, "f.write(bytes('hello', 'UTF-8'))")
+ else:
+ f.write(unicode("\u00f6", 'UTF-8'))
+ f.write("hello") # bytes
+ s = f.getvalue()
+ f.close()
+ assert isinstance(s, unicode)
+
+ @pytest.mark.skipif(
+ sys.version_info[0] == 2,
+ reason='python 3 only behaviour',
+ )
+ def test_write_bytes_to_buffer(self):
+ """In python3, stdout / stderr are text io wrappers (exposing a buffer
+ property of the underlying bytestream). See issue #1407
+ """
+ f = capture.CaptureIO()
+ f.buffer.write(b'foo\r\n')
+ assert f.getvalue() == 'foo\r\n'
+
+
+def test_bytes_io():
+ f = py.io.BytesIO()
+ f.write(tobytes("hello"))
+ pytest.raises(TypeError, "f.write(totext('hello'))")
+ s = f.getvalue()
+ assert s == tobytes("hello")
+
+
+def test_dontreadfrominput():
+ from _pytest.capture import DontReadFromInput
+ f = DontReadFromInput()
+ assert not f.isatty()
+ pytest.raises(IOError, f.read)
+ pytest.raises(IOError, f.readlines)
+ pytest.raises(IOError, iter, f)
+ pytest.raises(UnsupportedOperation, f.fileno)
+ f.close() # just for completeness
+
+
+@pytest.mark.skipif('sys.version_info < (3,)', reason='python2 has no buffer')
+def test_dontreadfrominput_buffer_python3():
+ from _pytest.capture import DontReadFromInput
+ f = DontReadFromInput()
+ fb = f.buffer
+ assert not fb.isatty()
+ pytest.raises(IOError, fb.read)
+ pytest.raises(IOError, fb.readlines)
+ pytest.raises(IOError, iter, fb)
+ pytest.raises(ValueError, fb.fileno)
+ f.close() # just for completeness
+
+
+@pytest.mark.skipif('sys.version_info >= (3,)', reason='python2 has no buffer')
+def test_dontreadfrominput_buffer_python2():
+ from _pytest.capture import DontReadFromInput
+ f = DontReadFromInput()
+ with pytest.raises(AttributeError):
+ f.buffer
+ f.close() # just for completeness
+
+
+@pytest.yield_fixture
+def tmpfile(testdir):
+ f = testdir.makepyfile("").open('wb+')
+ yield f
+ if not f.closed:
+ f.close()
+
+
+@needsosdup
+def test_dupfile(tmpfile):
+ flist = []
+ for i in range(5):
+ nf = capture.safe_text_dupfile(tmpfile, "wb")
+ assert nf != tmpfile
+ assert nf.fileno() != tmpfile.fileno()
+ assert nf not in flist
+ print(i, end="", file=nf)
+ flist.append(nf)
+
+ fname_open = flist[0].name
+ assert fname_open == repr(flist[0].buffer)
+
+ for i in range(5):
+ f = flist[i]
+ f.close()
+ fname_closed = flist[0].name
+ assert fname_closed == repr(flist[0].buffer)
+ assert fname_closed != fname_open
+ tmpfile.seek(0)
+ s = tmpfile.read()
+ assert "01234" in repr(s)
+ tmpfile.close()
+ assert fname_closed == repr(flist[0].buffer)
+
+
+def test_dupfile_on_bytesio():
+ io = py.io.BytesIO()
+ f = capture.safe_text_dupfile(io, "wb")
+ f.write("hello")
+ assert io.getvalue() == b"hello"
+ assert 'BytesIO object' in f.name
+
+
+def test_dupfile_on_textio():
+ io = py.io.TextIO()
+ f = capture.safe_text_dupfile(io, "wb")
+ f.write("hello")
+ assert io.getvalue() == "hello"
+ assert not hasattr(f, 'name')
+
+
+@contextlib.contextmanager
+def lsof_check():
+ pid = os.getpid()
+ try:
+ out = py.process.cmdexec("lsof -p %d" % pid)
+ except (py.process.cmdexec.Error, UnicodeDecodeError):
+ # about UnicodeDecodeError, see note on pytester
+ pytest.skip("could not run 'lsof'")
+ yield
+ out2 = py.process.cmdexec("lsof -p %d" % pid)
+ len1 = len([x for x in out.split("\n") if "REG" in x])
+ len2 = len([x for x in out2.split("\n") if "REG" in x])
+ assert len2 < len1 + 3, out2
+
+
+class TestFDCapture(object):
+ pytestmark = needsosdup
+
+ def test_simple(self, tmpfile):
+ fd = tmpfile.fileno()
+ cap = capture.FDCapture(fd)
+ data = tobytes("hello")
+ os.write(fd, data)
+ s = cap.snap()
+ cap.done()
+ assert not s
+ cap = capture.FDCapture(fd)
+ cap.start()
+ os.write(fd, data)
+ s = cap.snap()
+ cap.done()
+ assert s == "hello"
+
+ def test_simple_many(self, tmpfile):
+ for i in range(10):
+ self.test_simple(tmpfile)
+
+ def test_simple_many_check_open_files(self, testdir):
+ with lsof_check():
+ with testdir.makepyfile("").open('wb+') as tmpfile:
+ self.test_simple_many(tmpfile)
+
+ def test_simple_fail_second_start(self, tmpfile):
+ fd = tmpfile.fileno()
+ cap = capture.FDCapture(fd)
+ cap.done()
+ pytest.raises(ValueError, cap.start)
+
+ def test_stderr(self):
+ cap = capture.FDCapture(2)
+ cap.start()
+ print("hello", file=sys.stderr)
+ s = cap.snap()
+ cap.done()
+ assert s == "hello\n"
+
+ def test_stdin(self, tmpfile):
+ cap = capture.FDCapture(0)
+ cap.start()
+ x = os.read(0, 100).strip()
+ cap.done()
+ assert x == tobytes('')
+
+ def test_writeorg(self, tmpfile):
+ data1, data2 = tobytes("foo"), tobytes("bar")
+ cap = capture.FDCapture(tmpfile.fileno())
+ cap.start()
+ tmpfile.write(data1)
+ tmpfile.flush()
+ cap.writeorg(data2)
+ scap = cap.snap()
+ cap.done()
+ assert scap == totext(data1)
+ with open(tmpfile.name, 'rb') as stmp_file:
+ stmp = stmp_file.read()
+ assert stmp == data2
+
+ def test_simple_resume_suspend(self, tmpfile):
+ with saved_fd(1):
+ cap = capture.FDCapture(1)
+ cap.start()
+ data = tobytes("hello")
+ os.write(1, data)
+ sys.stdout.write("whatever")
+ s = cap.snap()
+ assert s == "hellowhatever"
+ cap.suspend()
+ os.write(1, tobytes("world"))
+ sys.stdout.write("qlwkej")
+ assert not cap.snap()
+ cap.resume()
+ os.write(1, tobytes("but now"))
+ sys.stdout.write(" yes\n")
+ s = cap.snap()
+ assert s == "but now yes\n"
+ cap.suspend()
+ cap.done()
+ pytest.raises(AttributeError, cap.suspend)
+
+
+@contextlib.contextmanager
+def saved_fd(fd):
+ new_fd = os.dup(fd)
+ try:
+ yield
+ finally:
+ os.dup2(new_fd, fd)
+ os.close(new_fd)
+
+
+class TestStdCapture(object):
+ captureclass = staticmethod(StdCapture)
+
+ @contextlib.contextmanager
+ def getcapture(self, **kw):
+ cap = self.__class__.captureclass(**kw)
+ cap.start_capturing()
+ try:
+ yield cap
+ finally:
+ cap.stop_capturing()
+
+ def test_capturing_done_simple(self):
+ with self.getcapture() as cap:
+ sys.stdout.write("hello")
+ sys.stderr.write("world")
+ out, err = cap.readouterr()
+ assert out == "hello"
+ assert err == "world"
+
+ def test_capturing_reset_simple(self):
+ with self.getcapture() as cap:
+ print("hello world")
+ sys.stderr.write("hello error\n")
+ out, err = cap.readouterr()
+ assert out == "hello world\n"
+ assert err == "hello error\n"
+
+ def test_capturing_readouterr(self):
+ with self.getcapture() as cap:
+ print("hello world")
+ sys.stderr.write("hello error\n")
+ out, err = cap.readouterr()
+ assert out == "hello world\n"
+ assert err == "hello error\n"
+ sys.stderr.write("error2")
+ out, err = cap.readouterr()
+ assert err == "error2"
+
+ def test_capture_results_accessible_by_attribute(self):
+ with self.getcapture() as cap:
+ sys.stdout.write("hello")
+ sys.stderr.write("world")
+ capture_result = cap.readouterr()
+ assert capture_result.out == "hello"
+ assert capture_result.err == "world"
+
+ def test_capturing_readouterr_unicode(self):
+ with self.getcapture() as cap:
+ print("hx\xc4\x85\xc4\x87")
+ out, err = cap.readouterr()
+ assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8")
+
+ @pytest.mark.skipif('sys.version_info >= (3,)',
+ reason='text output different for bytes on python3')
+ def test_capturing_readouterr_decode_error_handling(self):
+ with self.getcapture() as cap:
+ # triggered a internal error in pytest
+ print('\xa6')
+ out, err = cap.readouterr()
+ assert out == py.builtin._totext('\ufffd\n', 'unicode-escape')
+
+ def test_reset_twice_error(self):
+ with self.getcapture() as cap:
+ print("hello")
+ out, err = cap.readouterr()
+ pytest.raises(ValueError, cap.stop_capturing)
+ assert out == "hello\n"
+ assert not err
+
+ def test_capturing_modify_sysouterr_in_between(self):
+ oldout = sys.stdout
+ olderr = sys.stderr
+ with self.getcapture() as cap:
+ sys.stdout.write("hello")
+ sys.stderr.write("world")
+ sys.stdout = capture.CaptureIO()
+ sys.stderr = capture.CaptureIO()
+ print("not seen")
+ sys.stderr.write("not seen\n")
+ out, err = cap.readouterr()
+ assert out == "hello"
+ assert err == "world"
+ assert sys.stdout == oldout
+ assert sys.stderr == olderr
+
+ def test_capturing_error_recursive(self):
+ with self.getcapture() as cap1:
+ print("cap1")
+ with self.getcapture() as cap2:
+ print("cap2")
+ out2, err2 = cap2.readouterr()
+ out1, err1 = cap1.readouterr()
+ assert out1 == "cap1\n"
+ assert out2 == "cap2\n"
+
+ def test_just_out_capture(self):
+ with self.getcapture(out=True, err=False) as cap:
+ sys.stdout.write("hello")
+ sys.stderr.write("world")
+ out, err = cap.readouterr()
+ assert out == "hello"
+ assert not err
+
+ def test_just_err_capture(self):
+ with self.getcapture(out=False, err=True) as cap:
+ sys.stdout.write("hello")
+ sys.stderr.write("world")
+ out, err = cap.readouterr()
+ assert err == "world"
+ assert not out
+
+ def test_stdin_restored(self):
+ old = sys.stdin
+ with self.getcapture(in_=True):
+ newstdin = sys.stdin
+ assert newstdin != sys.stdin
+ assert sys.stdin is old
+
+ def test_stdin_nulled_by_default(self):
+ print("XXX this test may well hang instead of crashing")
+ print("XXX which indicates an error in the underlying capturing")
+ print("XXX mechanisms")
+ with self.getcapture():
+ pytest.raises(IOError, "sys.stdin.read()")
+
+
+class TestStdCaptureFD(TestStdCapture):
+ pytestmark = needsosdup
+ captureclass = staticmethod(StdCaptureFD)
+
+ def test_simple_only_fd(self, testdir):
+ testdir.makepyfile("""
+ import os
+ def test_x():
+ os.write(1, "hello\\n".encode("ascii"))
+ assert 0
+ """)
+ result = testdir.runpytest_subprocess()
+ result.stdout.fnmatch_lines("""
+ *test_x*
+ *assert 0*
+ *Captured stdout*
+ """)
+
+ def test_intermingling(self):
+ with self.getcapture() as cap:
+ oswritebytes(1, "1")
+ sys.stdout.write(str(2))
+ sys.stdout.flush()
+ oswritebytes(1, "3")
+ oswritebytes(2, "a")
+ sys.stderr.write("b")
+ sys.stderr.flush()
+ oswritebytes(2, "c")
+ out, err = cap.readouterr()
+ assert out == "123"
+ assert err == "abc"
+
+ def test_many(self, capfd):
+ with lsof_check():
+ for i in range(10):
+ cap = StdCaptureFD()
+ cap.stop_capturing()
+
+
+class TestStdCaptureFDinvalidFD(object):
+ pytestmark = needsosdup
+
+ def test_stdcapture_fd_invalid_fd(self, testdir):
+ testdir.makepyfile("""
+ import os
+ from _pytest import capture
+ def StdCaptureFD(out=True, err=True, in_=True):
+ return capture.MultiCapture(out, err, in_,
+ Capture=capture.FDCapture)
+ def test_stdout():
+ os.close(1)
+ cap = StdCaptureFD(out=True, err=False, in_=False)
+ cap.stop_capturing()
+ def test_stderr():
+ os.close(2)
+ cap = StdCaptureFD(out=False, err=True, in_=False)
+ cap.stop_capturing()
+ def test_stdin():
+ os.close(0)
+ cap = StdCaptureFD(out=False, err=False, in_=True)
+ cap.stop_capturing()
+ """)
+ result = testdir.runpytest_subprocess("--capture=fd")
+ assert result.ret == 0
+ assert result.parseoutcomes()['passed'] == 3
+
+
+def test_capture_not_started_but_reset():
+ capsys = StdCapture()
+ capsys.stop_capturing()
+
+
+def test_using_capsys_fixture_works_with_sys_stdout_encoding(capsys):
+ test_text = 'test text'
+
+ print(test_text.encode(sys.stdout.encoding, 'replace'))
+ (out, err) = capsys.readouterr()
+ assert out
+ assert err == ''
+
+
+def test_capsys_results_accessible_by_attribute(capsys):
+ sys.stdout.write("spam")
+ sys.stderr.write("eggs")
+ capture_result = capsys.readouterr()
+ assert capture_result.out == "spam"
+ assert capture_result.err == "eggs"
+
+
+@needsosdup
+@pytest.mark.parametrize('use', [True, False])
+def test_fdcapture_tmpfile_remains_the_same(tmpfile, use):
+ if not use:
+ tmpfile = True
+ cap = StdCaptureFD(out=False, err=tmpfile)
+ try:
+ cap.start_capturing()
+ capfile = cap.err.tmpfile
+ cap.readouterr()
+ finally:
+ cap.stop_capturing()
+ capfile2 = cap.err.tmpfile
+ assert capfile2 == capfile
+
+
+@needsosdup
+def test_close_and_capture_again(testdir):
+ testdir.makepyfile("""
+ import os
+ def test_close():
+ os.close(1)
+ def test_capture_again():
+ os.write(1, b"hello\\n")
+ assert 0
+ """)
+ result = testdir.runpytest_subprocess()
+ result.stdout.fnmatch_lines("""
+ *test_capture_again*
+ *assert 0*
+ *stdout*
+ *hello*
+ """)
+
+
+@pytest.mark.parametrize('method', ['SysCapture', 'FDCapture'])
+def test_capturing_and_logging_fundamentals(testdir, method):
+ if method == "StdCaptureFD" and not hasattr(os, 'dup'):
+ pytest.skip("need os.dup")
+ # here we check a fundamental feature
+ p = testdir.makepyfile("""
+ import sys, os
+ import py, logging
+ from _pytest import capture
+ cap = capture.MultiCapture(out=False, in_=False,
+ Capture=capture.%s)
+ cap.start_capturing()
+
+ logging.warn("hello1")
+ outerr = cap.readouterr()
+ print ("suspend, captured %%s" %%(outerr,))
+ logging.warn("hello2")
+
+ cap.pop_outerr_to_orig()
+ logging.warn("hello3")
+
+ outerr = cap.readouterr()
+ print ("suspend2, captured %%s" %% (outerr,))
+ """ % (method,))
+ result = testdir.runpython(p)
+ result.stdout.fnmatch_lines("""
+ suspend, captured*hello1*
+ suspend2, captured*WARNING:root:hello3*
+ """)
+ result.stderr.fnmatch_lines("""
+ WARNING:root:hello2
+ """)
+ assert "atexit" not in result.stderr.str()
+
+
+def test_error_attribute_issue555(testdir):
+ testdir.makepyfile("""
+ import sys
+ def test_capattr():
+ assert sys.stdout.errors == "strict"
+ assert sys.stderr.errors == "strict"
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+
+@pytest.mark.skipif(not sys.platform.startswith('win') and sys.version_info[:2] >= (3, 6),
+ reason='only py3.6+ on windows')
+def test_py36_windowsconsoleio_workaround_non_standard_streams():
+ """
+ Ensure _py36_windowsconsoleio_workaround function works with objects that
+ do not implement the full ``io``-based stream protocol, for example execnet channels (#2666).
+ """
+ from _pytest.capture import _py36_windowsconsoleio_workaround
+
+ class DummyStream:
+ def write(self, s):
+ pass
+
+ stream = DummyStream()
+ _py36_windowsconsoleio_workaround(stream)
+
+
+def test_dontreadfrominput_has_encoding(testdir):
+ testdir.makepyfile("""
+ import sys
+ def test_capattr():
+ # should not raise AttributeError
+ assert sys.stdout.encoding
+ assert sys.stderr.encoding
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+
+def test_pickling_and_unpickling_encoded_file():
+ # See https://bitbucket.org/pytest-dev/pytest/pull-request/194
+ # pickle.loads() raises infinite recursion if
+ # EncodedFile.__getattr__ is not implemented properly
+ ef = capture.EncodedFile(None, None)
+ ef_as_str = pickle.dumps(ef)
+ pickle.loads(ef_as_str)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_collection.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_collection.py
new file mode 100644
index 00000000000..563ed0439c0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_collection.py
@@ -0,0 +1,857 @@
+from __future__ import absolute_import, division, print_function
+import pytest
+import py
+
+import _pytest._code
+from _pytest.main import Session, EXIT_NOTESTSCOLLECTED, _in_venv
+
+
+class TestCollector(object):
+ def test_collect_versus_item(self):
+ from pytest import Collector, Item
+ assert not issubclass(Collector, Item)
+ assert not issubclass(Item, Collector)
+
+ def test_compat_attributes(self, testdir, recwarn):
+ modcol = testdir.getmodulecol("""
+ def test_pass(): pass
+ def test_fail(): assert 0
+ """)
+ recwarn.clear()
+ assert modcol.Module == pytest.Module
+ assert modcol.Class == pytest.Class
+ assert modcol.Item == pytest.Item
+ assert modcol.File == pytest.File
+ assert modcol.Function == pytest.Function
+
+ def test_check_equality(self, testdir):
+ modcol = testdir.getmodulecol("""
+ def test_pass(): pass
+ def test_fail(): assert 0
+ """)
+ fn1 = testdir.collect_by_name(modcol, "test_pass")
+ assert isinstance(fn1, pytest.Function)
+ fn2 = testdir.collect_by_name(modcol, "test_pass")
+ assert isinstance(fn2, pytest.Function)
+
+ assert fn1 == fn2
+ assert fn1 != modcol
+ if py.std.sys.version_info < (3, 0):
+ assert cmp(fn1, fn2) == 0
+ assert hash(fn1) == hash(fn2)
+
+ fn3 = testdir.collect_by_name(modcol, "test_fail")
+ assert isinstance(fn3, pytest.Function)
+ assert not (fn1 == fn3)
+ assert fn1 != fn3
+
+ for fn in fn1, fn2, fn3:
+ assert fn != 3
+ assert fn != modcol
+ assert fn != [1, 2, 3]
+ assert [1, 2, 3] != fn
+ assert modcol != fn
+
+ def test_getparent(self, testdir):
+ modcol = testdir.getmodulecol("""
+ class TestClass(object):
+ def test_foo():
+ pass
+ """)
+ cls = testdir.collect_by_name(modcol, "TestClass")
+ fn = testdir.collect_by_name(
+ testdir.collect_by_name(cls, "()"), "test_foo")
+
+ parent = fn.getparent(pytest.Module)
+ assert parent is modcol
+
+ parent = fn.getparent(pytest.Function)
+ assert parent is fn
+
+ parent = fn.getparent(pytest.Class)
+ assert parent is cls
+
+ def test_getcustomfile_roundtrip(self, testdir):
+ hello = testdir.makefile(".xxx", hello="world")
+ testdir.makepyfile(conftest="""
+ import pytest
+ class CustomFile(pytest.File):
+ pass
+ def pytest_collect_file(path, parent):
+ if path.ext == ".xxx":
+ return CustomFile(path, parent=parent)
+ """)
+ node = testdir.getpathnode(hello)
+ assert isinstance(node, pytest.File)
+ assert node.name == "hello.xxx"
+ nodes = node.session.perform_collect([node.nodeid], genitems=False)
+ assert len(nodes) == 1
+ assert isinstance(nodes[0], pytest.File)
+
+ def test_can_skip_class_with_test_attr(self, testdir):
+ """Assure test class is skipped when using `__test__=False` (See #2007)."""
+ testdir.makepyfile("""
+ class TestFoo(object):
+ __test__ = False
+ def __init__(self):
+ pass
+ def test_foo():
+ assert True
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ 'collected 0 items',
+ '*no tests ran in*',
+ ])
+
+
+class TestCollectFS(object):
+ def test_ignored_certain_directories(self, testdir):
+ tmpdir = testdir.tmpdir
+ tmpdir.ensure("build", 'test_notfound.py')
+ tmpdir.ensure("dist", 'test_notfound.py')
+ tmpdir.ensure("_darcs", 'test_notfound.py')
+ tmpdir.ensure("CVS", 'test_notfound.py')
+ tmpdir.ensure("{arch}", 'test_notfound.py')
+ tmpdir.ensure(".whatever", 'test_notfound.py')
+ tmpdir.ensure(".bzr", 'test_notfound.py')
+ tmpdir.ensure("normal", 'test_found.py')
+ for x in tmpdir.visit("test_*.py"):
+ x.write("def test_hello(): pass")
+
+ result = testdir.runpytest("--collect-only")
+ s = result.stdout.str()
+ assert "test_notfound" not in s
+ assert "test_found" in s
+
+ @pytest.mark.parametrize('fname',
+ ("activate", "activate.csh", "activate.fish",
+ "Activate", "Activate.bat", "Activate.ps1"))
+ def test_ignored_virtualenvs(self, testdir, fname):
+ bindir = "Scripts" if py.std.sys.platform.startswith("win") else "bin"
+ testdir.tmpdir.ensure("virtual", bindir, fname)
+ testfile = testdir.tmpdir.ensure("virtual", "test_invenv.py")
+ testfile.write("def test_hello(): pass")
+
+ # by default, ignore tests inside a virtualenv
+ result = testdir.runpytest()
+ assert "test_invenv" not in result.stdout.str()
+ # allow test collection if user insists
+ result = testdir.runpytest("--collect-in-virtualenv")
+ assert "test_invenv" in result.stdout.str()
+ # allow test collection if user directly passes in the directory
+ result = testdir.runpytest("virtual")
+ assert "test_invenv" in result.stdout.str()
+
+ @pytest.mark.parametrize('fname',
+ ("activate", "activate.csh", "activate.fish",
+ "Activate", "Activate.bat", "Activate.ps1"))
+ def test_ignored_virtualenvs_norecursedirs_precedence(self, testdir, fname):
+ bindir = "Scripts" if py.std.sys.platform.startswith("win") else "bin"
+ # norecursedirs takes priority
+ testdir.tmpdir.ensure(".virtual", bindir, fname)
+ testfile = testdir.tmpdir.ensure(".virtual", "test_invenv.py")
+ testfile.write("def test_hello(): pass")
+ result = testdir.runpytest("--collect-in-virtualenv")
+ assert "test_invenv" not in result.stdout.str()
+ # ...unless the virtualenv is explicitly given on the CLI
+ result = testdir.runpytest("--collect-in-virtualenv", ".virtual")
+ assert "test_invenv" in result.stdout.str()
+
+ @pytest.mark.parametrize('fname',
+ ("activate", "activate.csh", "activate.fish",
+ "Activate", "Activate.bat", "Activate.ps1"))
+ def test__in_venv(self, testdir, fname):
+ """Directly test the virtual env detection function"""
+ bindir = "Scripts" if py.std.sys.platform.startswith("win") else "bin"
+ # no bin/activate, not a virtualenv
+ base_path = testdir.tmpdir.mkdir('venv')
+ assert _in_venv(base_path) is False
+ # with bin/activate, totally a virtualenv
+ base_path.ensure(bindir, fname)
+ assert _in_venv(base_path) is True
+
+ def test_custom_norecursedirs(self, testdir):
+ testdir.makeini("""
+ [pytest]
+ norecursedirs = mydir xyz*
+ """)
+ tmpdir = testdir.tmpdir
+ tmpdir.ensure("mydir", "test_hello.py").write("def test_1(): pass")
+ tmpdir.ensure("xyz123", "test_2.py").write("def test_2(): 0/0")
+ tmpdir.ensure("xy", "test_ok.py").write("def test_3(): pass")
+ rec = testdir.inline_run()
+ rec.assertoutcome(passed=1)
+ rec = testdir.inline_run("xyz123/test_2.py")
+ rec.assertoutcome(failed=1)
+
+ def test_testpaths_ini(self, testdir, monkeypatch):
+ testdir.makeini("""
+ [pytest]
+ testpaths = gui uts
+ """)
+ tmpdir = testdir.tmpdir
+ tmpdir.ensure("env", "test_1.py").write("def test_env(): pass")
+ tmpdir.ensure("gui", "test_2.py").write("def test_gui(): pass")
+ tmpdir.ensure("uts", "test_3.py").write("def test_uts(): pass")
+
+ # executing from rootdir only tests from `testpaths` directories
+ # are collected
+ items, reprec = testdir.inline_genitems('-v')
+ assert [x.name for x in items] == ['test_gui', 'test_uts']
+
+ # check that explicitly passing directories in the command-line
+ # collects the tests
+ for dirname in ('env', 'gui', 'uts'):
+ items, reprec = testdir.inline_genitems(tmpdir.join(dirname))
+ assert [x.name for x in items] == ['test_%s' % dirname]
+
+ # changing cwd to each subdirectory and running pytest without
+ # arguments collects the tests in that directory normally
+ for dirname in ('env', 'gui', 'uts'):
+ monkeypatch.chdir(testdir.tmpdir.join(dirname))
+ items, reprec = testdir.inline_genitems()
+ assert [x.name for x in items] == ['test_%s' % dirname]
+
+
+class TestCollectPluginHookRelay(object):
+ def test_pytest_collect_file(self, testdir):
+ wascalled = []
+
+ class Plugin(object):
+ def pytest_collect_file(self, path, parent):
+ if not path.basename.startswith("."):
+ # Ignore hidden files, e.g. .testmondata.
+ wascalled.append(path)
+
+ testdir.makefile(".abc", "xyz")
+ pytest.main([testdir.tmpdir], plugins=[Plugin()])
+ assert len(wascalled) == 1
+ assert wascalled[0].ext == '.abc'
+
+ def test_pytest_collect_directory(self, testdir):
+ wascalled = []
+
+ class Plugin(object):
+ def pytest_collect_directory(self, path, parent):
+ wascalled.append(path.basename)
+
+ testdir.mkdir("hello")
+ testdir.mkdir("world")
+ pytest.main(testdir.tmpdir, plugins=[Plugin()])
+ assert "hello" in wascalled
+ assert "world" in wascalled
+
+
+class TestPrunetraceback(object):
+
+ def test_custom_repr_failure(self, testdir):
+ p = testdir.makepyfile("""
+ import not_exists
+ """)
+ testdir.makeconftest("""
+ import pytest
+ def pytest_collect_file(path, parent):
+ return MyFile(path, parent)
+ class MyError(Exception):
+ pass
+ class MyFile(pytest.File):
+ def collect(self):
+ raise MyError()
+ def repr_failure(self, excinfo):
+ if excinfo.errisinstance(MyError):
+ return "hello world"
+ return pytest.File.repr_failure(self, excinfo)
+ """)
+
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*ERROR collecting*",
+ "*hello world*",
+ ])
+
+ @pytest.mark.xfail(reason="other mechanism for adding to reporting needed")
+ def test_collect_report_postprocessing(self, testdir):
+ p = testdir.makepyfile("""
+ import not_exists
+ """)
+ testdir.makeconftest("""
+ import pytest
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_make_collect_report():
+ outcome = yield
+ rep = outcome.get_result()
+ rep.headerlines += ["header1"]
+ outcome.force_result(rep)
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*ERROR collecting*",
+ "*header1*",
+ ])
+
+
+class TestCustomConftests(object):
+ def test_ignore_collect_path(self, testdir):
+ testdir.makeconftest("""
+ def pytest_ignore_collect(path, config):
+ return path.basename.startswith("x") or \
+ path.basename == "test_one.py"
+ """)
+ sub = testdir.mkdir("xy123")
+ sub.ensure("test_hello.py").write("syntax error")
+ sub.join("conftest.py").write("syntax error")
+ testdir.makepyfile("def test_hello(): pass")
+ testdir.makepyfile(test_one="syntax error")
+ result = testdir.runpytest("--fulltrace")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_ignore_collect_not_called_on_argument(self, testdir):
+ testdir.makeconftest("""
+ def pytest_ignore_collect(path, config):
+ return True
+ """)
+ p = testdir.makepyfile("def test_hello(): pass")
+ result = testdir.runpytest(p)
+ assert result.ret == 0
+ result.stdout.fnmatch_lines("*1 passed*")
+ result = testdir.runpytest()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ result.stdout.fnmatch_lines("*collected 0 items*")
+
+ def test_collectignore_exclude_on_option(self, testdir):
+ testdir.makeconftest("""
+ collect_ignore = ['hello', 'test_world.py']
+ def pytest_addoption(parser):
+ parser.addoption("--XX", action="store_true", default=False)
+ def pytest_configure(config):
+ if config.getvalue("XX"):
+ collect_ignore[:] = []
+ """)
+ testdir.mkdir("hello")
+ testdir.makepyfile(test_world="def test_hello(): pass")
+ result = testdir.runpytest()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ assert "passed" not in result.stdout.str()
+ result = testdir.runpytest("--XX")
+ assert result.ret == 0
+ assert "passed" in result.stdout.str()
+
+ def test_pytest_fs_collect_hooks_are_seen(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ class MyModule(pytest.Module):
+ pass
+ def pytest_collect_file(path, parent):
+ if path.ext == ".py":
+ return MyModule(path, parent)
+ """)
+ testdir.mkdir("sub")
+ testdir.makepyfile("def test_x(): pass")
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*MyModule*",
+ "*test_x*"
+ ])
+
+ def test_pytest_collect_file_from_sister_dir(self, testdir):
+ sub1 = testdir.mkpydir("sub1")
+ sub2 = testdir.mkpydir("sub2")
+ conf1 = testdir.makeconftest("""
+ import pytest
+ class MyModule1(pytest.Module):
+ pass
+ def pytest_collect_file(path, parent):
+ if path.ext == ".py":
+ return MyModule1(path, parent)
+ """)
+ conf1.move(sub1.join(conf1.basename))
+ conf2 = testdir.makeconftest("""
+ import pytest
+ class MyModule2(pytest.Module):
+ pass
+ def pytest_collect_file(path, parent):
+ if path.ext == ".py":
+ return MyModule2(path, parent)
+ """)
+ conf2.move(sub2.join(conf2.basename))
+ p = testdir.makepyfile("def test_x(): pass")
+ p.copy(sub1.join(p.basename))
+ p.copy(sub2.join(p.basename))
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*MyModule1*",
+ "*MyModule2*",
+ "*test_x*"
+ ])
+
+
+class TestSession(object):
+ def test_parsearg(self, testdir):
+ p = testdir.makepyfile("def test_func(): pass")
+ subdir = testdir.mkdir("sub")
+ subdir.ensure("__init__.py")
+ target = subdir.join(p.basename)
+ p.move(target)
+ subdir.chdir()
+ config = testdir.parseconfig(p.basename)
+ rcol = Session(config=config)
+ assert rcol.fspath == subdir
+ parts = rcol._parsearg(p.basename)
+
+ assert parts[0] == target
+ assert len(parts) == 1
+ parts = rcol._parsearg(p.basename + "::test_func")
+ assert parts[0] == target
+ assert parts[1] == "test_func"
+ assert len(parts) == 2
+
+ def test_collect_topdir(self, testdir):
+ p = testdir.makepyfile("def test_func(): pass")
+ id = "::".join([p.basename, "test_func"])
+ # XXX migrate to collectonly? (see below)
+ config = testdir.parseconfig(id)
+ topdir = testdir.tmpdir
+ rcol = Session(config)
+ assert topdir == rcol.fspath
+ # rootid = rcol.nodeid
+ # root2 = rcol.perform_collect([rcol.nodeid], genitems=False)[0]
+ # assert root2 == rcol, rootid
+ colitems = rcol.perform_collect([rcol.nodeid], genitems=False)
+ assert len(colitems) == 1
+ assert colitems[0].fspath == p
+
+ def get_reported_items(self, hookrec):
+ """Return pytest.Item instances reported by the pytest_collectreport hook"""
+ calls = hookrec.getcalls('pytest_collectreport')
+ return [x for call in calls for x in call.report.result
+ if isinstance(x, pytest.Item)]
+
+ def test_collect_protocol_single_function(self, testdir):
+ p = testdir.makepyfile("def test_func(): pass")
+ id = "::".join([p.basename, "test_func"])
+ items, hookrec = testdir.inline_genitems(id)
+ item, = items
+ assert item.name == "test_func"
+ newid = item.nodeid
+ assert newid == id
+ py.std.pprint.pprint(hookrec.calls)
+ topdir = testdir.tmpdir # noqa
+ hookrec.assert_contains([
+ ("pytest_collectstart", "collector.fspath == topdir"),
+ ("pytest_make_collect_report", "collector.fspath == topdir"),
+ ("pytest_collectstart", "collector.fspath == p"),
+ ("pytest_make_collect_report", "collector.fspath == p"),
+ ("pytest_pycollect_makeitem", "name == 'test_func'"),
+ ("pytest_collectreport", "report.result[0].name == 'test_func'"),
+ ])
+ # ensure we are reporting the collection of the single test item (#2464)
+ assert [x.name for x in self.get_reported_items(hookrec)] == ['test_func']
+
+ def test_collect_protocol_method(self, testdir):
+ p = testdir.makepyfile("""
+ class TestClass(object):
+ def test_method(self):
+ pass
+ """)
+ normid = p.basename + "::TestClass::()::test_method"
+ for id in [p.basename,
+ p.basename + "::TestClass",
+ p.basename + "::TestClass::()",
+ normid,
+ ]:
+ items, hookrec = testdir.inline_genitems(id)
+ assert len(items) == 1
+ assert items[0].name == "test_method"
+ newid = items[0].nodeid
+ assert newid == normid
+ # ensure we are reporting the collection of the single test item (#2464)
+ assert [x.name for x in self.get_reported_items(hookrec)] == ['test_method']
+
+ def test_collect_custom_nodes_multi_id(self, testdir):
+ p = testdir.makepyfile("def test_func(): pass")
+ testdir.makeconftest("""
+ import pytest
+ class SpecialItem(pytest.Item):
+ def runtest(self):
+ return # ok
+ class SpecialFile(pytest.File):
+ def collect(self):
+ return [SpecialItem(name="check", parent=self)]
+ def pytest_collect_file(path, parent):
+ if path.basename == %r:
+ return SpecialFile(fspath=path, parent=parent)
+ """ % p.basename)
+ id = p.basename
+
+ items, hookrec = testdir.inline_genitems(id)
+ py.std.pprint.pprint(hookrec.calls)
+ assert len(items) == 2
+ hookrec.assert_contains([
+ ("pytest_collectstart",
+ "collector.fspath == collector.session.fspath"),
+ ("pytest_collectstart",
+ "collector.__class__.__name__ == 'SpecialFile'"),
+ ("pytest_collectstart",
+ "collector.__class__.__name__ == 'Module'"),
+ ("pytest_pycollect_makeitem", "name == 'test_func'"),
+ ("pytest_collectreport", "report.nodeid.startswith(p.basename)"),
+ ])
+ assert len(self.get_reported_items(hookrec)) == 2
+
+ def test_collect_subdir_event_ordering(self, testdir):
+ p = testdir.makepyfile("def test_func(): pass")
+ aaa = testdir.mkpydir("aaa")
+ test_aaa = aaa.join("test_aaa.py")
+ p.move(test_aaa)
+
+ items, hookrec = testdir.inline_genitems()
+ assert len(items) == 1
+ py.std.pprint.pprint(hookrec.calls)
+ hookrec.assert_contains([
+ ("pytest_collectstart", "collector.fspath == test_aaa"),
+ ("pytest_pycollect_makeitem", "name == 'test_func'"),
+ ("pytest_collectreport",
+ "report.nodeid.startswith('aaa/test_aaa.py')"),
+ ])
+
+ def test_collect_two_commandline_args(self, testdir):
+ p = testdir.makepyfile("def test_func(): pass")
+ aaa = testdir.mkpydir("aaa")
+ bbb = testdir.mkpydir("bbb")
+ test_aaa = aaa.join("test_aaa.py")
+ p.copy(test_aaa)
+ test_bbb = bbb.join("test_bbb.py")
+ p.move(test_bbb)
+
+ id = "."
+
+ items, hookrec = testdir.inline_genitems(id)
+ assert len(items) == 2
+ py.std.pprint.pprint(hookrec.calls)
+ hookrec.assert_contains([
+ ("pytest_collectstart", "collector.fspath == test_aaa"),
+ ("pytest_pycollect_makeitem", "name == 'test_func'"),
+ ("pytest_collectreport", "report.nodeid == 'aaa/test_aaa.py'"),
+ ("pytest_collectstart", "collector.fspath == test_bbb"),
+ ("pytest_pycollect_makeitem", "name == 'test_func'"),
+ ("pytest_collectreport", "report.nodeid == 'bbb/test_bbb.py'"),
+ ])
+
+ def test_serialization_byid(self, testdir):
+ testdir.makepyfile("def test_func(): pass")
+ items, hookrec = testdir.inline_genitems()
+ assert len(items) == 1
+ item, = items
+ items2, hookrec = testdir.inline_genitems(item.nodeid)
+ item2, = items2
+ assert item2.name == item.name
+ assert item2.fspath == item.fspath
+
+ def test_find_byid_without_instance_parents(self, testdir):
+ p = testdir.makepyfile("""
+ class TestClass(object):
+ def test_method(self):
+ pass
+ """)
+ arg = p.basename + "::TestClass::test_method"
+ items, hookrec = testdir.inline_genitems(arg)
+ assert len(items) == 1
+ item, = items
+ assert item.nodeid.endswith("TestClass::()::test_method")
+ # ensure we are reporting the collection of the single test item (#2464)
+ assert [x.name for x in self.get_reported_items(hookrec)] == ['test_method']
+
+
+class Test_getinitialnodes(object):
+ def test_global_file(self, testdir, tmpdir):
+ x = tmpdir.ensure("x.py")
+ with tmpdir.as_cwd():
+ config = testdir.parseconfigure(x)
+ col = testdir.getnode(config, x)
+ assert isinstance(col, pytest.Module)
+ assert col.name == 'x.py'
+ assert col.parent.parent is None
+ for col in col.listchain():
+ assert col.config is config
+
+ def test_pkgfile(self, testdir):
+ tmpdir = testdir.tmpdir
+ subdir = tmpdir.join("subdir")
+ x = subdir.ensure("x.py")
+ subdir.ensure("__init__.py")
+ with subdir.as_cwd():
+ config = testdir.parseconfigure(x)
+ col = testdir.getnode(config, x)
+ assert isinstance(col, pytest.Module)
+ assert col.name == 'x.py'
+ assert col.parent.parent is None
+ for col in col.listchain():
+ assert col.config is config
+
+
+class Test_genitems(object):
+ def test_check_collect_hashes(self, testdir):
+ p = testdir.makepyfile("""
+ def test_1():
+ pass
+
+ def test_2():
+ pass
+ """)
+ p.copy(p.dirpath(p.purebasename + "2" + ".py"))
+ items, reprec = testdir.inline_genitems(p.dirpath())
+ assert len(items) == 4
+ for numi, i in enumerate(items):
+ for numj, j in enumerate(items):
+ if numj != numi:
+ assert hash(i) != hash(j)
+ assert i != j
+
+ def test_example_items1(self, testdir):
+ p = testdir.makepyfile('''
+ def testone():
+ pass
+
+ class TestX(object):
+ def testmethod_one(self):
+ pass
+
+ class TestY(TestX):
+ pass
+ ''')
+ items, reprec = testdir.inline_genitems(p)
+ assert len(items) == 3
+ assert items[0].name == 'testone'
+ assert items[1].name == 'testmethod_one'
+ assert items[2].name == 'testmethod_one'
+
+ # let's also test getmodpath here
+ assert items[0].getmodpath() == "testone"
+ assert items[1].getmodpath() == "TestX.testmethod_one"
+ assert items[2].getmodpath() == "TestY.testmethod_one"
+
+ s = items[0].getmodpath(stopatmodule=False)
+ assert s.endswith("test_example_items1.testone")
+ print(s)
+
+ def test_class_and_functions_discovery_using_glob(self, testdir):
+ """
+ tests that python_classes and python_functions config options work
+ as prefixes and glob-like patterns (issue #600).
+ """
+ testdir.makeini("""
+ [pytest]
+ python_classes = *Suite Test
+ python_functions = *_test test
+ """)
+ p = testdir.makepyfile('''
+ class MyTestSuite(object):
+ def x_test(self):
+ pass
+
+ class TestCase(object):
+ def test_y(self):
+ pass
+ ''')
+ items, reprec = testdir.inline_genitems(p)
+ ids = [x.getmodpath() for x in items]
+ assert ids == ['MyTestSuite.x_test', 'TestCase.test_y']
+
+
+def test_matchnodes_two_collections_same_file(testdir):
+ testdir.makeconftest("""
+ import pytest
+ def pytest_configure(config):
+ config.pluginmanager.register(Plugin2())
+
+ class Plugin2(object):
+ def pytest_collect_file(self, path, parent):
+ if path.ext == ".abc":
+ return MyFile2(path, parent)
+
+ def pytest_collect_file(path, parent):
+ if path.ext == ".abc":
+ return MyFile1(path, parent)
+
+ class MyFile1(pytest.Item, pytest.File):
+ def runtest(self):
+ pass
+ class MyFile2(pytest.File):
+ def collect(self):
+ return [Item2("hello", parent=self)]
+
+ class Item2(pytest.Item):
+ def runtest(self):
+ pass
+ """)
+ p = testdir.makefile(".abc", "")
+ result = testdir.runpytest()
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*2 passed*",
+ ])
+ res = testdir.runpytest("%s::hello" % p.basename)
+ res.stdout.fnmatch_lines([
+ "*1 passed*",
+ ])
+
+
+class TestNodekeywords(object):
+ def test_no_under(self, testdir):
+ modcol = testdir.getmodulecol("""
+ def test_pass(): pass
+ def test_fail(): assert 0
+ """)
+ values = list(modcol.keywords)
+ assert modcol.name in values
+ for x in values:
+ assert not x.startswith("_")
+ assert modcol.name in repr(modcol.keywords)
+
+ def test_issue345(self, testdir):
+ testdir.makepyfile("""
+ def test_should_not_be_selected():
+ assert False, 'I should not have been selected to run'
+
+ def test___repr__():
+ pass
+ """)
+ reprec = testdir.inline_run("-k repr")
+ reprec.assertoutcome(passed=1, failed=0)
+
+
+COLLECTION_ERROR_PY_FILES = dict(
+ test_01_failure="""
+ def test_1():
+ assert False
+ """,
+ test_02_import_error="""
+ import asdfasdfasdf
+ def test_2():
+ assert True
+ """,
+ test_03_import_error="""
+ import asdfasdfasdf
+ def test_3():
+ assert True
+ """,
+ test_04_success="""
+ def test_4():
+ assert True
+ """,
+)
+
+
+def test_exit_on_collection_error(testdir):
+ """Verify that all collection errors are collected and no tests executed"""
+ testdir.makepyfile(**COLLECTION_ERROR_PY_FILES)
+
+ res = testdir.runpytest()
+ assert res.ret == 2
+
+ res.stdout.fnmatch_lines([
+ "collected 2 items / 2 errors",
+ "*ERROR collecting test_02_import_error.py*",
+ "*No module named *asdfa*",
+ "*ERROR collecting test_03_import_error.py*",
+ "*No module named *asdfa*",
+ ])
+
+
+def test_exit_on_collection_with_maxfail_smaller_than_n_errors(testdir):
+ """
+ Verify collection is aborted once maxfail errors are encountered ignoring
+ further modules which would cause more collection errors.
+ """
+ testdir.makepyfile(**COLLECTION_ERROR_PY_FILES)
+
+ res = testdir.runpytest("--maxfail=1")
+ assert res.ret == 1
+
+ res.stdout.fnmatch_lines([
+ "*ERROR collecting test_02_import_error.py*",
+ "*No module named *asdfa*",
+ ])
+
+ assert 'test_03' not in res.stdout.str()
+
+
+def test_exit_on_collection_with_maxfail_bigger_than_n_errors(testdir):
+ """
+ Verify the test run aborts due to collection errors even if maxfail count of
+ errors was not reached.
+ """
+ testdir.makepyfile(**COLLECTION_ERROR_PY_FILES)
+
+ res = testdir.runpytest("--maxfail=4")
+ assert res.ret == 2
+
+ res.stdout.fnmatch_lines([
+ "collected 2 items / 2 errors",
+ "*ERROR collecting test_02_import_error.py*",
+ "*No module named *asdfa*",
+ "*ERROR collecting test_03_import_error.py*",
+ "*No module named *asdfa*",
+ ])
+
+
+def test_continue_on_collection_errors(testdir):
+ """
+ Verify tests are executed even when collection errors occur when the
+ --continue-on-collection-errors flag is set
+ """
+ testdir.makepyfile(**COLLECTION_ERROR_PY_FILES)
+
+ res = testdir.runpytest("--continue-on-collection-errors")
+ assert res.ret == 1
+
+ res.stdout.fnmatch_lines([
+ "collected 2 items / 2 errors",
+ "*1 failed, 1 passed, 2 error*",
+ ])
+
+
+def test_continue_on_collection_errors_maxfail(testdir):
+ """
+ Verify tests are executed even when collection errors occur and that maxfail
+ is honoured (including the collection error count).
+ 4 tests: 2 collection errors + 1 failure + 1 success
+ test_4 is never executed because the test run is with --maxfail=3 which
+ means it is interrupted after the 2 collection errors + 1 failure.
+ """
+ testdir.makepyfile(**COLLECTION_ERROR_PY_FILES)
+
+ res = testdir.runpytest("--continue-on-collection-errors", "--maxfail=3")
+ assert res.ret == 1
+
+ res.stdout.fnmatch_lines([
+ "collected 2 items / 2 errors",
+ "*1 failed, 2 error*",
+ ])
+
+
+def test_fixture_scope_sibling_conftests(testdir):
+ """Regression test case for https://github.com/pytest-dev/pytest/issues/2836"""
+ foo_path = testdir.mkpydir("foo")
+ foo_path.join("conftest.py").write(_pytest._code.Source("""
+ import pytest
+ @pytest.fixture
+ def fix():
+ return 1
+ """))
+ foo_path.join("test_foo.py").write("def test_foo(fix): assert fix == 1")
+
+ # Tests in `food/` should not see the conftest fixture from `foo/`
+ food_path = testdir.mkpydir("food")
+ food_path.join("test_food.py").write("def test_food(fix): assert fix == 1")
+
+ res = testdir.runpytest()
+ assert res.ret == 1
+
+ res.stdout.fnmatch_lines([
+ "*ERROR at setup of test_food*",
+ "E*fixture 'fix' not found",
+ "*1 passed, 1 error*",
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_compat.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_compat.py
new file mode 100644
index 00000000000..c74801c6c85
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_compat.py
@@ -0,0 +1,101 @@
+from __future__ import absolute_import, division, print_function
+import sys
+
+import pytest
+from _pytest.compat import is_generator, get_real_func, safe_getattr
+from _pytest.outcomes import OutcomeException
+
+
+def test_is_generator():
+ def zap():
+ yield
+
+ def foo():
+ pass
+
+ assert is_generator(zap)
+ assert not is_generator(foo)
+
+
+def test_real_func_loop_limit():
+
+ class Evil(object):
+ def __init__(self):
+ self.left = 1000
+
+ def __repr__(self):
+ return "<Evil left={left}>".format(left=self.left)
+
+ def __getattr__(self, attr):
+ if not self.left:
+ raise RuntimeError('its over')
+ self.left -= 1
+ return self
+
+ evil = Evil()
+
+ with pytest.raises(ValueError):
+ res = get_real_func(evil)
+ print(res)
+
+
+@pytest.mark.skipif(sys.version_info < (3, 4),
+ reason='asyncio available in Python 3.4+')
+def test_is_generator_asyncio(testdir):
+ testdir.makepyfile("""
+ from _pytest.compat import is_generator
+ import asyncio
+ @asyncio.coroutine
+ def baz():
+ yield from [1,2,3]
+
+ def test_is_generator_asyncio():
+ assert not is_generator(baz)
+ """)
+ # avoid importing asyncio into pytest's own process,
+ # which in turn imports logging (#8)
+ result = testdir.runpytest_subprocess()
+ result.stdout.fnmatch_lines(['*1 passed*'])
+
+
+@pytest.mark.skipif(sys.version_info < (3, 5),
+ reason='async syntax available in Python 3.5+')
+def test_is_generator_async_syntax(testdir):
+ testdir.makepyfile("""
+ from _pytest.compat import is_generator
+ def test_is_generator_py35():
+ async def foo():
+ await foo()
+
+ async def bar():
+ pass
+
+ assert not is_generator(foo)
+ assert not is_generator(bar)
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['*1 passed*'])
+
+
+class ErrorsHelper(object):
+ @property
+ def raise_exception(self):
+ raise Exception('exception should be catched')
+
+ @property
+ def raise_fail(self):
+ pytest.fail('fail should be catched')
+
+
+def test_helper_failures():
+ helper = ErrorsHelper()
+ with pytest.raises(Exception):
+ helper.raise_exception
+ with pytest.raises(OutcomeException):
+ helper.raise_fail
+
+
+def test_safe_getattr():
+ helper = ErrorsHelper()
+ assert safe_getattr(helper, 'raise_exception', 'default') == 'default'
+ assert safe_getattr(helper, 'raise_fail', 'default') == 'default'
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_config.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_config.py
new file mode 100644
index 00000000000..44b8c317a28
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_config.py
@@ -0,0 +1,862 @@
+from __future__ import absolute_import, division, print_function
+import sys
+import py
+import pytest
+
+import _pytest._code
+from _pytest.config import getcfg, get_common_ancestor, determine_setup, _iter_rewritable_modules
+from _pytest.main import EXIT_NOTESTSCOLLECTED
+
+
+class TestParseIni(object):
+
+ @pytest.mark.parametrize('section, filename',
+ [('pytest', 'pytest.ini'), ('tool:pytest', 'setup.cfg')])
+ def test_getcfg_and_config(self, testdir, tmpdir, section, filename):
+ sub = tmpdir.mkdir("sub")
+ sub.chdir()
+ tmpdir.join(filename).write(_pytest._code.Source("""
+ [{section}]
+ name = value
+ """.format(section=section)))
+ rootdir, inifile, cfg = getcfg([sub])
+ assert cfg['name'] == "value"
+ config = testdir.parseconfigure(sub)
+ assert config.inicfg['name'] == 'value'
+
+ def test_getcfg_empty_path(self):
+ """correctly handle zero length arguments (a la pytest '')"""
+ getcfg([''])
+
+ def test_append_parse_args(self, testdir, tmpdir, monkeypatch):
+ monkeypatch.setenv('PYTEST_ADDOPTS', '--color no -rs --tb="short"')
+ tmpdir.join("pytest.ini").write(_pytest._code.Source("""
+ [pytest]
+ addopts = --verbose
+ """))
+ config = testdir.parseconfig(tmpdir)
+ assert config.option.color == 'no'
+ assert config.option.reportchars == 's'
+ assert config.option.tbstyle == 'short'
+ assert config.option.verbose
+
+ def test_tox_ini_wrong_version(self, testdir):
+ testdir.makefile('.ini', tox="""
+ [pytest]
+ minversion=9.0
+ """)
+ result = testdir.runpytest()
+ assert result.ret != 0
+ result.stderr.fnmatch_lines([
+ "*tox.ini:2*requires*9.0*actual*"
+ ])
+
+ @pytest.mark.parametrize("section, name", [
+ ('tool:pytest', 'setup.cfg'),
+ ('pytest', 'tox.ini'),
+ ('pytest', 'pytest.ini')],
+ )
+ def test_ini_names(self, testdir, name, section):
+ testdir.tmpdir.join(name).write(py.std.textwrap.dedent("""
+ [{section}]
+ minversion = 1.0
+ """.format(section=section)))
+ config = testdir.parseconfig()
+ assert config.getini("minversion") == "1.0"
+
+ def test_toxini_before_lower_pytestini(self, testdir):
+ sub = testdir.tmpdir.mkdir("sub")
+ sub.join("tox.ini").write(py.std.textwrap.dedent("""
+ [pytest]
+ minversion = 2.0
+ """))
+ testdir.tmpdir.join("pytest.ini").write(py.std.textwrap.dedent("""
+ [pytest]
+ minversion = 1.5
+ """))
+ config = testdir.parseconfigure(sub)
+ assert config.getini("minversion") == "2.0"
+
+ @pytest.mark.xfail(reason="probably not needed")
+ def test_confcutdir(self, testdir):
+ sub = testdir.mkdir("sub")
+ sub.chdir()
+ testdir.makeini("""
+ [pytest]
+ addopts = --qwe
+ """)
+ result = testdir.inline_run("--confcutdir=.")
+ assert result.ret == 0
+
+
+class TestConfigCmdlineParsing(object):
+ def test_parsing_again_fails(self, testdir):
+ config = testdir.parseconfig()
+ pytest.raises(AssertionError, lambda: config.parse([]))
+
+ def test_explicitly_specified_config_file_is_loaded(self, testdir):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addini("custom", "")
+ """)
+ testdir.makeini("""
+ [pytest]
+ custom = 0
+ """)
+ testdir.makefile(".cfg", custom="""
+ [pytest]
+ custom = 1
+ """)
+ config = testdir.parseconfig("-c", "custom.cfg")
+ assert config.getini("custom") == "1"
+
+ def test_absolute_win32_path(self, testdir):
+ temp_cfg_file = testdir.makefile(".cfg", custom="""
+ [pytest]
+ addopts = --version
+ """)
+ from os.path import normpath
+ temp_cfg_file = normpath(str(temp_cfg_file))
+ ret = pytest.main("-c " + temp_cfg_file)
+ assert ret == _pytest.main.EXIT_OK
+
+
+class TestConfigAPI(object):
+ def test_config_trace(self, testdir):
+ config = testdir.parseconfig()
+ values = []
+ config.trace.root.setwriter(values.append)
+ config.trace("hello")
+ assert len(values) == 1
+ assert values[0] == "hello [config]\n"
+
+ def test_config_getoption(self, testdir):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addoption("--hello", "-X", dest="hello")
+ """)
+ config = testdir.parseconfig("--hello=this")
+ for x in ("hello", "--hello", "-X"):
+ assert config.getoption(x) == "this"
+ pytest.raises(ValueError, "config.getoption('qweqwe')")
+
+ @pytest.mark.skipif('sys.version_info[0] < 3')
+ def test_config_getoption_unicode(self, testdir):
+ testdir.makeconftest("""
+ from __future__ import unicode_literals
+
+ def pytest_addoption(parser):
+ parser.addoption('--hello', type=str)
+ """)
+ config = testdir.parseconfig('--hello=this')
+ assert config.getoption('hello') == 'this'
+
+ def test_config_getvalueorskip(self, testdir):
+ config = testdir.parseconfig()
+ pytest.raises(pytest.skip.Exception,
+ "config.getvalueorskip('hello')")
+ verbose = config.getvalueorskip("verbose")
+ assert verbose == config.option.verbose
+
+ def test_config_getvalueorskip_None(self, testdir):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addoption("--hello")
+ """)
+ config = testdir.parseconfig()
+ with pytest.raises(pytest.skip.Exception):
+ config.getvalueorskip('hello')
+
+ def test_getoption(self, testdir):
+ config = testdir.parseconfig()
+ with pytest.raises(ValueError):
+ config.getvalue('x')
+ assert config.getoption("x", 1) == 1
+
+ def test_getconftest_pathlist(self, testdir, tmpdir):
+ somepath = tmpdir.join("x", "y", "z")
+ p = tmpdir.join("conftest.py")
+ p.write("pathlist = ['.', %r]" % str(somepath))
+ config = testdir.parseconfigure(p)
+ assert config._getconftest_pathlist('notexist', path=tmpdir) is None
+ pl = config._getconftest_pathlist('pathlist', path=tmpdir)
+ print(pl)
+ assert len(pl) == 2
+ assert pl[0] == tmpdir
+ assert pl[1] == somepath
+
+ def test_addini(self, testdir):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addini("myname", "my new ini value")
+ """)
+ testdir.makeini("""
+ [pytest]
+ myname=hello
+ """)
+ config = testdir.parseconfig()
+ val = config.getini("myname")
+ assert val == "hello"
+ pytest.raises(ValueError, config.getini, 'other')
+
+ def test_addini_pathlist(self, testdir):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addini("paths", "my new ini value", type="pathlist")
+ parser.addini("abc", "abc value")
+ """)
+ p = testdir.makeini("""
+ [pytest]
+ paths=hello world/sub.py
+ """)
+ config = testdir.parseconfig()
+ values = config.getini("paths")
+ assert len(values) == 2
+ assert values[0] == p.dirpath('hello')
+ assert values[1] == p.dirpath('world/sub.py')
+ pytest.raises(ValueError, config.getini, 'other')
+
+ def test_addini_args(self, testdir):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addini("args", "new args", type="args")
+ parser.addini("a2", "", "args", default="1 2 3".split())
+ """)
+ testdir.makeini("""
+ [pytest]
+ args=123 "123 hello" "this"
+ """)
+ config = testdir.parseconfig()
+ values = config.getini("args")
+ assert len(values) == 3
+ assert values == ["123", "123 hello", "this"]
+ values = config.getini("a2")
+ assert values == list("123")
+
+ def test_addini_linelist(self, testdir):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addini("xy", "", type="linelist")
+ parser.addini("a2", "", "linelist")
+ """)
+ testdir.makeini("""
+ [pytest]
+ xy= 123 345
+ second line
+ """)
+ config = testdir.parseconfig()
+ values = config.getini("xy")
+ assert len(values) == 2
+ assert values == ["123 345", "second line"]
+ values = config.getini("a2")
+ assert values == []
+
+ @pytest.mark.parametrize('str_val, bool_val',
+ [('True', True), ('no', False), ('no-ini', True)])
+ def test_addini_bool(self, testdir, str_val, bool_val):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addini("strip", "", type="bool", default=True)
+ """)
+ if str_val != 'no-ini':
+ testdir.makeini("""
+ [pytest]
+ strip=%s
+ """ % str_val)
+ config = testdir.parseconfig()
+ assert config.getini("strip") is bool_val
+
+ def test_addinivalue_line_existing(self, testdir):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addini("xy", "", type="linelist")
+ """)
+ testdir.makeini("""
+ [pytest]
+ xy= 123
+ """)
+ config = testdir.parseconfig()
+ values = config.getini("xy")
+ assert len(values) == 1
+ assert values == ["123"]
+ config.addinivalue_line("xy", "456")
+ values = config.getini("xy")
+ assert len(values) == 2
+ assert values == ["123", "456"]
+
+ def test_addinivalue_line_new(self, testdir):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addini("xy", "", type="linelist")
+ """)
+ config = testdir.parseconfig()
+ assert not config.getini("xy")
+ config.addinivalue_line("xy", "456")
+ values = config.getini("xy")
+ assert len(values) == 1
+ assert values == ["456"]
+ config.addinivalue_line("xy", "123")
+ values = config.getini("xy")
+ assert len(values) == 2
+ assert values == ["456", "123"]
+
+ def test_confcutdir_check_isdir(self, testdir):
+ """Give an error if --confcutdir is not a valid directory (#2078)"""
+ with pytest.raises(pytest.UsageError):
+ testdir.parseconfig('--confcutdir', testdir.tmpdir.join('file').ensure(file=1))
+ with pytest.raises(pytest.UsageError):
+ testdir.parseconfig('--confcutdir', testdir.tmpdir.join('inexistant'))
+ config = testdir.parseconfig('--confcutdir', testdir.tmpdir.join('dir').ensure(dir=1))
+ assert config.getoption('confcutdir') == str(testdir.tmpdir.join('dir'))
+
+ @pytest.mark.parametrize('names, expected', [
+ (['bar.py'], ['bar']),
+ (['foo', 'bar.py'], []),
+ (['foo', 'bar.pyc'], []),
+ (['foo', '__init__.py'], ['foo']),
+ (['foo', 'bar', '__init__.py'], []),
+ ])
+ def test_iter_rewritable_modules(self, names, expected):
+ assert list(_iter_rewritable_modules(['/'.join(names)])) == expected
+
+
+class TestConfigFromdictargs(object):
+ def test_basic_behavior(self):
+ from _pytest.config import Config
+ option_dict = {
+ 'verbose': 444,
+ 'foo': 'bar',
+ 'capture': 'no',
+ }
+ args = ['a', 'b']
+
+ config = Config.fromdictargs(option_dict, args)
+ with pytest.raises(AssertionError):
+ config.parse(['should refuse to parse again'])
+ assert config.option.verbose == 444
+ assert config.option.foo == 'bar'
+ assert config.option.capture == 'no'
+ assert config.args == args
+
+ def test_origargs(self):
+ """Show that fromdictargs can handle args in their "orig" format"""
+ from _pytest.config import Config
+ option_dict = {}
+ args = ['-vvvv', '-s', 'a', 'b']
+
+ config = Config.fromdictargs(option_dict, args)
+ assert config.args == ['a', 'b']
+ assert config._origargs == args
+ assert config.option.verbose == 4
+ assert config.option.capture == 'no'
+
+ def test_inifilename(self, tmpdir):
+ tmpdir.join("foo/bar.ini").ensure().write(_pytest._code.Source("""
+ [pytest]
+ name = value
+ """))
+
+ from _pytest.config import Config
+ inifile = '../../foo/bar.ini'
+ option_dict = {
+ 'inifilename': inifile,
+ 'capture': 'no',
+ }
+
+ cwd = tmpdir.join('a/b')
+ cwd.join('pytest.ini').ensure().write(_pytest._code.Source("""
+ [pytest]
+ name = wrong-value
+ should_not_be_set = true
+ """))
+ with cwd.ensure(dir=True).as_cwd():
+ config = Config.fromdictargs(option_dict, ())
+
+ assert config.args == [str(cwd)]
+ assert config.option.inifilename == inifile
+ assert config.option.capture == 'no'
+
+ # this indicates this is the file used for getting configuration values
+ assert config.inifile == inifile
+ assert config.inicfg.get('name') == 'value'
+ assert config.inicfg.get('should_not_be_set') is None
+
+
+def test_options_on_small_file_do_not_blow_up(testdir):
+ def runfiletest(opts):
+ reprec = testdir.inline_run(*opts)
+ passed, skipped, failed = reprec.countoutcomes()
+ assert failed == 2
+ assert skipped == passed == 0
+ path = testdir.makepyfile("""
+ def test_f1(): assert 0
+ def test_f2(): assert 0
+ """)
+
+ for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'],
+ ['--tb=long'], ['--fulltrace'],
+ ['--traceconfig'], ['-v'], ['-v', '-v']):
+ runfiletest(opts + [path])
+
+
+def test_preparse_ordering_with_setuptools(testdir, monkeypatch):
+ pkg_resources = pytest.importorskip("pkg_resources")
+
+ def my_iter(name):
+ assert name == "pytest11"
+
+ class Dist(object):
+ project_name = 'spam'
+ version = '1.0'
+
+ def _get_metadata(self, name):
+ return ['foo.txt,sha256=abc,123']
+
+ class EntryPoint(object):
+ name = "mytestplugin"
+ dist = Dist()
+
+ def load(self):
+ class PseudoPlugin(object):
+ x = 42
+ return PseudoPlugin()
+
+ return iter([EntryPoint()])
+
+ monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
+ testdir.makeconftest("""
+ pytest_plugins = "mytestplugin",
+ """)
+ monkeypatch.setenv("PYTEST_PLUGINS", "mytestplugin")
+ config = testdir.parseconfig()
+ plugin = config.pluginmanager.getplugin("mytestplugin")
+ assert plugin.x == 42
+
+
+def test_setuptools_importerror_issue1479(testdir, monkeypatch):
+ pkg_resources = pytest.importorskip("pkg_resources")
+
+ def my_iter(name):
+ assert name == "pytest11"
+
+ class Dist(object):
+ project_name = 'spam'
+ version = '1.0'
+
+ def _get_metadata(self, name):
+ return ['foo.txt,sha256=abc,123']
+
+ class EntryPoint(object):
+ name = "mytestplugin"
+ dist = Dist()
+
+ def load(self):
+ raise ImportError("Don't hide me!")
+
+ return iter([EntryPoint()])
+
+ monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
+ with pytest.raises(ImportError):
+ testdir.parseconfig()
+
+
+@pytest.mark.parametrize('block_it', [True, False])
+def test_plugin_preparse_prevents_setuptools_loading(testdir, monkeypatch, block_it):
+ pkg_resources = pytest.importorskip("pkg_resources")
+
+ plugin_module_placeholder = object()
+
+ def my_iter(name):
+ assert name == "pytest11"
+
+ class Dist(object):
+ project_name = 'spam'
+ version = '1.0'
+
+ def _get_metadata(self, name):
+ return ['foo.txt,sha256=abc,123']
+
+ class EntryPoint(object):
+ name = "mytestplugin"
+ dist = Dist()
+
+ def load(self):
+ return plugin_module_placeholder
+
+ return iter([EntryPoint()])
+
+ monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
+ args = ("-p", "no:mytestplugin") if block_it else ()
+ config = testdir.parseconfig(*args)
+ config.pluginmanager.import_plugin("mytestplugin")
+ if block_it:
+ assert "mytestplugin" not in sys.modules
+ assert config.pluginmanager.get_plugin('mytestplugin') is None
+ else:
+ assert config.pluginmanager.get_plugin('mytestplugin') is plugin_module_placeholder
+
+
+def test_cmdline_processargs_simple(testdir):
+ testdir.makeconftest("""
+ def pytest_cmdline_preparse(args):
+ args.append("-h")
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*pytest*",
+ "*-h*",
+ ])
+
+
+def test_invalid_options_show_extra_information(testdir):
+ """display extra information when pytest exits due to unrecognized
+ options in the command-line"""
+ testdir.makeini("""
+ [pytest]
+ addopts = --invalid-option
+ """)
+ result = testdir.runpytest()
+ result.stderr.fnmatch_lines([
+ "*error: unrecognized arguments: --invalid-option*",
+ "* inifile: %s*" % testdir.tmpdir.join('tox.ini'),
+ "* rootdir: %s*" % testdir.tmpdir,
+ ])
+
+
+@pytest.mark.parametrize('args', [
+ ['dir1', 'dir2', '-v'],
+ ['dir1', '-v', 'dir2'],
+ ['dir2', '-v', 'dir1'],
+ ['-v', 'dir2', 'dir1'],
+])
+def test_consider_args_after_options_for_rootdir_and_inifile(testdir, args):
+ """
+ Consider all arguments in the command-line for rootdir and inifile
+ discovery, even if they happen to occur after an option. #949
+ """
+ # replace "dir1" and "dir2" from "args" into their real directory
+ root = testdir.tmpdir.mkdir('myroot')
+ d1 = root.mkdir('dir1')
+ d2 = root.mkdir('dir2')
+ for i, arg in enumerate(args):
+ if arg == 'dir1':
+ args[i] = d1
+ elif arg == 'dir2':
+ args[i] = d2
+ with root.as_cwd():
+ result = testdir.runpytest(*args)
+ result.stdout.fnmatch_lines(['*rootdir: *myroot, inifile:'])
+
+
+@pytest.mark.skipif("sys.platform == 'win32'")
+def test_toolongargs_issue224(testdir):
+ result = testdir.runpytest("-m", "hello" * 500)
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+
+
+def test_config_in_subdirectory_colon_command_line_issue2148(testdir):
+ conftest_source = '''
+ def pytest_addoption(parser):
+ parser.addini('foo', 'foo')
+ '''
+
+ testdir.makefile('.ini', **{
+ 'pytest': '[pytest]\nfoo = root',
+ 'subdir/pytest': '[pytest]\nfoo = subdir',
+ })
+
+ testdir.makepyfile(**{
+ 'conftest': conftest_source,
+ 'subdir/conftest': conftest_source,
+ 'subdir/test_foo': '''
+ def test_foo(pytestconfig):
+ assert pytestconfig.getini('foo') == 'subdir'
+ '''})
+
+ result = testdir.runpytest('subdir/test_foo.py::test_foo')
+ assert result.ret == 0
+
+
+def test_notify_exception(testdir, capfd):
+ config = testdir.parseconfig()
+ excinfo = pytest.raises(ValueError, "raise ValueError(1)")
+ config.notify_exception(excinfo)
+ out, err = capfd.readouterr()
+ assert "ValueError" in err
+
+ class A(object):
+ def pytest_internalerror(self, excrepr):
+ return True
+
+ config.pluginmanager.register(A())
+ config.notify_exception(excinfo)
+ out, err = capfd.readouterr()
+ assert not err
+
+
+def test_load_initial_conftest_last_ordering(testdir):
+ from _pytest.config import get_config
+ pm = get_config().pluginmanager
+
+ class My(object):
+ def pytest_load_initial_conftests(self):
+ pass
+
+ m = My()
+ pm.register(m)
+ hc = pm.hook.pytest_load_initial_conftests
+ values = hc._nonwrappers + hc._wrappers
+ expected = [
+ "_pytest.config",
+ 'test_config',
+ '_pytest.capture',
+ ]
+ assert [x.function.__module__ for x in values] == expected
+
+
+def test_get_plugin_specs_as_list():
+ from _pytest.config import _get_plugin_specs_as_list
+ with pytest.raises(pytest.UsageError):
+ _get_plugin_specs_as_list(set(['foo']))
+ with pytest.raises(pytest.UsageError):
+ _get_plugin_specs_as_list(dict())
+
+ assert _get_plugin_specs_as_list(None) == []
+ assert _get_plugin_specs_as_list('') == []
+ assert _get_plugin_specs_as_list('foo') == ['foo']
+ assert _get_plugin_specs_as_list('foo,bar') == ['foo', 'bar']
+ assert _get_plugin_specs_as_list(['foo', 'bar']) == ['foo', 'bar']
+ assert _get_plugin_specs_as_list(('foo', 'bar')) == ['foo', 'bar']
+
+
+class TestWarning(object):
+ def test_warn_config(self, testdir):
+ testdir.makeconftest("""
+ values = []
+ def pytest_configure(config):
+ config.warn("C1", "hello")
+ def pytest_logwarning(code, message):
+ if message == "hello" and code == "C1":
+ values.append(1)
+ """)
+ testdir.makepyfile("""
+ def test_proper(pytestconfig):
+ import conftest
+ assert conftest.values == [1]
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_warn_on_test_item_from_request(self, testdir, request):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def fix(request):
+ request.node.warn("T1", "hello")
+
+ def test_hello(fix):
+ pass
+ """)
+ result = testdir.runpytest("--disable-pytest-warnings")
+ assert result.parseoutcomes()["warnings"] > 0
+ assert "hello" not in result.stdout.str()
+
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ ===*warnings summary*===
+ *test_warn_on_test_item_from_request.py::test_hello*
+ *hello*
+ """)
+
+
+class TestRootdir(object):
+ def test_simple_noini(self, tmpdir):
+ assert get_common_ancestor([tmpdir]) == tmpdir
+ a = tmpdir.mkdir("a")
+ assert get_common_ancestor([a, tmpdir]) == tmpdir
+ assert get_common_ancestor([tmpdir, a]) == tmpdir
+ with tmpdir.as_cwd():
+ assert get_common_ancestor([]) == tmpdir
+ no_path = tmpdir.join('does-not-exist')
+ assert get_common_ancestor([no_path]) == tmpdir
+ assert get_common_ancestor([no_path.join('a')]) == tmpdir
+
+ @pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
+ def test_with_ini(self, tmpdir, name):
+ inifile = tmpdir.join(name)
+ inifile.write("[pytest]\n")
+
+ a = tmpdir.mkdir("a")
+ b = a.mkdir("b")
+ for args in ([tmpdir], [a], [b]):
+ rootdir, inifile, inicfg = determine_setup(None, args)
+ assert rootdir == tmpdir
+ assert inifile == inifile
+ rootdir, inifile, inicfg = determine_setup(None, [b, a])
+ assert rootdir == tmpdir
+ assert inifile == inifile
+
+ @pytest.mark.parametrize("name", "setup.cfg tox.ini".split())
+ def test_pytestini_overides_empty_other(self, tmpdir, name):
+ inifile = tmpdir.ensure("pytest.ini")
+ a = tmpdir.mkdir("a")
+ a.ensure(name)
+ rootdir, inifile, inicfg = determine_setup(None, [a])
+ assert rootdir == tmpdir
+ assert inifile == inifile
+
+ def test_setuppy_fallback(self, tmpdir):
+ a = tmpdir.mkdir("a")
+ a.ensure("setup.cfg")
+ tmpdir.ensure("setup.py")
+ rootdir, inifile, inicfg = determine_setup(None, [a])
+ assert rootdir == tmpdir
+ assert inifile is None
+ assert inicfg == {}
+
+ def test_nothing(self, tmpdir, monkeypatch):
+ monkeypatch.chdir(str(tmpdir))
+ rootdir, inifile, inicfg = determine_setup(None, [tmpdir])
+ assert rootdir == tmpdir
+ assert inifile is None
+ assert inicfg == {}
+
+ def test_with_specific_inifile(self, tmpdir):
+ inifile = tmpdir.ensure("pytest.ini")
+ rootdir, inifile, inicfg = determine_setup(inifile, [tmpdir])
+ assert rootdir == tmpdir
+
+
+class TestOverrideIniArgs(object):
+ @pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
+ def test_override_ini_names(self, testdir, name):
+ testdir.tmpdir.join(name).write(py.std.textwrap.dedent("""
+ [pytest]
+ custom = 1.0"""))
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addini("custom", "")""")
+ testdir.makepyfile("""
+ def test_pass(pytestconfig):
+ ini_val = pytestconfig.getini("custom")
+ print('\\ncustom_option:%s\\n' % ini_val)""")
+
+ result = testdir.runpytest("--override-ini", "custom=2.0", "-s")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines(["custom_option:2.0"])
+
+ result = testdir.runpytest("--override-ini", "custom=2.0",
+ "--override-ini=custom=3.0", "-s")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines(["custom_option:3.0"])
+
+ def test_override_ini_pathlist(self, testdir):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ parser.addini("paths", "my new ini value", type="pathlist")""")
+ testdir.makeini("""
+ [pytest]
+ paths=blah.py""")
+ testdir.makepyfile("""
+ import py.path
+ def test_pathlist(pytestconfig):
+ config_paths = pytestconfig.getini("paths")
+ print(config_paths)
+ for cpf in config_paths:
+ print('\\nuser_path:%s' % cpf.basename)""")
+ result = testdir.runpytest("--override-ini",
+ 'paths=foo/bar1.py foo/bar2.py', "-s")
+ result.stdout.fnmatch_lines(["user_path:bar1.py",
+ "user_path:bar2.py"])
+
+ def test_override_multiple_and_default(self, testdir):
+ testdir.makeconftest("""
+ def pytest_addoption(parser):
+ addini = parser.addini
+ addini("custom_option_1", "", default="o1")
+ addini("custom_option_2", "", default="o2")
+ addini("custom_option_3", "", default=False, type="bool")
+ addini("custom_option_4", "", default=True, type="bool")""")
+ testdir.makeini("""
+ [pytest]
+ custom_option_1=custom_option_1
+ custom_option_2=custom_option_2""")
+ testdir.makepyfile("""
+ def test_multiple_options(pytestconfig):
+ prefix = "custom_option"
+ for x in range(1, 5):
+ ini_value=pytestconfig.getini("%s_%d" % (prefix, x))
+ print('\\nini%d:%s' % (x, ini_value))""")
+ result = testdir.runpytest(
+ "--override-ini", 'custom_option_1=fulldir=/tmp/user1',
+ 'custom_option_2=url=/tmp/user2?a=b&d=e',
+ "-o", 'custom_option_3=True',
+ "-o", 'custom_option_4=no', "-s")
+ result.stdout.fnmatch_lines(["ini1:fulldir=/tmp/user1",
+ "ini2:url=/tmp/user2?a=b&d=e",
+ "ini3:True",
+ "ini4:False"])
+
+ def test_override_ini_usage_error_bad_style(self, testdir):
+ testdir.makeini("""
+ [pytest]
+ xdist_strict=False
+ """)
+ result = testdir.runpytest("--override-ini", 'xdist_strict True', "-s")
+ result.stderr.fnmatch_lines(["*ERROR* *expects option=value*"])
+
+ @pytest.mark.parametrize('with_ini', [True, False])
+ def test_override_ini_handled_asap(self, testdir, with_ini):
+ """-o should be handled as soon as possible and always override what's in ini files (#2238)"""
+ if with_ini:
+ testdir.makeini("""
+ [pytest]
+ python_files=test_*.py
+ """)
+ testdir.makepyfile(unittest_ini_handle="""
+ def test():
+ pass
+ """)
+ result = testdir.runpytest("--override-ini", 'python_files=unittest_*.py')
+ result.stdout.fnmatch_lines(["*1 passed in*"])
+
+ def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch):
+ monkeypatch.chdir(str(tmpdir))
+ a = tmpdir.mkdir("a")
+ b = tmpdir.mkdir("b")
+ rootdir, inifile, inicfg = determine_setup(None, [a, b])
+ assert rootdir == tmpdir
+ assert inifile is None
+
+ def test_with_arg_outside_cwd_with_inifile(self, tmpdir):
+ a = tmpdir.mkdir("a")
+ b = tmpdir.mkdir("b")
+ inifile = a.ensure("pytest.ini")
+ rootdir, parsed_inifile, inicfg = determine_setup(None, [a, b])
+ assert rootdir == a
+ assert inifile == parsed_inifile
+
+ @pytest.mark.parametrize('dirs', ([], ['does-not-exist'],
+ ['a/does-not-exist']))
+ def test_with_non_dir_arg(self, dirs, tmpdir):
+ with tmpdir.ensure(dir=True).as_cwd():
+ rootdir, inifile, inicfg = determine_setup(None, dirs)
+ assert rootdir == tmpdir
+ assert inifile is None
+
+ def test_with_existing_file_in_subdir(self, tmpdir):
+ a = tmpdir.mkdir("a")
+ a.ensure("exist")
+ with tmpdir.as_cwd():
+ rootdir, inifile, inicfg = determine_setup(None, ['a/exist'])
+ assert rootdir == tmpdir
+ assert inifile is None
+
+ def test_addopts_before_initini(self, testdir, tmpdir, monkeypatch):
+ cache_dir = '.custom_cache'
+ monkeypatch.setenv('PYTEST_ADDOPTS', '-o cache_dir=%s' % cache_dir)
+ from _pytest.config import get_config
+ config = get_config()
+ config._preparse([], addopts=True)
+ assert config._override_ini == [['cache_dir=%s' % cache_dir]]
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_conftest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_conftest.py
new file mode 100644
index 00000000000..c0411b72321
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_conftest.py
@@ -0,0 +1,478 @@
+from __future__ import absolute_import, division, print_function
+from textwrap import dedent
+
+import _pytest._code
+import py
+import pytest
+from _pytest.config import PytestPluginManager
+from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
+
+
+@pytest.fixture(scope="module", params=["global", "inpackage"])
+def basedir(request, tmpdir_factory):
+ from _pytest.tmpdir import tmpdir
+ tmpdir = tmpdir(request, tmpdir_factory)
+ tmpdir.ensure("adir/conftest.py").write("a=1 ; Directory = 3")
+ tmpdir.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5")
+ if request.param == "inpackage":
+ tmpdir.ensure("adir/__init__.py")
+ tmpdir.ensure("adir/b/__init__.py")
+ return tmpdir
+
+
+def ConftestWithSetinitial(path):
+ conftest = PytestPluginManager()
+ conftest_setinitial(conftest, [path])
+ return conftest
+
+
+def conftest_setinitial(conftest, args, confcutdir=None):
+ class Namespace(object):
+ def __init__(self):
+ self.file_or_dir = args
+ self.confcutdir = str(confcutdir)
+ self.noconftest = False
+ conftest._set_initial_conftests(Namespace())
+
+
+class TestConftestValueAccessGlobal(object):
+ def test_basic_init(self, basedir):
+ conftest = PytestPluginManager()
+ p = basedir.join("adir")
+ assert conftest._rget_with_confmod("a", p)[1] == 1
+
+ def test_immediate_initialiation_and_incremental_are_the_same(self, basedir):
+ conftest = PytestPluginManager()
+ len(conftest._path2confmods)
+ conftest._getconftestmodules(basedir)
+ snap1 = len(conftest._path2confmods)
+ # assert len(conftest._path2confmods) == snap1 + 1
+ conftest._getconftestmodules(basedir.join('adir'))
+ assert len(conftest._path2confmods) == snap1 + 1
+ conftest._getconftestmodules(basedir.join('b'))
+ assert len(conftest._path2confmods) == snap1 + 2
+
+ def test_value_access_not_existing(self, basedir):
+ conftest = ConftestWithSetinitial(basedir)
+ with pytest.raises(KeyError):
+ conftest._rget_with_confmod('a', basedir)
+
+ def test_value_access_by_path(self, basedir):
+ conftest = ConftestWithSetinitial(basedir)
+ adir = basedir.join("adir")
+ assert conftest._rget_with_confmod("a", adir)[1] == 1
+ assert conftest._rget_with_confmod("a", adir.join("b"))[1] == 1.5
+
+ def test_value_access_with_confmod(self, basedir):
+ startdir = basedir.join("adir", "b")
+ startdir.ensure("xx", dir=True)
+ conftest = ConftestWithSetinitial(startdir)
+ mod, value = conftest._rget_with_confmod("a", startdir)
+ assert value == 1.5
+ path = py.path.local(mod.__file__)
+ assert path.dirpath() == basedir.join("adir", "b")
+ assert path.purebasename.startswith("conftest")
+
+
+def test_conftest_in_nonpkg_with_init(tmpdir):
+ tmpdir.ensure("adir-1.0/conftest.py").write("a=1 ; Directory = 3")
+ tmpdir.ensure("adir-1.0/b/conftest.py").write("b=2 ; a = 1.5")
+ tmpdir.ensure("adir-1.0/b/__init__.py")
+ tmpdir.ensure("adir-1.0/__init__.py")
+ ConftestWithSetinitial(tmpdir.join("adir-1.0", "b"))
+
+
+def test_doubledash_considered(testdir):
+ conf = testdir.mkdir("--option")
+ conf.join("conftest.py").ensure()
+ conftest = PytestPluginManager()
+ conftest_setinitial(conftest, [conf.basename, conf.basename])
+ values = conftest._getconftestmodules(conf)
+ assert len(values) == 1
+
+
+def test_issue151_load_all_conftests(testdir):
+ names = "code proj src".split()
+ for name in names:
+ p = testdir.mkdir(name)
+ p.ensure("conftest.py")
+
+ conftest = PytestPluginManager()
+ conftest_setinitial(conftest, names)
+ d = list(conftest._conftestpath2mod.values())
+ assert len(d) == len(names)
+
+
+def test_conftest_global_import(testdir):
+ testdir.makeconftest("x=3")
+ p = testdir.makepyfile("""
+ import py, pytest
+ from _pytest.config import PytestPluginManager
+ conf = PytestPluginManager()
+ mod = conf._importconftest(py.path.local("conftest.py"))
+ assert mod.x == 3
+ import conftest
+ assert conftest is mod, (conftest, mod)
+ subconf = py.path.local().ensure("sub", "conftest.py")
+ subconf.write("y=4")
+ mod2 = conf._importconftest(subconf)
+ assert mod != mod2
+ assert mod2.y == 4
+ import conftest
+ assert conftest is mod2, (conftest, mod)
+ """)
+ res = testdir.runpython(p)
+ assert res.ret == 0
+
+
+def test_conftestcutdir(testdir):
+ conf = testdir.makeconftest("")
+ p = testdir.mkdir("x")
+ conftest = PytestPluginManager()
+ conftest_setinitial(conftest, [testdir.tmpdir], confcutdir=p)
+ values = conftest._getconftestmodules(p)
+ assert len(values) == 0
+ values = conftest._getconftestmodules(conf.dirpath())
+ assert len(values) == 0
+ assert conf not in conftest._conftestpath2mod
+ # but we can still import a conftest directly
+ conftest._importconftest(conf)
+ values = conftest._getconftestmodules(conf.dirpath())
+ assert values[0].__file__.startswith(str(conf))
+ # and all sub paths get updated properly
+ values = conftest._getconftestmodules(p)
+ assert len(values) == 1
+ assert values[0].__file__.startswith(str(conf))
+
+
+def test_conftestcutdir_inplace_considered(testdir):
+ conf = testdir.makeconftest("")
+ conftest = PytestPluginManager()
+ conftest_setinitial(conftest, [conf.dirpath()], confcutdir=conf.dirpath())
+ values = conftest._getconftestmodules(conf.dirpath())
+ assert len(values) == 1
+ assert values[0].__file__.startswith(str(conf))
+
+
+@pytest.mark.parametrize("name", 'test tests whatever .dotdir'.split())
+def test_setinitial_conftest_subdirs(testdir, name):
+ sub = testdir.mkdir(name)
+ subconftest = sub.ensure("conftest.py")
+ conftest = PytestPluginManager()
+ conftest_setinitial(conftest, [sub.dirpath()], confcutdir=testdir.tmpdir)
+ if name not in ('whatever', '.dotdir'):
+ assert subconftest in conftest._conftestpath2mod
+ assert len(conftest._conftestpath2mod) == 1
+ else:
+ assert subconftest not in conftest._conftestpath2mod
+ assert len(conftest._conftestpath2mod) == 0
+
+
+def test_conftest_confcutdir(testdir):
+ testdir.makeconftest("assert 0")
+ x = testdir.mkdir("x")
+ x.join("conftest.py").write(_pytest._code.Source("""
+ def pytest_addoption(parser):
+ parser.addoption("--xyz", action="store_true")
+ """))
+ result = testdir.runpytest("-h", "--confcutdir=%s" % x, x)
+ result.stdout.fnmatch_lines(["*--xyz*"])
+ assert 'warning: could not load initial' not in result.stdout.str()
+
+
+def test_no_conftest(testdir):
+ testdir.makeconftest("assert 0")
+ result = testdir.runpytest("--noconftest")
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+
+ result = testdir.runpytest()
+ assert result.ret == EXIT_USAGEERROR
+
+
+def test_conftest_existing_resultlog(testdir):
+ x = testdir.mkdir("tests")
+ x.join("conftest.py").write(_pytest._code.Source("""
+ def pytest_addoption(parser):
+ parser.addoption("--xyz", action="store_true")
+ """))
+ testdir.makefile(ext=".log", result="") # Writes result.log
+ result = testdir.runpytest("-h", "--resultlog", "result.log")
+ result.stdout.fnmatch_lines(["*--xyz*"])
+
+
+def test_conftest_existing_junitxml(testdir):
+ x = testdir.mkdir("tests")
+ x.join("conftest.py").write(_pytest._code.Source("""
+ def pytest_addoption(parser):
+ parser.addoption("--xyz", action="store_true")
+ """))
+ testdir.makefile(ext=".xml", junit="") # Writes junit.xml
+ result = testdir.runpytest("-h", "--junitxml", "junit.xml")
+ result.stdout.fnmatch_lines(["*--xyz*"])
+
+
+def test_conftest_import_order(testdir, monkeypatch):
+ ct1 = testdir.makeconftest("")
+ sub = testdir.mkdir("sub")
+ ct2 = sub.join("conftest.py")
+ ct2.write("")
+
+ def impct(p):
+ return p
+
+ conftest = PytestPluginManager()
+ conftest._confcutdir = testdir.tmpdir
+ monkeypatch.setattr(conftest, '_importconftest', impct)
+ assert conftest._getconftestmodules(sub) == [ct1, ct2]
+
+
+def test_fixture_dependency(testdir, monkeypatch):
+ ct1 = testdir.makeconftest("")
+ ct1 = testdir.makepyfile("__init__.py")
+ ct1.write("")
+ sub = testdir.mkdir("sub")
+ sub.join("__init__.py").write("")
+ sub.join("conftest.py").write(py.std.textwrap.dedent("""
+ import pytest
+
+ @pytest.fixture
+ def not_needed():
+ assert False, "Should not be called!"
+
+ @pytest.fixture
+ def foo():
+ assert False, "Should not be called!"
+
+ @pytest.fixture
+ def bar(foo):
+ return 'bar'
+ """))
+ subsub = sub.mkdir("subsub")
+ subsub.join("__init__.py").write("")
+ subsub.join("test_bar.py").write(py.std.textwrap.dedent("""
+ import pytest
+
+ @pytest.fixture
+ def bar():
+ return 'sub bar'
+
+ def test_event_fixture(bar):
+ assert bar == 'sub bar'
+ """))
+ result = testdir.runpytest("sub")
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+
+def test_conftest_found_with_double_dash(testdir):
+ sub = testdir.mkdir("sub")
+ sub.join("conftest.py").write(py.std.textwrap.dedent("""
+ def pytest_addoption(parser):
+ parser.addoption("--hello-world", action="store_true")
+ """))
+ p = sub.join("test_hello.py")
+ p.write(py.std.textwrap.dedent("""
+ import pytest
+ def test_hello(found):
+ assert found == 1
+ """))
+ result = testdir.runpytest(str(p) + "::test_hello", "-h")
+ result.stdout.fnmatch_lines("""
+ *--hello-world*
+ """)
+
+
+class TestConftestVisibility(object):
+ def _setup_tree(self, testdir): # for issue616
+ # example mostly taken from:
+ # https://mail.python.org/pipermail/pytest-dev/2014-September/002617.html
+ runner = testdir.mkdir("empty")
+ package = testdir.mkdir("package")
+
+ package.join("conftest.py").write(dedent("""\
+ import pytest
+ @pytest.fixture
+ def fxtr():
+ return "from-package"
+ """))
+ package.join("test_pkgroot.py").write(dedent("""\
+ def test_pkgroot(fxtr):
+ assert fxtr == "from-package"
+ """))
+
+ swc = package.mkdir("swc")
+ swc.join("__init__.py").ensure()
+ swc.join("conftest.py").write(dedent("""\
+ import pytest
+ @pytest.fixture
+ def fxtr():
+ return "from-swc"
+ """))
+ swc.join("test_with_conftest.py").write(dedent("""\
+ def test_with_conftest(fxtr):
+ assert fxtr == "from-swc"
+
+ """))
+
+ snc = package.mkdir("snc")
+ snc.join("__init__.py").ensure()
+ snc.join("test_no_conftest.py").write(dedent("""\
+ def test_no_conftest(fxtr):
+ assert fxtr == "from-package" # No local conftest.py, so should
+ # use value from parent dir's
+
+ """))
+ print("created directory structure:")
+ for x in testdir.tmpdir.visit():
+ print(" " + x.relto(testdir.tmpdir))
+
+ return {
+ "runner": runner,
+ "package": package,
+ "swc": swc,
+ "snc": snc}
+
+ # N.B.: "swc" stands for "subdir with conftest.py"
+ # "snc" stands for "subdir no [i.e. without] conftest.py"
+ @pytest.mark.parametrize("chdir,testarg,expect_ntests_passed", [
+ # Effective target: package/..
+ ("runner", "..", 3),
+ ("package", "..", 3),
+ ("swc", "../..", 3),
+ ("snc", "../..", 3),
+
+ # Effective target: package
+ ("runner", "../package", 3),
+ ("package", ".", 3),
+ ("swc", "..", 3),
+ ("snc", "..", 3),
+
+ # Effective target: package/swc
+ ("runner", "../package/swc", 1),
+ ("package", "./swc", 1),
+ ("swc", ".", 1),
+ ("snc", "../swc", 1),
+
+ # Effective target: package/snc
+ ("runner", "../package/snc", 1),
+ ("package", "./snc", 1),
+ ("swc", "../snc", 1),
+ ("snc", ".", 1),
+ ])
+ @pytest.mark.issue616
+ def test_parsefactories_relative_node_ids(
+ self, testdir, chdir, testarg, expect_ntests_passed):
+ dirs = self._setup_tree(testdir)
+ print("pytest run in cwd: %s" % (
+ dirs[chdir].relto(testdir.tmpdir)))
+ print("pytestarg : %s" % (testarg))
+ print("expected pass : %s" % (expect_ntests_passed))
+ with dirs[chdir].as_cwd():
+ reprec = testdir.inline_run(testarg, "-q", "--traceconfig")
+ reprec.assertoutcome(passed=expect_ntests_passed)
+
+
+@pytest.mark.parametrize('confcutdir,passed,error', [
+ ('.', 2, 0),
+ ('src', 1, 1),
+ (None, 1, 1),
+])
+def test_search_conftest_up_to_inifile(testdir, confcutdir, passed, error):
+ """Test that conftest files are detected only up to a ini file, unless
+ an explicit --confcutdir option is given.
+ """
+ root = testdir.tmpdir
+ src = root.join('src').ensure(dir=1)
+ src.join('pytest.ini').write('[pytest]')
+ src.join('conftest.py').write(_pytest._code.Source("""
+ import pytest
+ @pytest.fixture
+ def fix1(): pass
+ """))
+ src.join('test_foo.py').write(_pytest._code.Source("""
+ def test_1(fix1):
+ pass
+ def test_2(out_of_reach):
+ pass
+ """))
+ root.join('conftest.py').write(_pytest._code.Source("""
+ import pytest
+ @pytest.fixture
+ def out_of_reach(): pass
+ """))
+
+ args = [str(src)]
+ if confcutdir:
+ args = ['--confcutdir=%s' % root.join(confcutdir)]
+ result = testdir.runpytest(*args)
+ match = ''
+ if passed:
+ match += '*%d passed*' % passed
+ if error:
+ match += '*%d error*' % error
+ result.stdout.fnmatch_lines(match)
+
+
+def test_issue1073_conftest_special_objects(testdir):
+ testdir.makeconftest("""
+ class DontTouchMe(object):
+ def __getattr__(self, x):
+ raise Exception('cant touch me')
+
+ x = DontTouchMe()
+ """)
+ testdir.makepyfile("""
+ def test_some():
+ pass
+ """)
+ res = testdir.runpytest()
+ assert res.ret == 0
+
+
+def test_conftest_exception_handling(testdir):
+ testdir.makeconftest('''
+ raise ValueError()
+ ''')
+ testdir.makepyfile("""
+ def test_some():
+ pass
+ """)
+ res = testdir.runpytest()
+ assert res.ret == 4
+ assert 'raise ValueError()' in [line.strip() for line in res.errlines]
+
+
+def test_hook_proxy(testdir):
+ """Session's gethookproxy() would cache conftests incorrectly (#2016).
+ It was decided to remove the cache altogether.
+ """
+ testdir.makepyfile(**{
+ 'root/demo-0/test_foo1.py': "def test1(): pass",
+
+ 'root/demo-a/test_foo2.py': "def test1(): pass",
+ 'root/demo-a/conftest.py': """
+ def pytest_ignore_collect(path, config):
+ return True
+ """,
+
+ 'root/demo-b/test_foo3.py': "def test1(): pass",
+ 'root/demo-c/test_foo4.py': "def test1(): pass",
+ })
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*test_foo1.py*',
+ '*test_foo3.py*',
+ '*test_foo4.py*',
+ '*3 passed*',
+ ])
+
+
+def test_required_option_help(testdir):
+ testdir.makeconftest("assert 0")
+ x = testdir.mkdir("x")
+ x.join("conftest.py").write(_pytest._code.Source("""
+ def pytest_addoption(parser):
+ parser.addoption("--xyz", action="store_true", required=True)
+ """))
+ result = testdir.runpytest("-h", x)
+ assert 'argument --xyz is required' not in result.stdout.str()
+ assert 'general:' in result.stdout.str()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_doctest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_doctest.py
new file mode 100644
index 00000000000..b15067f15e9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_doctest.py
@@ -0,0 +1,1003 @@
+# encoding: utf-8
+from __future__ import absolute_import, division, print_function
+import sys
+import _pytest._code
+from _pytest.compat import MODULE_NOT_FOUND_ERROR
+from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile
+import pytest
+
+
+class TestDoctests(object):
+
+ def test_collect_testtextfile(self, testdir):
+ w = testdir.maketxtfile(whatever="")
+ checkfile = testdir.maketxtfile(test_something="""
+ alskdjalsdk
+ >>> i = 5
+ >>> i-1
+ 4
+ """)
+
+ for x in (testdir.tmpdir, checkfile):
+ # print "checking that %s returns custom items" % (x,)
+ items, reprec = testdir.inline_genitems(x)
+ assert len(items) == 1
+ assert isinstance(items[0], DoctestItem)
+ assert isinstance(items[0].parent, DoctestTextfile)
+ # Empty file has no items.
+ items, reprec = testdir.inline_genitems(w)
+ assert len(items) == 0
+
+ def test_collect_module_empty(self, testdir):
+ path = testdir.makepyfile(whatever="#")
+ for p in (path, testdir.tmpdir):
+ items, reprec = testdir.inline_genitems(p,
+ '--doctest-modules')
+ assert len(items) == 0
+
+ def test_collect_module_single_modulelevel_doctest(self, testdir):
+ path = testdir.makepyfile(whatever='""">>> pass"""')
+ for p in (path, testdir.tmpdir):
+ items, reprec = testdir.inline_genitems(p,
+ '--doctest-modules')
+ assert len(items) == 1
+ assert isinstance(items[0], DoctestItem)
+ assert isinstance(items[0].parent, DoctestModule)
+
+ def test_collect_module_two_doctest_one_modulelevel(self, testdir):
+ path = testdir.makepyfile(whatever="""
+ '>>> x = None'
+ def my_func():
+ ">>> magic = 42 "
+ """)
+ for p in (path, testdir.tmpdir):
+ items, reprec = testdir.inline_genitems(p,
+ '--doctest-modules')
+ assert len(items) == 2
+ assert isinstance(items[0], DoctestItem)
+ assert isinstance(items[1], DoctestItem)
+ assert isinstance(items[0].parent, DoctestModule)
+ assert items[0].parent is items[1].parent
+
+ def test_collect_module_two_doctest_no_modulelevel(self, testdir):
+ path = testdir.makepyfile(whatever="""
+ '# Empty'
+ def my_func():
+ ">>> magic = 42 "
+ def unuseful():
+ '''
+ # This is a function
+ # >>> # it doesn't have any doctest
+ '''
+ def another():
+ '''
+ # This is another function
+ >>> import os # this one does have a doctest
+ '''
+ """)
+ for p in (path, testdir.tmpdir):
+ items, reprec = testdir.inline_genitems(p,
+ '--doctest-modules')
+ assert len(items) == 2
+ assert isinstance(items[0], DoctestItem)
+ assert isinstance(items[1], DoctestItem)
+ assert isinstance(items[0].parent, DoctestModule)
+ assert items[0].parent is items[1].parent
+
+ def test_simple_doctestfile(self, testdir):
+ p = testdir.maketxtfile(test_doc="""
+ >>> x = 1
+ >>> x == 1
+ False
+ """)
+ reprec = testdir.inline_run(p, )
+ reprec.assertoutcome(failed=1)
+
+ def test_new_pattern(self, testdir):
+ p = testdir.maketxtfile(xdoc="""
+ >>> x = 1
+ >>> x == 1
+ False
+ """)
+ reprec = testdir.inline_run(p, "--doctest-glob=x*.txt")
+ reprec.assertoutcome(failed=1)
+
+ def test_multiple_patterns(self, testdir):
+ """Test support for multiple --doctest-glob arguments (#1255).
+ """
+ testdir.maketxtfile(xdoc="""
+ >>> 1
+ 1
+ """)
+ testdir.makefile('.foo', test="""
+ >>> 1
+ 1
+ """)
+ testdir.maketxtfile(test_normal="""
+ >>> 1
+ 1
+ """)
+ expected = set(['xdoc.txt', 'test.foo', 'test_normal.txt'])
+ assert set(x.basename for x in testdir.tmpdir.listdir()) == expected
+ args = ["--doctest-glob=xdoc*.txt", "--doctest-glob=*.foo"]
+ result = testdir.runpytest(*args)
+ result.stdout.fnmatch_lines([
+ '*test.foo *',
+ '*xdoc.txt *',
+ '*2 passed*',
+ ])
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*test_normal.txt *',
+ '*1 passed*',
+ ])
+
+ @pytest.mark.parametrize(
+ ' test_string, encoding',
+ [
+ (u'foo', 'ascii'),
+ (u'öäü', 'latin1'),
+ (u'öäü', 'utf-8')
+ ]
+ )
+ def test_encoding(self, testdir, test_string, encoding):
+ """Test support for doctest_encoding ini option.
+ """
+ testdir.makeini("""
+ [pytest]
+ doctest_encoding={0}
+ """.format(encoding))
+ doctest = u"""
+ >>> u"{0}"
+ {1}
+ """.format(test_string, repr(test_string))
+ testdir._makefile(".txt", [doctest], {}, encoding=encoding)
+
+ result = testdir.runpytest()
+
+ result.stdout.fnmatch_lines([
+ '*1 passed*',
+ ])
+
+ def test_doctest_unexpected_exception(self, testdir):
+ testdir.maketxtfile("""
+ >>> i = 0
+ >>> 0 / i
+ 2
+ """)
+ result = testdir.runpytest("--doctest-modules")
+ result.stdout.fnmatch_lines([
+ "*unexpected_exception*",
+ "*>>> i = 0*",
+ "*>>> 0 / i*",
+ "*UNEXPECTED*ZeroDivision*",
+ ])
+
+ def test_docstring_partial_context_around_error(self, testdir):
+ """Test that we show some context before the actual line of a failing
+ doctest.
+ """
+ testdir.makepyfile('''
+ def foo():
+ """
+ text-line-1
+ text-line-2
+ text-line-3
+ text-line-4
+ text-line-5
+ text-line-6
+ text-line-7
+ text-line-8
+ text-line-9
+ text-line-10
+ text-line-11
+ >>> 1 + 1
+ 3
+
+ text-line-after
+ """
+ ''')
+ result = testdir.runpytest('--doctest-modules')
+ result.stdout.fnmatch_lines([
+ '*docstring_partial_context_around_error*',
+ '005*text-line-3',
+ '006*text-line-4',
+ '013*text-line-11',
+ '014*>>> 1 + 1',
+ 'Expected:',
+ ' 3',
+ 'Got:',
+ ' 2',
+ ])
+ # lines below should be trimmed out
+ assert 'text-line-2' not in result.stdout.str()
+ assert 'text-line-after' not in result.stdout.str()
+
+ def test_docstring_full_context_around_error(self, testdir):
+ """Test that we show the whole context before the actual line of a failing
+ doctest, provided that the context is up to 10 lines long.
+ """
+ testdir.makepyfile('''
+ def foo():
+ """
+ text-line-1
+ text-line-2
+
+ >>> 1 + 1
+ 3
+ """
+ ''')
+ result = testdir.runpytest('--doctest-modules')
+ result.stdout.fnmatch_lines([
+ '*docstring_full_context_around_error*',
+ '003*text-line-1',
+ '004*text-line-2',
+ '006*>>> 1 + 1',
+ 'Expected:',
+ ' 3',
+ 'Got:',
+ ' 2',
+ ])
+
+ def test_doctest_linedata_missing(self, testdir):
+ testdir.tmpdir.join('hello.py').write(_pytest._code.Source("""
+ class Fun(object):
+ @property
+ def test(self):
+ '''
+ >>> a = 1
+ >>> 1/0
+ '''
+ """))
+ result = testdir.runpytest("--doctest-modules")
+ result.stdout.fnmatch_lines([
+ "*hello*",
+ "*EXAMPLE LOCATION UNKNOWN, not showing all tests of that example*",
+ "*1/0*",
+ "*UNEXPECTED*ZeroDivision*",
+ "*1 failed*",
+ ])
+
+ def test_doctest_unex_importerror_only_txt(self, testdir):
+ testdir.maketxtfile("""
+ >>> import asdalsdkjaslkdjasd
+ >>>
+ """)
+ result = testdir.runpytest()
+ # doctest is never executed because of error during hello.py collection
+ result.stdout.fnmatch_lines([
+ "*>>> import asdals*",
+ "*UNEXPECTED*{e}*".format(e=MODULE_NOT_FOUND_ERROR),
+ "{e}: No module named *asdal*".format(e=MODULE_NOT_FOUND_ERROR),
+ ])
+
+ def test_doctest_unex_importerror_with_module(self, testdir):
+ testdir.tmpdir.join("hello.py").write(_pytest._code.Source("""
+ import asdalsdkjaslkdjasd
+ """))
+ testdir.maketxtfile("""
+ >>> import hello
+ >>>
+ """)
+ result = testdir.runpytest("--doctest-modules")
+ # doctest is never executed because of error during hello.py collection
+ result.stdout.fnmatch_lines([
+ "*ERROR collecting hello.py*",
+ "*{e}: No module named *asdals*".format(e=MODULE_NOT_FOUND_ERROR),
+ "*Interrupted: 1 errors during collection*",
+ ])
+
+ def test_doctestmodule(self, testdir):
+ p = testdir.makepyfile("""
+ '''
+ >>> x = 1
+ >>> x == 1
+ False
+
+ '''
+ """)
+ reprec = testdir.inline_run(p, "--doctest-modules")
+ reprec.assertoutcome(failed=1)
+
+ def test_doctestmodule_external_and_issue116(self, testdir):
+ p = testdir.mkpydir("hello")
+ p.join("__init__.py").write(_pytest._code.Source("""
+ def somefunc():
+ '''
+ >>> i = 0
+ >>> i + 1
+ 2
+ '''
+ """))
+ result = testdir.runpytest(p, "--doctest-modules")
+ result.stdout.fnmatch_lines([
+ '004 *>>> i = 0',
+ '005 *>>> i + 1',
+ '*Expected:',
+ "* 2",
+ "*Got:",
+ "* 1",
+ "*:5: DocTestFailure"
+ ])
+
+ def test_txtfile_failing(self, testdir):
+ p = testdir.maketxtfile("""
+ >>> i = 0
+ >>> i + 1
+ 2
+ """)
+ result = testdir.runpytest(p, "-s")
+ result.stdout.fnmatch_lines([
+ '001 >>> i = 0',
+ '002 >>> i + 1',
+ 'Expected:',
+ " 2",
+ "Got:",
+ " 1",
+ "*test_txtfile_failing.txt:2: DocTestFailure"
+ ])
+
+ def test_txtfile_with_fixtures(self, testdir):
+ p = testdir.maketxtfile("""
+ >>> dir = getfixture('tmpdir')
+ >>> type(dir).__name__
+ 'LocalPath'
+ """)
+ reprec = testdir.inline_run(p, )
+ reprec.assertoutcome(passed=1)
+
+ def test_txtfile_with_usefixtures_in_ini(self, testdir):
+ testdir.makeini("""
+ [pytest]
+ usefixtures = myfixture
+ """)
+ testdir.makeconftest("""
+ import pytest
+ @pytest.fixture
+ def myfixture(monkeypatch):
+ monkeypatch.setenv("HELLO", "WORLD")
+ """)
+
+ p = testdir.maketxtfile("""
+ >>> import os
+ >>> os.environ["HELLO"]
+ 'WORLD'
+ """)
+ reprec = testdir.inline_run(p, )
+ reprec.assertoutcome(passed=1)
+
+ def test_doctestmodule_with_fixtures(self, testdir):
+ p = testdir.makepyfile("""
+ '''
+ >>> dir = getfixture('tmpdir')
+ >>> type(dir).__name__
+ 'LocalPath'
+ '''
+ """)
+ reprec = testdir.inline_run(p, "--doctest-modules")
+ reprec.assertoutcome(passed=1)
+
+ def test_doctestmodule_three_tests(self, testdir):
+ p = testdir.makepyfile("""
+ '''
+ >>> dir = getfixture('tmpdir')
+ >>> type(dir).__name__
+ 'LocalPath'
+ '''
+ def my_func():
+ '''
+ >>> magic = 42
+ >>> magic - 42
+ 0
+ '''
+ def unuseful():
+ pass
+ def another():
+ '''
+ >>> import os
+ >>> os is os
+ True
+ '''
+ """)
+ reprec = testdir.inline_run(p, "--doctest-modules")
+ reprec.assertoutcome(passed=3)
+
+ def test_doctestmodule_two_tests_one_fail(self, testdir):
+ p = testdir.makepyfile("""
+ class MyClass(object):
+ def bad_meth(self):
+ '''
+ >>> magic = 42
+ >>> magic
+ 0
+ '''
+ def nice_meth(self):
+ '''
+ >>> magic = 42
+ >>> magic - 42
+ 0
+ '''
+ """)
+ reprec = testdir.inline_run(p, "--doctest-modules")
+ reprec.assertoutcome(failed=1, passed=1)
+
+ def test_ignored_whitespace(self, testdir):
+ testdir.makeini("""
+ [pytest]
+ doctest_optionflags = ELLIPSIS NORMALIZE_WHITESPACE
+ """)
+ p = testdir.makepyfile("""
+ class MyClass(object):
+ '''
+ >>> a = "foo "
+ >>> print(a)
+ foo
+ '''
+ pass
+ """)
+ reprec = testdir.inline_run(p, "--doctest-modules")
+ reprec.assertoutcome(passed=1)
+
+ def test_non_ignored_whitespace(self, testdir):
+ testdir.makeini("""
+ [pytest]
+ doctest_optionflags = ELLIPSIS
+ """)
+ p = testdir.makepyfile("""
+ class MyClass(object):
+ '''
+ >>> a = "foo "
+ >>> print(a)
+ foo
+ '''
+ pass
+ """)
+ reprec = testdir.inline_run(p, "--doctest-modules")
+ reprec.assertoutcome(failed=1, passed=0)
+
+ def test_ignored_whitespace_glob(self, testdir):
+ testdir.makeini("""
+ [pytest]
+ doctest_optionflags = ELLIPSIS NORMALIZE_WHITESPACE
+ """)
+ p = testdir.maketxtfile(xdoc="""
+ >>> a = "foo "
+ >>> print(a)
+ foo
+ """)
+ reprec = testdir.inline_run(p, "--doctest-glob=x*.txt")
+ reprec.assertoutcome(passed=1)
+
+ def test_non_ignored_whitespace_glob(self, testdir):
+ testdir.makeini("""
+ [pytest]
+ doctest_optionflags = ELLIPSIS
+ """)
+ p = testdir.maketxtfile(xdoc="""
+ >>> a = "foo "
+ >>> print(a)
+ foo
+ """)
+ reprec = testdir.inline_run(p, "--doctest-glob=x*.txt")
+ reprec.assertoutcome(failed=1, passed=0)
+
+ def test_contains_unicode(self, testdir):
+ """Fix internal error with docstrings containing non-ascii characters.
+ """
+ testdir.makepyfile(u'''
+ # encoding: utf-8
+ def foo():
+ """
+ >>> name = 'с' # not letter 'c' but instead Cyrillic 's'.
+ 'anything'
+ """
+ ''')
+ result = testdir.runpytest('--doctest-modules')
+ result.stdout.fnmatch_lines([
+ 'Got nothing',
+ '* 1 failed in*',
+ ])
+
+ def test_ignore_import_errors_on_doctest(self, testdir):
+ p = testdir.makepyfile("""
+ import asdf
+
+ def add_one(x):
+ '''
+ >>> add_one(1)
+ 2
+ '''
+ return x + 1
+ """)
+
+ reprec = testdir.inline_run(p, "--doctest-modules",
+ "--doctest-ignore-import-errors")
+ reprec.assertoutcome(skipped=1, failed=1, passed=0)
+
+ def test_junit_report_for_doctest(self, testdir):
+ """
+ #713: Fix --junit-xml option when used with --doctest-modules.
+ """
+ p = testdir.makepyfile("""
+ def foo():
+ '''
+ >>> 1 + 1
+ 3
+ '''
+ pass
+ """)
+ reprec = testdir.inline_run(p, "--doctest-modules",
+ "--junit-xml=junit.xml")
+ reprec.assertoutcome(failed=1)
+
+ def test_unicode_doctest(self, testdir):
+ """
+ Test case for issue 2434: DecodeError on Python 2 when doctest contains non-ascii
+ characters.
+ """
+ p = testdir.maketxtfile(test_unicode_doctest="""
+ .. doctest::
+
+ >>> print(
+ ... "Hi\\n\\nByé")
+ Hi
+ ...
+ Byé
+ >>> 1/0 # Byé
+ 1
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ '*UNEXPECTED EXCEPTION: ZeroDivisionError*',
+ '*1 failed*',
+ ])
+
+ def test_unicode_doctest_module(self, testdir):
+ """
+ Test case for issue 2434: DecodeError on Python 2 when doctest docstring
+ contains non-ascii characters.
+ """
+ p = testdir.makepyfile(test_unicode_doctest_module="""
+ # -*- encoding: utf-8 -*-
+ from __future__ import unicode_literals
+
+ def fix_bad_unicode(text):
+ '''
+ >>> print(fix_bad_unicode('único'))
+ único
+ '''
+ return "único"
+ """)
+ result = testdir.runpytest(p, '--doctest-modules')
+ result.stdout.fnmatch_lines(['* 1 passed *'])
+
+ def test_reportinfo(self, testdir):
+ '''
+ Test case to make sure that DoctestItem.reportinfo() returns lineno.
+ '''
+ p = testdir.makepyfile(test_reportinfo="""
+ def foo(x):
+ '''
+ >>> foo('a')
+ 'b'
+ '''
+ return 'c'
+ """)
+ items, reprec = testdir.inline_genitems(p, '--doctest-modules')
+ reportinfo = items[0].reportinfo()
+ assert reportinfo[1] == 1
+
+ def test_valid_setup_py(self, testdir):
+ '''
+ Test to make sure that pytest ignores valid setup.py files when ran
+ with --doctest-modules
+ '''
+ p = testdir.makepyfile(setup="""
+ from setuptools import setup, find_packages
+ setup(name='sample',
+ version='0.0',
+ description='description',
+ packages=find_packages()
+ )
+ """)
+ result = testdir.runpytest(p, '--doctest-modules')
+ result.stdout.fnmatch_lines(['*collected 0 items*'])
+
+ def test_invalid_setup_py(self, testdir):
+ '''
+ Test to make sure that pytest reads setup.py files that are not used
+ for python packages when ran with --doctest-modules
+ '''
+ p = testdir.makepyfile(setup="""
+ def test_foo():
+ return 'bar'
+ """)
+ result = testdir.runpytest(p, '--doctest-modules')
+ result.stdout.fnmatch_lines(['*collected 1 item*'])
+
+
+class TestLiterals(object):
+
+ @pytest.mark.parametrize('config_mode', ['ini', 'comment'])
+ def test_allow_unicode(self, testdir, config_mode):
+ """Test that doctests which output unicode work in all python versions
+ tested by pytest when the ALLOW_UNICODE option is used (either in
+ the ini file or by an inline comment).
+ """
+ if config_mode == 'ini':
+ testdir.makeini('''
+ [pytest]
+ doctest_optionflags = ALLOW_UNICODE
+ ''')
+ comment = ''
+ else:
+ comment = '#doctest: +ALLOW_UNICODE'
+
+ testdir.maketxtfile(test_doc="""
+ >>> b'12'.decode('ascii') {comment}
+ '12'
+ """.format(comment=comment))
+ testdir.makepyfile(foo="""
+ def foo():
+ '''
+ >>> b'12'.decode('ascii') {comment}
+ '12'
+ '''
+ """.format(comment=comment))
+ reprec = testdir.inline_run("--doctest-modules")
+ reprec.assertoutcome(passed=2)
+
+ @pytest.mark.parametrize('config_mode', ['ini', 'comment'])
+ def test_allow_bytes(self, testdir, config_mode):
+ """Test that doctests which output bytes work in all python versions
+ tested by pytest when the ALLOW_BYTES option is used (either in
+ the ini file or by an inline comment)(#1287).
+ """
+ if config_mode == 'ini':
+ testdir.makeini('''
+ [pytest]
+ doctest_optionflags = ALLOW_BYTES
+ ''')
+ comment = ''
+ else:
+ comment = '#doctest: +ALLOW_BYTES'
+
+ testdir.maketxtfile(test_doc="""
+ >>> b'foo' {comment}
+ 'foo'
+ """.format(comment=comment))
+ testdir.makepyfile(foo="""
+ def foo():
+ '''
+ >>> b'foo' {comment}
+ 'foo'
+ '''
+ """.format(comment=comment))
+ reprec = testdir.inline_run("--doctest-modules")
+ reprec.assertoutcome(passed=2)
+
+ def test_unicode_string(self, testdir):
+ """Test that doctests which output unicode fail in Python 2 when
+ the ALLOW_UNICODE option is not used. The same test should pass
+ in Python 3.
+ """
+ testdir.maketxtfile(test_doc="""
+ >>> b'12'.decode('ascii')
+ '12'
+ """)
+ reprec = testdir.inline_run()
+ passed = int(sys.version_info[0] >= 3)
+ reprec.assertoutcome(passed=passed, failed=int(not passed))
+
+ def test_bytes_literal(self, testdir):
+ """Test that doctests which output bytes fail in Python 3 when
+ the ALLOW_BYTES option is not used. The same test should pass
+ in Python 2 (#1287).
+ """
+ testdir.maketxtfile(test_doc="""
+ >>> b'foo'
+ 'foo'
+ """)
+ reprec = testdir.inline_run()
+ passed = int(sys.version_info[0] == 2)
+ reprec.assertoutcome(passed=passed, failed=int(not passed))
+
+
+class TestDoctestSkips(object):
+ """
+ If all examples in a doctest are skipped due to the SKIP option, then
+ the tests should be SKIPPED rather than PASSED. (#957)
+ """
+
+ @pytest.fixture(params=['text', 'module'])
+ def makedoctest(self, testdir, request):
+ def makeit(doctest):
+ mode = request.param
+ if mode == 'text':
+ testdir.maketxtfile(doctest)
+ else:
+ assert mode == 'module'
+ testdir.makepyfile('"""\n%s"""' % doctest)
+
+ return makeit
+
+ def test_one_skipped(self, testdir, makedoctest):
+ makedoctest("""
+ >>> 1 + 1 # doctest: +SKIP
+ 2
+ >>> 2 + 2
+ 4
+ """)
+ reprec = testdir.inline_run("--doctest-modules")
+ reprec.assertoutcome(passed=1)
+
+ def test_one_skipped_failed(self, testdir, makedoctest):
+ makedoctest("""
+ >>> 1 + 1 # doctest: +SKIP
+ 2
+ >>> 2 + 2
+ 200
+ """)
+ reprec = testdir.inline_run("--doctest-modules")
+ reprec.assertoutcome(failed=1)
+
+ def test_all_skipped(self, testdir, makedoctest):
+ makedoctest("""
+ >>> 1 + 1 # doctest: +SKIP
+ 2
+ >>> 2 + 2 # doctest: +SKIP
+ 200
+ """)
+ reprec = testdir.inline_run("--doctest-modules")
+ reprec.assertoutcome(skipped=1)
+
+ def test_vacuous_all_skipped(self, testdir, makedoctest):
+ makedoctest('')
+ reprec = testdir.inline_run("--doctest-modules")
+ reprec.assertoutcome(passed=0, skipped=0)
+
+
+class TestDoctestAutoUseFixtures(object):
+
+ SCOPES = ['module', 'session', 'class', 'function']
+
+ def test_doctest_module_session_fixture(self, testdir):
+ """Test that session fixtures are initialized for doctest modules (#768)
+ """
+ # session fixture which changes some global data, which will
+ # be accessed by doctests in a module
+ testdir.makeconftest("""
+ import pytest
+ import sys
+
+ @pytest.yield_fixture(autouse=True, scope='session')
+ def myfixture():
+ assert not hasattr(sys, 'pytest_session_data')
+ sys.pytest_session_data = 1
+ yield
+ del sys.pytest_session_data
+ """)
+ testdir.makepyfile(foo="""
+ import sys
+
+ def foo():
+ '''
+ >>> assert sys.pytest_session_data == 1
+ '''
+
+ def bar():
+ '''
+ >>> assert sys.pytest_session_data == 1
+ '''
+ """)
+ result = testdir.runpytest("--doctest-modules")
+ result.stdout.fnmatch_lines('*2 passed*')
+
+ @pytest.mark.parametrize('scope', SCOPES)
+ @pytest.mark.parametrize('enable_doctest', [True, False])
+ def test_fixture_scopes(self, testdir, scope, enable_doctest):
+ """Test that auto-use fixtures work properly with doctest modules.
+ See #1057 and #1100.
+ """
+ testdir.makeconftest('''
+ import pytest
+
+ @pytest.fixture(autouse=True, scope="{scope}")
+ def auto(request):
+ return 99
+ '''.format(scope=scope))
+ testdir.makepyfile(test_1='''
+ def test_foo():
+ """
+ >>> getfixture('auto') + 1
+ 100
+ """
+ def test_bar():
+ assert 1
+ ''')
+ params = ('--doctest-modules',) if enable_doctest else ()
+ passes = 3 if enable_doctest else 2
+ result = testdir.runpytest(*params)
+ result.stdout.fnmatch_lines(['*=== %d passed in *' % passes])
+
+ @pytest.mark.parametrize('scope', SCOPES)
+ @pytest.mark.parametrize('autouse', [True, False])
+ @pytest.mark.parametrize('use_fixture_in_doctest', [True, False])
+ def test_fixture_module_doctest_scopes(self, testdir, scope, autouse,
+ use_fixture_in_doctest):
+ """Test that auto-use fixtures work properly with doctest files.
+ See #1057 and #1100.
+ """
+ testdir.makeconftest('''
+ import pytest
+
+ @pytest.fixture(autouse={autouse}, scope="{scope}")
+ def auto(request):
+ return 99
+ '''.format(scope=scope, autouse=autouse))
+ if use_fixture_in_doctest:
+ testdir.maketxtfile(test_doc="""
+ >>> getfixture('auto')
+ 99
+ """)
+ else:
+ testdir.maketxtfile(test_doc="""
+ >>> 1 + 1
+ 2
+ """)
+ result = testdir.runpytest('--doctest-modules')
+ assert 'FAILURES' not in str(result.stdout.str())
+ result.stdout.fnmatch_lines(['*=== 1 passed in *'])
+
+ @pytest.mark.parametrize('scope', SCOPES)
+ def test_auto_use_request_attributes(self, testdir, scope):
+ """Check that all attributes of a request in an autouse fixture
+ behave as expected when requested for a doctest item.
+ """
+ testdir.makeconftest('''
+ import pytest
+
+ @pytest.fixture(autouse=True, scope="{scope}")
+ def auto(request):
+ if "{scope}" == 'module':
+ assert request.module is None
+ if "{scope}" == 'class':
+ assert request.cls is None
+ if "{scope}" == 'function':
+ assert request.function is None
+ return 99
+ '''.format(scope=scope))
+ testdir.maketxtfile(test_doc="""
+ >>> 1 + 1
+ 2
+ """)
+ result = testdir.runpytest('--doctest-modules')
+ assert 'FAILURES' not in str(result.stdout.str())
+ result.stdout.fnmatch_lines(['*=== 1 passed in *'])
+
+
+class TestDoctestNamespaceFixture(object):
+
+ SCOPES = ['module', 'session', 'class', 'function']
+
+ @pytest.mark.parametrize('scope', SCOPES)
+ def test_namespace_doctestfile(self, testdir, scope):
+ """
+ Check that inserting something into the namespace works in a
+ simple text file doctest
+ """
+ testdir.makeconftest("""
+ import pytest
+ import contextlib
+
+ @pytest.fixture(autouse=True, scope="{scope}")
+ def add_contextlib(doctest_namespace):
+ doctest_namespace['cl'] = contextlib
+ """.format(scope=scope))
+ p = testdir.maketxtfile("""
+ >>> print(cl.__name__)
+ contextlib
+ """)
+ reprec = testdir.inline_run(p)
+ reprec.assertoutcome(passed=1)
+
+ @pytest.mark.parametrize('scope', SCOPES)
+ def test_namespace_pyfile(self, testdir, scope):
+ """
+ Check that inserting something into the namespace works in a
+ simple Python file docstring doctest
+ """
+ testdir.makeconftest("""
+ import pytest
+ import contextlib
+
+ @pytest.fixture(autouse=True, scope="{scope}")
+ def add_contextlib(doctest_namespace):
+ doctest_namespace['cl'] = contextlib
+ """.format(scope=scope))
+ p = testdir.makepyfile("""
+ def foo():
+ '''
+ >>> print(cl.__name__)
+ contextlib
+ '''
+ """)
+ reprec = testdir.inline_run(p, "--doctest-modules")
+ reprec.assertoutcome(passed=1)
+
+
+class TestDoctestReportingOption(object):
+ def _run_doctest_report(self, testdir, format):
+ testdir.makepyfile("""
+ def foo():
+ '''
+ >>> foo()
+ a b
+ 0 1 4
+ 1 2 4
+ 2 3 6
+ '''
+ print(' a b\\n'
+ '0 1 4\\n'
+ '1 2 5\\n'
+ '2 3 6')
+ """)
+ return testdir.runpytest("--doctest-modules", "--doctest-report", format)
+
+ @pytest.mark.parametrize('format', ['udiff', 'UDIFF', 'uDiFf'])
+ def test_doctest_report_udiff(self, testdir, format):
+ result = self._run_doctest_report(testdir, format)
+ result.stdout.fnmatch_lines([
+ ' 0 1 4',
+ ' -1 2 4',
+ ' +1 2 5',
+ ' 2 3 6',
+ ])
+
+ def test_doctest_report_cdiff(self, testdir):
+ result = self._run_doctest_report(testdir, 'cdiff')
+ result.stdout.fnmatch_lines([
+ ' a b',
+ ' 0 1 4',
+ ' ! 1 2 4',
+ ' 2 3 6',
+ ' --- 1,4 ----',
+ ' a b',
+ ' 0 1 4',
+ ' ! 1 2 5',
+ ' 2 3 6',
+ ])
+
+ def test_doctest_report_ndiff(self, testdir):
+ result = self._run_doctest_report(testdir, 'ndiff')
+ result.stdout.fnmatch_lines([
+ ' a b',
+ ' 0 1 4',
+ ' - 1 2 4',
+ ' ? ^',
+ ' + 1 2 5',
+ ' ? ^',
+ ' 2 3 6',
+ ])
+
+ @pytest.mark.parametrize('format', ['none', 'only_first_failure'])
+ def test_doctest_report_none_or_only_first_failure(self, testdir, format):
+ result = self._run_doctest_report(testdir, format)
+ result.stdout.fnmatch_lines([
+ 'Expected:',
+ ' a b',
+ ' 0 1 4',
+ ' 1 2 4',
+ ' 2 3 6',
+ 'Got:',
+ ' a b',
+ ' 0 1 4',
+ ' 1 2 5',
+ ' 2 3 6',
+ ])
+
+ def test_doctest_report_invalid(self, testdir):
+ result = self._run_doctest_report(testdir, 'obviously_invalid_format')
+ result.stderr.fnmatch_lines([
+ "*error: argument --doctest-report: invalid choice: 'obviously_invalid_format' (choose from*"
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_entry_points.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_entry_points.py
new file mode 100644
index 00000000000..6ca68b481fa
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_entry_points.py
@@ -0,0 +1,14 @@
+from __future__ import absolute_import, division, print_function
+import pkg_resources
+
+import pytest
+
+
+@pytest.mark.parametrize("entrypoint", ['py.test', 'pytest'])
+def test_entry_point_exist(entrypoint):
+ assert entrypoint in pkg_resources.get_entry_map('pytest')['console_scripts']
+
+
+def test_pytest_entry_points_are_identical():
+ entryMap = pkg_resources.get_entry_map('pytest')['console_scripts']
+ assert entryMap['pytest'].module_name == entryMap['py.test'].module_name
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_helpconfig.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_helpconfig.py
new file mode 100644
index 00000000000..845005a0575
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_helpconfig.py
@@ -0,0 +1,77 @@
+from __future__ import absolute_import, division, print_function
+from _pytest.main import EXIT_NOTESTSCOLLECTED
+import pytest
+
+
+def test_version(testdir, pytestconfig):
+ result = testdir.runpytest("--version")
+ assert result.ret == 0
+ # p = py.path.local(py.__file__).dirpath()
+ result.stderr.fnmatch_lines([
+ '*pytest*%s*imported from*' % (pytest.__version__, )
+ ])
+ if pytestconfig.pluginmanager.list_plugin_distinfo():
+ result.stderr.fnmatch_lines([
+ "*setuptools registered plugins:",
+ "*at*",
+ ])
+
+
+def test_help(testdir):
+ result = testdir.runpytest("--help")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines("""
+ *-v*verbose*
+ *setup.cfg*
+ *minversion*
+ *to see*markers*pytest --markers*
+ *to see*fixtures*pytest --fixtures*
+ """)
+
+
+def test_hookvalidation_unknown(testdir):
+ testdir.makeconftest("""
+ def pytest_hello(xyz):
+ pass
+ """)
+ result = testdir.runpytest()
+ assert result.ret != 0
+ result.stdout.fnmatch_lines([
+ '*unknown hook*pytest_hello*'
+ ])
+
+
+def test_hookvalidation_optional(testdir):
+ testdir.makeconftest("""
+ import pytest
+ @pytest.hookimpl(optionalhook=True)
+ def pytest_hello(xyz):
+ pass
+ """)
+ result = testdir.runpytest()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+
+
+def test_traceconfig(testdir):
+ result = testdir.runpytest("--traceconfig")
+ result.stdout.fnmatch_lines([
+ "*using*pytest*py*",
+ "*active plugins*",
+ ])
+
+
+def test_debug(testdir, monkeypatch):
+ result = testdir.runpytest_subprocess("--debug")
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ p = testdir.tmpdir.join("pytestdebug.log")
+ assert "pytest_sessionstart" in p.read()
+
+
+def test_PYTEST_DEBUG(testdir, monkeypatch):
+ monkeypatch.setenv("PYTEST_DEBUG", "1")
+ result = testdir.runpytest_subprocess()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ result.stderr.fnmatch_lines([
+ "*pytest_plugin_registered*",
+ "*manager*PluginManager*"
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_junitxml.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_junitxml.py
new file mode 100644
index 00000000000..b604c02a3de
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_junitxml.py
@@ -0,0 +1,1062 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+from xml.dom import minidom
+import py
+import sys
+import os
+from _pytest.junitxml import LogXML
+import pytest
+
+
+def runandparse(testdir, *args):
+ resultpath = testdir.tmpdir.join("junit.xml")
+ result = testdir.runpytest("--junitxml=%s" % resultpath, *args)
+ xmldoc = minidom.parse(str(resultpath))
+ return result, DomNode(xmldoc)
+
+
+def assert_attr(node, **kwargs):
+ __tracebackhide__ = True
+
+ def nodeval(node, name):
+ anode = node.getAttributeNode(name)
+ if anode is not None:
+ return anode.value
+
+ expected = dict((name, str(value)) for name, value in kwargs.items())
+ on_node = dict((name, nodeval(node, name)) for name in expected)
+ assert on_node == expected
+
+
+class DomNode(object):
+ def __init__(self, dom):
+ self.__node = dom
+
+ def __repr__(self):
+ return self.__node.toxml()
+
+ def find_first_by_tag(self, tag):
+ return self.find_nth_by_tag(tag, 0)
+
+ def _by_tag(self, tag):
+ return self.__node.getElementsByTagName(tag)
+
+ def find_nth_by_tag(self, tag, n):
+ items = self._by_tag(tag)
+ try:
+ nth = items[n]
+ except IndexError:
+ pass
+ else:
+ return type(self)(nth)
+
+ def find_by_tag(self, tag):
+ t = type(self)
+ return [t(x) for x in self.__node.getElementsByTagName(tag)]
+
+ def __getitem__(self, key):
+ node = self.__node.getAttributeNode(key)
+ if node is not None:
+ return node.value
+
+ def assert_attr(self, **kwargs):
+ __tracebackhide__ = True
+ return assert_attr(self.__node, **kwargs)
+
+ def toxml(self):
+ return self.__node.toxml()
+
+ @property
+ def text(self):
+ return self.__node.childNodes[0].wholeText
+
+ @property
+ def tag(self):
+ return self.__node.tagName
+
+ @property
+ def next_siebling(self):
+ return type(self)(self.__node.nextSibling)
+
+
+class TestPython(object):
+ def test_summing_simple(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def test_pass():
+ pass
+ def test_fail():
+ assert 0
+ def test_skip():
+ pytest.skip("")
+ @pytest.mark.xfail
+ def test_xfail():
+ assert 0
+ @pytest.mark.xfail
+ def test_xpass():
+ assert 1
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(name="pytest", errors=0, failures=1, skips=2, tests=5)
+
+ def test_summing_simple_with_errors(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture
+ def fixture():
+ raise Exception()
+ def test_pass():
+ pass
+ def test_fail():
+ assert 0
+ def test_error(fixture):
+ pass
+ @pytest.mark.xfail
+ def test_xfail():
+ assert False
+ @pytest.mark.xfail(strict=True)
+ def test_xpass():
+ assert True
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(name="pytest", errors=1, failures=2, skips=1, tests=5)
+
+ def test_timing_function(self, testdir):
+ testdir.makepyfile("""
+ import time, pytest
+ def setup_module():
+ time.sleep(0.01)
+ def teardown_module():
+ time.sleep(0.01)
+ def test_sleep():
+ time.sleep(0.01)
+ """)
+ result, dom = runandparse(testdir)
+ node = dom.find_first_by_tag("testsuite")
+ tnode = node.find_first_by_tag("testcase")
+ val = tnode["time"]
+ assert round(float(val), 2) >= 0.03
+
+ def test_setup_error(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def arg(request):
+ raise ValueError()
+ def test_function(arg):
+ pass
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(errors=1, tests=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_setup_error.py",
+ line="5",
+ classname="test_setup_error",
+ name="test_function")
+ fnode = tnode.find_first_by_tag("error")
+ fnode.assert_attr(message="test setup failure")
+ assert "ValueError" in fnode.toxml()
+
+ def test_teardown_error(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def arg():
+ yield
+ raise ValueError()
+ def test_function(arg):
+ pass
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_teardown_error.py",
+ line="6",
+ classname="test_teardown_error",
+ name="test_function")
+ fnode = tnode.find_first_by_tag("error")
+ fnode.assert_attr(message="test teardown failure")
+ assert "ValueError" in fnode.toxml()
+
+ def test_call_failure_teardown_error(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def arg():
+ yield
+ raise Exception("Teardown Exception")
+ def test_function(arg):
+ raise Exception("Call Exception")
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(errors=1, failures=1, tests=1)
+ first, second = dom.find_by_tag("testcase")
+ if not first or not second or first == second:
+ assert 0
+ fnode = first.find_first_by_tag("failure")
+ fnode.assert_attr(message="Exception: Call Exception")
+ snode = second.find_first_by_tag("error")
+ snode.assert_attr(message="test teardown failure")
+
+ def test_skip_contains_name_reason(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def test_skip():
+ pytest.skip("hello23")
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret == 0
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(skips=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_skip_contains_name_reason.py",
+ line="1",
+ classname="test_skip_contains_name_reason",
+ name="test_skip")
+ snode = tnode.find_first_by_tag("skipped")
+ snode.assert_attr(type="pytest.skip", message="hello23", )
+
+ def test_mark_skip_contains_name_reason(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skip(reason="hello24")
+ def test_skip():
+ assert True
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret == 0
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(skips=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_mark_skip_contains_name_reason.py",
+ line="1",
+ classname="test_mark_skip_contains_name_reason",
+ name="test_skip")
+ snode = tnode.find_first_by_tag("skipped")
+ snode.assert_attr(type="pytest.skip", message="hello24", )
+
+ def test_mark_skipif_contains_name_reason(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ GLOBAL_CONDITION = True
+ @pytest.mark.skipif(GLOBAL_CONDITION, reason="hello25")
+ def test_skip():
+ assert True
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret == 0
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(skips=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_mark_skipif_contains_name_reason.py",
+ line="2",
+ classname="test_mark_skipif_contains_name_reason",
+ name="test_skip")
+ snode = tnode.find_first_by_tag("skipped")
+ snode.assert_attr(type="pytest.skip", message="hello25", )
+
+ def test_mark_skip_doesnt_capture_output(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skip(reason="foo")
+ def test_skip():
+ print("bar!")
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret == 0
+ node_xml = dom.find_first_by_tag("testsuite").toxml()
+ assert "bar!" not in node_xml
+
+ def test_classname_instance(self, testdir):
+ testdir.makepyfile("""
+ class TestClass(object):
+ def test_method(self):
+ assert 0
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(failures=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_classname_instance.py",
+ line="1",
+ classname="test_classname_instance.TestClass",
+ name="test_method")
+
+ def test_classname_nested_dir(self, testdir):
+ p = testdir.tmpdir.ensure("sub", "test_hello.py")
+ p.write("def test_func(): 0/0")
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(failures=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file=os.path.join("sub", "test_hello.py"),
+ line="0",
+ classname="sub.test_hello",
+ name="test_func")
+
+ def test_internal_error(self, testdir):
+ testdir.makeconftest("def pytest_runtest_protocol(): 0 / 0")
+ testdir.makepyfile("def test_function(): pass")
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(errors=1, tests=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(classname="pytest", name="internal")
+ fnode = tnode.find_first_by_tag("error")
+ fnode.assert_attr(message="internal error")
+ assert "Division" in fnode.toxml()
+
+ def test_failure_function(self, testdir):
+ testdir.makepyfile("""
+ import sys
+ def test_fail():
+ print ("hello-stdout")
+ sys.stderr.write("hello-stderr\\n")
+ raise ValueError(42)
+ """)
+
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(failures=1, tests=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_failure_function.py",
+ line="1",
+ classname="test_failure_function",
+ name="test_fail")
+ fnode = tnode.find_first_by_tag("failure")
+ fnode.assert_attr(message="ValueError: 42")
+ assert "ValueError" in fnode.toxml()
+ systemout = fnode.next_siebling
+ assert systemout.tag == "system-out"
+ assert "hello-stdout" in systemout.toxml()
+ systemerr = systemout.next_siebling
+ assert systemerr.tag == "system-err"
+ assert "hello-stderr" in systemerr.toxml()
+
+ def test_failure_verbose_message(self, testdir):
+ testdir.makepyfile("""
+ import sys
+ def test_fail():
+ assert 0, "An error"
+ """)
+
+ result, dom = runandparse(testdir)
+ node = dom.find_first_by_tag("testsuite")
+ tnode = node.find_first_by_tag("testcase")
+ fnode = tnode.find_first_by_tag("failure")
+ fnode.assert_attr(message="AssertionError: An error assert 0")
+
+ def test_failure_escape(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize('arg1', "<&'", ids="<&'")
+ def test_func(arg1):
+ print(arg1)
+ assert 0
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(failures=3, tests=3)
+
+ for index, char in enumerate("<&'"):
+
+ tnode = node.find_nth_by_tag("testcase", index)
+ tnode.assert_attr(
+ file="test_failure_escape.py",
+ line="1",
+ classname="test_failure_escape",
+ name="test_func[%s]" % char)
+ sysout = tnode.find_first_by_tag('system-out')
+ text = sysout.text
+ assert text == '%s\n' % char
+
+ def test_junit_prefixing(self, testdir):
+ testdir.makepyfile("""
+ def test_func():
+ assert 0
+ class TestHello(object):
+ def test_hello(self):
+ pass
+ """)
+ result, dom = runandparse(testdir, "--junitprefix=xyz")
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(failures=1, tests=2)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_junit_prefixing.py",
+ line="0",
+ classname="xyz.test_junit_prefixing",
+ name="test_func")
+ tnode = node.find_nth_by_tag("testcase", 1)
+ tnode.assert_attr(
+ file="test_junit_prefixing.py",
+ line="3",
+ classname="xyz.test_junit_prefixing."
+ "TestHello",
+ name="test_hello")
+
+ def test_xfailure_function(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def test_xfail():
+ pytest.xfail("42")
+ """)
+ result, dom = runandparse(testdir)
+ assert not result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(skips=1, tests=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_xfailure_function.py",
+ line="1",
+ classname="test_xfailure_function",
+ name="test_xfail")
+ fnode = tnode.find_first_by_tag("skipped")
+ fnode.assert_attr(message="expected test failure")
+ # assert "ValueError" in fnode.toxml()
+
+ def test_xfailure_xpass(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.xfail
+ def test_xpass():
+ pass
+ """)
+ result, dom = runandparse(testdir)
+ # assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(skips=0, tests=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_xfailure_xpass.py",
+ line="1",
+ classname="test_xfailure_xpass",
+ name="test_xpass")
+
+ def test_xfailure_xpass_strict(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.xfail(strict=True, reason="This needs to fail!")
+ def test_xpass():
+ pass
+ """)
+ result, dom = runandparse(testdir)
+ # assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(skips=0, tests=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_xfailure_xpass_strict.py",
+ line="1",
+ classname="test_xfailure_xpass_strict",
+ name="test_xpass")
+ fnode = tnode.find_first_by_tag("failure")
+ fnode.assert_attr(message="[XPASS(strict)] This needs to fail!")
+
+ def test_collect_error(self, testdir):
+ testdir.makepyfile("syntax error")
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(errors=1, tests=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(
+ file="test_collect_error.py",
+ name="test_collect_error")
+ assert tnode["line"] is None
+ fnode = tnode.find_first_by_tag("error")
+ fnode.assert_attr(message="collection failure")
+ assert "SyntaxError" in fnode.toxml()
+
+ def test_unicode(self, testdir):
+ value = 'hx\xc4\x85\xc4\x87\n'
+ testdir.makepyfile("""
+ # coding: latin1
+ def test_hello():
+ print (%r)
+ assert 0
+ """ % value)
+ result, dom = runandparse(testdir)
+ assert result.ret == 1
+ tnode = dom.find_first_by_tag("testcase")
+ fnode = tnode.find_first_by_tag("failure")
+ if not sys.platform.startswith("java"):
+ assert "hx" in fnode.toxml()
+
+ def test_assertion_binchars(self, testdir):
+ """this test did fail when the escaping wasnt strict"""
+ testdir.makepyfile("""
+
+ M1 = '\x01\x02\x03\x04'
+ M2 = '\x01\x02\x03\x05'
+
+ def test_str_compare():
+ assert M1 == M2
+ """)
+ result, dom = runandparse(testdir)
+ print(dom.toxml())
+
+ def test_pass_captures_stdout(self, testdir):
+ testdir.makepyfile("""
+ def test_pass():
+ print('hello-stdout')
+ """)
+ result, dom = runandparse(testdir)
+ node = dom.find_first_by_tag("testsuite")
+ pnode = node.find_first_by_tag("testcase")
+ systemout = pnode.find_first_by_tag("system-out")
+ assert "hello-stdout" in systemout.toxml()
+
+ def test_pass_captures_stderr(self, testdir):
+ testdir.makepyfile("""
+ import sys
+ def test_pass():
+ sys.stderr.write('hello-stderr')
+ """)
+ result, dom = runandparse(testdir)
+ node = dom.find_first_by_tag("testsuite")
+ pnode = node.find_first_by_tag("testcase")
+ systemout = pnode.find_first_by_tag("system-err")
+ assert "hello-stderr" in systemout.toxml()
+
+ def test_setup_error_captures_stdout(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def arg(request):
+ print('hello-stdout')
+ raise ValueError()
+ def test_function(arg):
+ pass
+ """)
+ result, dom = runandparse(testdir)
+ node = dom.find_first_by_tag("testsuite")
+ pnode = node.find_first_by_tag("testcase")
+ systemout = pnode.find_first_by_tag("system-out")
+ assert "hello-stdout" in systemout.toxml()
+
+ def test_setup_error_captures_stderr(self, testdir):
+ testdir.makepyfile("""
+ import sys
+ import pytest
+
+ @pytest.fixture
+ def arg(request):
+ sys.stderr.write('hello-stderr')
+ raise ValueError()
+ def test_function(arg):
+ pass
+ """)
+ result, dom = runandparse(testdir)
+ node = dom.find_first_by_tag("testsuite")
+ pnode = node.find_first_by_tag("testcase")
+ systemout = pnode.find_first_by_tag("system-err")
+ assert "hello-stderr" in systemout.toxml()
+
+ def test_avoid_double_stdout(self, testdir):
+ testdir.makepyfile("""
+ import sys
+ import pytest
+
+ @pytest.fixture
+ def arg(request):
+ yield
+ sys.stdout.write('hello-stdout teardown')
+ raise ValueError()
+ def test_function(arg):
+ sys.stdout.write('hello-stdout call')
+ """)
+ result, dom = runandparse(testdir)
+ node = dom.find_first_by_tag("testsuite")
+ pnode = node.find_first_by_tag("testcase")
+ systemout = pnode.find_first_by_tag("system-out")
+ assert "hello-stdout call" in systemout.toxml()
+ assert "hello-stdout teardown" in systemout.toxml()
+
+
+def test_mangle_test_address():
+ from _pytest.junitxml import mangle_test_address
+ address = '::'.join(
+ ["a/my.py.thing.py", "Class", "()", "method", "[a-1-::]"])
+ newnames = mangle_test_address(address)
+ assert newnames == ["a.my.py.thing", "Class", "method", "[a-1-::]"]
+
+
+def test_dont_configure_on_slaves(tmpdir):
+ gotten = []
+
+ class FakeConfig(object):
+ def __init__(self):
+ self.pluginmanager = self
+ self.option = self
+
+ def getini(self, name):
+ return "pytest"
+
+ junitprefix = None
+ # XXX: shouldnt need tmpdir ?
+ xmlpath = str(tmpdir.join('junix.xml'))
+ register = gotten.append
+
+ fake_config = FakeConfig()
+ from _pytest import junitxml
+ junitxml.pytest_configure(fake_config)
+ assert len(gotten) == 1
+ FakeConfig.slaveinput = None
+ junitxml.pytest_configure(fake_config)
+ assert len(gotten) == 1
+
+
+class TestNonPython(object):
+ def test_summing_simple(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ def pytest_collect_file(path, parent):
+ if path.ext == ".xyz":
+ return MyItem(path, parent)
+ class MyItem(pytest.Item):
+ def __init__(self, path, parent):
+ super(MyItem, self).__init__(path.basename, parent)
+ self.fspath = path
+ def runtest(self):
+ raise ValueError(42)
+ def repr_failure(self, excinfo):
+ return "custom item runtest failed"
+ """)
+ testdir.tmpdir.join("myfile.xyz").write("hello")
+ result, dom = runandparse(testdir)
+ assert result.ret
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(errors=0, failures=1, skips=0, tests=1)
+ tnode = node.find_first_by_tag("testcase")
+ tnode.assert_attr(name="myfile.xyz")
+ fnode = tnode.find_first_by_tag("failure")
+ fnode.assert_attr(message="custom item runtest failed")
+ assert "custom item runtest failed" in fnode.toxml()
+
+
+def test_nullbyte(testdir):
+ # A null byte can not occur in XML (see section 2.2 of the spec)
+ testdir.makepyfile("""
+ import sys
+ def test_print_nullbyte():
+ sys.stdout.write('Here the null -->' + chr(0) + '<--')
+ sys.stdout.write('In repr form -->' + repr(chr(0)) + '<--')
+ assert False
+ """)
+ xmlf = testdir.tmpdir.join('junit.xml')
+ testdir.runpytest('--junitxml=%s' % xmlf)
+ text = xmlf.read()
+ assert '\x00' not in text
+ assert '#x00' in text
+
+
+def test_nullbyte_replace(testdir):
+ # Check if the null byte gets replaced
+ testdir.makepyfile("""
+ import sys
+ def test_print_nullbyte():
+ sys.stdout.write('Here the null -->' + chr(0) + '<--')
+ sys.stdout.write('In repr form -->' + repr(chr(0)) + '<--')
+ assert False
+ """)
+ xmlf = testdir.tmpdir.join('junit.xml')
+ testdir.runpytest('--junitxml=%s' % xmlf)
+ text = xmlf.read()
+ assert '#x0' in text
+
+
+def test_invalid_xml_escape():
+ # Test some more invalid xml chars, the full range should be
+ # tested really but let's just thest the edges of the ranges
+ # intead.
+ # XXX This only tests low unicode character points for now as
+ # there are some issues with the testing infrastructure for
+ # the higher ones.
+ # XXX Testing 0xD (\r) is tricky as it overwrites the just written
+ # line in the output, so we skip it too.
+ global unichr
+ try:
+ unichr(65)
+ except NameError:
+ unichr = chr
+ invalid = (0x00, 0x1, 0xB, 0xC, 0xE, 0x19, 27, # issue #126
+ 0xD800, 0xDFFF, 0xFFFE, 0x0FFFF) # , 0x110000)
+ valid = (0x9, 0xA, 0x20, )
+ # 0xD, 0xD7FF, 0xE000, 0xFFFD, 0x10000, 0x10FFFF)
+
+ from _pytest.junitxml import bin_xml_escape
+
+ for i in invalid:
+ got = bin_xml_escape(unichr(i)).uniobj
+ if i <= 0xFF:
+ expected = '#x%02X' % i
+ else:
+ expected = '#x%04X' % i
+ assert got == expected
+ for i in valid:
+ assert chr(i) == bin_xml_escape(unichr(i)).uniobj
+
+
+def test_logxml_path_expansion(tmpdir, monkeypatch):
+ home_tilde = py.path.local(os.path.expanduser('~')).join('test.xml')
+
+ xml_tilde = LogXML('~%stest.xml' % tmpdir.sep, None)
+ assert xml_tilde.logfile == home_tilde
+
+ # this is here for when $HOME is not set correct
+ monkeypatch.setenv("HOME", tmpdir)
+ home_var = os.path.normpath(os.path.expandvars('$HOME/test.xml'))
+
+ xml_var = LogXML('$HOME%stest.xml' % tmpdir.sep, None)
+ assert xml_var.logfile == home_var
+
+
+def test_logxml_changingdir(testdir):
+ testdir.makepyfile("""
+ def test_func():
+ import os
+ os.chdir("a")
+ """)
+ testdir.tmpdir.mkdir("a")
+ result = testdir.runpytest("--junitxml=a/x.xml")
+ assert result.ret == 0
+ assert testdir.tmpdir.join("a/x.xml").check()
+
+
+def test_logxml_makedir(testdir):
+ """--junitxml should automatically create directories for the xml file"""
+ testdir.makepyfile("""
+ def test_pass():
+ pass
+ """)
+ result = testdir.runpytest("--junitxml=path/to/results.xml")
+ assert result.ret == 0
+ assert testdir.tmpdir.join("path/to/results.xml").check()
+
+
+def test_logxml_check_isdir(testdir):
+ """Give an error if --junit-xml is a directory (#2089)"""
+ result = testdir.runpytest("--junit-xml=.")
+ result.stderr.fnmatch_lines(["*--junitxml must be a filename*"])
+
+
+def test_escaped_parametrized_names_xml(testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize('char', [u"\\x00"])
+ def test_func(char):
+ assert char
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret == 0
+ node = dom.find_first_by_tag("testcase")
+ node.assert_attr(name="test_func[\\x00]")
+
+
+def test_double_colon_split_function_issue469(testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize('param', ["double::colon"])
+ def test_func(param):
+ pass
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret == 0
+ node = dom.find_first_by_tag("testcase")
+ node.assert_attr(classname="test_double_colon_split_function_issue469")
+ node.assert_attr(name='test_func[double::colon]')
+
+
+def test_double_colon_split_method_issue469(testdir):
+ testdir.makepyfile("""
+ import pytest
+ class TestClass(object):
+ @pytest.mark.parametrize('param', ["double::colon"])
+ def test_func(self, param):
+ pass
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret == 0
+ node = dom.find_first_by_tag("testcase")
+ node.assert_attr(
+ classname="test_double_colon_split_method_issue469.TestClass")
+ node.assert_attr(name='test_func[double::colon]')
+
+
+def test_unicode_issue368(testdir):
+ path = testdir.tmpdir.join("test.xml")
+ log = LogXML(str(path), None)
+ ustr = py.builtin._totext("ВНИ!", "utf-8")
+ from _pytest.runner import BaseReport
+
+ class Report(BaseReport):
+ longrepr = ustr
+ sections = []
+ nodeid = "something"
+ location = 'tests/filename.py', 42, 'TestClass.method'
+
+ test_report = Report()
+
+ # hopefully this is not too brittle ...
+ log.pytest_sessionstart()
+ node_reporter = log._opentestcase(test_report)
+ node_reporter.append_failure(test_report)
+ node_reporter.append_collect_error(test_report)
+ node_reporter.append_collect_skipped(test_report)
+ node_reporter.append_error(test_report)
+ test_report.longrepr = "filename", 1, ustr
+ node_reporter.append_skipped(test_report)
+ test_report.longrepr = "filename", 1, "Skipped: 卡嘣嘣"
+ node_reporter.append_skipped(test_report)
+ test_report.wasxfail = ustr
+ node_reporter.append_skipped(test_report)
+ log.pytest_sessionfinish()
+
+
+def test_record_property(testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def other(record_xml_property):
+ record_xml_property("bar", 1)
+ def test_record(record_xml_property, other):
+ record_xml_property("foo", "<1");
+ """)
+ result, dom = runandparse(testdir, '-rw')
+ node = dom.find_first_by_tag("testsuite")
+ tnode = node.find_first_by_tag("testcase")
+ psnode = tnode.find_first_by_tag('properties')
+ pnodes = psnode.find_by_tag('property')
+ pnodes[0].assert_attr(name="bar", value="1")
+ pnodes[1].assert_attr(name="foo", value="<1")
+ result.stdout.fnmatch_lines([
+ 'test_record_property.py::test_record',
+ '*record_xml_property*experimental*',
+ ])
+
+
+def test_record_property_same_name(testdir):
+ testdir.makepyfile("""
+ def test_record_with_same_name(record_xml_property):
+ record_xml_property("foo", "bar")
+ record_xml_property("foo", "baz")
+ """)
+ result, dom = runandparse(testdir, '-rw')
+ node = dom.find_first_by_tag("testsuite")
+ tnode = node.find_first_by_tag("testcase")
+ psnode = tnode.find_first_by_tag('properties')
+ pnodes = psnode.find_by_tag('property')
+ pnodes[0].assert_attr(name="foo", value="bar")
+ pnodes[1].assert_attr(name="foo", value="baz")
+
+
+def test_random_report_log_xdist(testdir):
+ """xdist calls pytest_runtest_logreport as they are executed by the slaves,
+ with nodes from several nodes overlapping, so junitxml must cope with that
+ to produce correct reports. #1064
+ """
+ pytest.importorskip('xdist')
+ testdir.makepyfile("""
+ import pytest, time
+ @pytest.mark.parametrize('i', list(range(30)))
+ def test_x(i):
+ assert i != 22
+ """)
+ _, dom = runandparse(testdir, '-n2')
+ suite_node = dom.find_first_by_tag("testsuite")
+ failed = []
+ for case_node in suite_node.find_by_tag("testcase"):
+ if case_node.find_first_by_tag('failure'):
+ failed.append(case_node['name'])
+
+ assert failed == ['test_x[22]']
+
+
+def test_runs_twice(testdir):
+ f = testdir.makepyfile('''
+ def test_pass():
+ pass
+ ''')
+
+ result, dom = runandparse(testdir, f, f)
+ assert 'INTERNALERROR' not in result.stdout.str()
+ first, second = [x['classname'] for x in dom.find_by_tag("testcase")]
+ assert first == second
+
+
+@pytest.mark.xfail(reason='hangs', run=False)
+def test_runs_twice_xdist(testdir):
+ pytest.importorskip('xdist')
+ f = testdir.makepyfile('''
+ def test_pass():
+ pass
+ ''')
+
+ result, dom = runandparse(
+ testdir, f,
+ '--dist', 'each', '--tx', '2*popen',)
+ assert 'INTERNALERROR' not in result.stdout.str()
+ first, second = [x['classname'] for x in dom.find_by_tag("testcase")]
+ assert first == second
+
+
+def test_fancy_items_regression(testdir):
+ # issue 1259
+ testdir.makeconftest("""
+ import pytest
+ class FunItem(pytest.Item):
+ def runtest(self):
+ pass
+ class NoFunItem(pytest.Item):
+ def runtest(self):
+ pass
+
+ class FunCollector(pytest.File):
+ def collect(self):
+ return [
+ FunItem('a', self),
+ NoFunItem('a', self),
+ NoFunItem('b', self),
+ ]
+
+ def pytest_collect_file(path, parent):
+ if path.check(ext='.py'):
+ return FunCollector(path, parent)
+ """)
+
+ testdir.makepyfile('''
+ def test_pass():
+ pass
+ ''')
+
+ result, dom = runandparse(testdir)
+
+ assert 'INTERNALERROR' not in result.stdout.str()
+
+ items = sorted(
+ '%(classname)s %(name)s %(file)s' % x
+
+ for x in dom.find_by_tag("testcase"))
+ import pprint
+ pprint.pprint(items)
+ assert items == [
+ u'conftest a conftest.py',
+ u'conftest a conftest.py',
+ u'conftest b conftest.py',
+ u'test_fancy_items_regression a test_fancy_items_regression.py',
+ u'test_fancy_items_regression a test_fancy_items_regression.py',
+ u'test_fancy_items_regression b test_fancy_items_regression.py',
+ u'test_fancy_items_regression test_pass'
+ u' test_fancy_items_regression.py',
+ ]
+
+
+def test_global_properties(testdir):
+ path = testdir.tmpdir.join("test_global_properties.xml")
+ log = LogXML(str(path), None)
+ from _pytest.runner import BaseReport
+
+ class Report(BaseReport):
+ sections = []
+ nodeid = "test_node_id"
+
+ log.pytest_sessionstart()
+ log.add_global_property('foo', 1)
+ log.add_global_property('bar', 2)
+ log.pytest_sessionfinish()
+
+ dom = minidom.parse(str(path))
+
+ properties = dom.getElementsByTagName('properties')
+
+ assert (properties.length == 1), "There must be one <properties> node"
+
+ property_list = dom.getElementsByTagName('property')
+
+ assert (property_list.length == 2), "There most be only 2 property nodes"
+
+ expected = {'foo': '1', 'bar': '2'}
+ actual = {}
+
+ for p in property_list:
+ k = str(p.getAttribute('name'))
+ v = str(p.getAttribute('value'))
+ actual[k] = v
+
+ assert actual == expected
+
+
+def test_url_property(testdir):
+ test_url = "http://www.github.com/pytest-dev"
+ path = testdir.tmpdir.join("test_url_property.xml")
+ log = LogXML(str(path), None)
+ from _pytest.runner import BaseReport
+
+ class Report(BaseReport):
+ longrepr = "FooBarBaz"
+ sections = []
+ nodeid = "something"
+ location = 'tests/filename.py', 42, 'TestClass.method'
+ url = test_url
+
+ test_report = Report()
+
+ log.pytest_sessionstart()
+ node_reporter = log._opentestcase(test_report)
+ node_reporter.append_failure(test_report)
+ log.pytest_sessionfinish()
+
+ test_case = minidom.parse(str(path)).getElementsByTagName('testcase')[0]
+
+ assert (test_case.getAttribute('url') == test_url), "The URL did not get written to the xml"
+
+
+@pytest.mark.parametrize('suite_name', ['my_suite', ''])
+def test_set_suite_name(testdir, suite_name):
+ if suite_name:
+ testdir.makeini("""
+ [pytest]
+ junit_suite_name={0}
+ """.format(suite_name))
+ expected = suite_name
+ else:
+ expected = 'pytest'
+ testdir.makepyfile("""
+ import pytest
+
+ def test_func():
+ pass
+ """)
+ result, dom = runandparse(testdir)
+ assert result.ret == 0
+ node = dom.find_first_by_tag("testsuite")
+ node.assert_attr(name=expected)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_mark.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_mark.py
new file mode 100644
index 00000000000..46bf0b0e778
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_mark.py
@@ -0,0 +1,878 @@
+from __future__ import absolute_import, division, print_function
+import os
+import sys
+
+import pytest
+from _pytest.mark import MarkGenerator as Mark, ParameterSet, transfer_markers
+
+
+class TestMark(object):
+ def test_markinfo_repr(self):
+ from _pytest.mark import MarkInfo, Mark
+ m = MarkInfo(Mark("hello", (1, 2), {}))
+ repr(m)
+
+ @pytest.mark.parametrize('attr', ['mark', 'param'])
+ @pytest.mark.parametrize('modulename', ['py.test', 'pytest'])
+ def test_pytest_exists_in_namespace_all(self, attr, modulename):
+ module = sys.modules[modulename]
+ assert attr in module.__all__
+
+ def test_pytest_mark_notcallable(self):
+ mark = Mark()
+ pytest.raises((AttributeError, TypeError), mark)
+
+ def test_mark_with_param(self):
+ def some_function(abc):
+ pass
+
+ class SomeClass(object):
+ pass
+
+ assert pytest.mark.fun(some_function) is some_function
+ assert pytest.mark.fun.with_args(some_function) is not some_function
+
+ assert pytest.mark.fun(SomeClass) is SomeClass
+ assert pytest.mark.fun.with_args(SomeClass) is not SomeClass
+
+ def test_pytest_mark_name_starts_with_underscore(self):
+ mark = Mark()
+ pytest.raises(AttributeError, getattr, mark, '_some_name')
+
+ def test_pytest_mark_bare(self):
+ mark = Mark()
+
+ def f():
+ pass
+
+ mark.hello(f)
+ assert f.hello
+
+ def test_pytest_mark_keywords(self):
+ mark = Mark()
+
+ def f():
+ pass
+
+ mark.world(x=3, y=4)(f)
+ assert f.world
+ assert f.world.kwargs['x'] == 3
+ assert f.world.kwargs['y'] == 4
+
+ def test_apply_multiple_and_merge(self):
+ mark = Mark()
+
+ def f():
+ pass
+
+ mark.world
+ mark.world(x=3)(f)
+ assert f.world.kwargs['x'] == 3
+ mark.world(y=4)(f)
+ assert f.world.kwargs['x'] == 3
+ assert f.world.kwargs['y'] == 4
+ mark.world(y=1)(f)
+ assert f.world.kwargs['y'] == 1
+ assert len(f.world.args) == 0
+
+ def test_pytest_mark_positional(self):
+ mark = Mark()
+
+ def f():
+ pass
+
+ mark.world("hello")(f)
+ assert f.world.args[0] == "hello"
+ mark.world("world")(f)
+
+ def test_pytest_mark_positional_func_and_keyword(self):
+ mark = Mark()
+
+ def f():
+ raise Exception
+
+ m = mark.world(f, omega="hello")
+
+ def g():
+ pass
+
+ assert m(g) == g
+ assert g.world.args[0] is f
+ assert g.world.kwargs["omega"] == "hello"
+
+ def test_pytest_mark_reuse(self):
+ mark = Mark()
+
+ def f():
+ pass
+
+ w = mark.some
+ w("hello", reason="123")(f)
+ assert f.some.args[0] == "hello"
+ assert f.some.kwargs['reason'] == "123"
+
+ def g():
+ pass
+
+ w("world", reason2="456")(g)
+ assert g.some.args[0] == "world"
+ assert 'reason' not in g.some.kwargs
+ assert g.some.kwargs['reason2'] == "456"
+
+
+def test_marked_class_run_twice(testdir, request):
+ """Test fails file is run twice that contains marked class.
+ See issue#683.
+ """
+ py_file = testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize('abc', [1, 2, 3])
+ class Test1(object):
+ def test_1(self, abc):
+ assert abc in [1, 2, 3]
+ """)
+ file_name = os.path.basename(py_file.strpath)
+ rec = testdir.inline_run(file_name, file_name)
+ rec.assertoutcome(passed=6)
+
+
+def test_ini_markers(testdir):
+ testdir.makeini("""
+ [pytest]
+ markers =
+ a1: this is a webtest marker
+ a2: this is a smoke marker
+ """)
+ testdir.makepyfile("""
+ def test_markers(pytestconfig):
+ markers = pytestconfig.getini("markers")
+ print (markers)
+ assert len(markers) >= 2
+ assert markers[0].startswith("a1:")
+ assert markers[1].startswith("a2:")
+ """)
+ rec = testdir.inline_run()
+ rec.assertoutcome(passed=1)
+
+
+def test_markers_option(testdir):
+ testdir.makeini("""
+ [pytest]
+ markers =
+ a1: this is a webtest marker
+ a1some: another marker
+ nodescription
+ """)
+ result = testdir.runpytest("--markers", )
+ result.stdout.fnmatch_lines([
+ "*a1*this is a webtest*",
+ "*a1some*another marker",
+ "*nodescription*",
+ ])
+
+
+def test_ini_markers_whitespace(testdir):
+ testdir.makeini("""
+ [pytest]
+ markers =
+ a1 : this is a whitespace marker
+ """)
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.a1
+ def test_markers():
+ assert True
+ """)
+ rec = testdir.inline_run("--strict", "-m", "a1")
+ rec.assertoutcome(passed=1)
+
+
+def test_marker_without_description(testdir):
+ testdir.makefile(".cfg", setup="""
+ [tool:pytest]
+ markers=slow
+ """)
+ testdir.makeconftest("""
+ import pytest
+ pytest.mark.xfail('FAIL')
+ """)
+ ftdir = testdir.mkdir("ft1_dummy")
+ testdir.tmpdir.join("conftest.py").move(ftdir.join("conftest.py"))
+ rec = testdir.runpytest_subprocess("--strict")
+ rec.assert_outcomes()
+
+
+def test_markers_option_with_plugin_in_current_dir(testdir):
+ testdir.makeconftest('pytest_plugins = "flip_flop"')
+ testdir.makepyfile(flip_flop="""\
+ def pytest_configure(config):
+ config.addinivalue_line("markers", "flip:flop")
+
+ def pytest_generate_tests(metafunc):
+ try:
+ mark = metafunc.function.flipper
+ except AttributeError:
+ return
+ metafunc.parametrize("x", (10, 20))""")
+ testdir.makepyfile("""\
+ import pytest
+ @pytest.mark.flipper
+ def test_example(x):
+ assert x""")
+
+ result = testdir.runpytest("--markers")
+ result.stdout.fnmatch_lines(["*flip*flop*"])
+
+
+def test_mark_on_pseudo_function(testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.r(lambda x: 0/0)
+ def test_hello():
+ pass
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+
+def test_strict_prohibits_unregistered_markers(testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.unregisteredmark
+ def test_hello():
+ pass
+ """)
+ result = testdir.runpytest("--strict")
+ assert result.ret != 0
+ result.stdout.fnmatch_lines([
+ "*unregisteredmark*not*registered*",
+ ])
+
+
+@pytest.mark.parametrize("spec", [
+ ("xyz", ("test_one",)),
+ ("xyz and xyz2", ()),
+ ("xyz2", ("test_two",)),
+ ("xyz or xyz2", ("test_one", "test_two"),)
+])
+def test_mark_option(spec, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.xyz
+ def test_one():
+ pass
+ @pytest.mark.xyz2
+ def test_two():
+ pass
+ """)
+ opt, passed_result = spec
+ rec = testdir.inline_run("-m", opt)
+ passed, skipped, fail = rec.listoutcomes()
+ passed = [x.nodeid.split("::")[-1] for x in passed]
+ assert len(passed) == len(passed_result)
+ assert list(passed) == list(passed_result)
+
+
+@pytest.mark.parametrize("spec", [
+ ("interface", ("test_interface",)),
+ ("not interface", ("test_nointer",)),
+])
+def test_mark_option_custom(spec, testdir):
+ testdir.makeconftest("""
+ import pytest
+ def pytest_collection_modifyitems(items):
+ for item in items:
+ if "interface" in item.nodeid:
+ item.keywords["interface"] = pytest.mark.interface
+ """)
+ testdir.makepyfile("""
+ def test_interface():
+ pass
+ def test_nointer():
+ pass
+ """)
+ opt, passed_result = spec
+ rec = testdir.inline_run("-m", opt)
+ passed, skipped, fail = rec.listoutcomes()
+ passed = [x.nodeid.split("::")[-1] for x in passed]
+ assert len(passed) == len(passed_result)
+ assert list(passed) == list(passed_result)
+
+
+@pytest.mark.parametrize("spec", [
+ ("interface", ("test_interface",)),
+ ("not interface", ("test_nointer", "test_pass")),
+ ("pass", ("test_pass",)),
+ ("not pass", ("test_interface", "test_nointer")),
+])
+def test_keyword_option_custom(spec, testdir):
+ testdir.makepyfile("""
+ def test_interface():
+ pass
+ def test_nointer():
+ pass
+ def test_pass():
+ pass
+ """)
+ opt, passed_result = spec
+ rec = testdir.inline_run("-k", opt)
+ passed, skipped, fail = rec.listoutcomes()
+ passed = [x.nodeid.split("::")[-1] for x in passed]
+ assert len(passed) == len(passed_result)
+ assert list(passed) == list(passed_result)
+
+
+@pytest.mark.parametrize("spec", [
+ ("None", ("test_func[None]",)),
+ ("1.3", ("test_func[1.3]",)),
+ ("2-3", ("test_func[2-3]",))
+])
+def test_keyword_option_parametrize(spec, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize("arg", [None, 1.3, "2-3"])
+ def test_func(arg):
+ pass
+ """)
+ opt, passed_result = spec
+ rec = testdir.inline_run("-k", opt)
+ passed, skipped, fail = rec.listoutcomes()
+ passed = [x.nodeid.split("::")[-1] for x in passed]
+ assert len(passed) == len(passed_result)
+ assert list(passed) == list(passed_result)
+
+
+def test_parametrized_collected_from_command_line(testdir):
+ """Parametrized test not collected if test named specified
+ in command line issue#649.
+ """
+ py_file = testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize("arg", [None, 1.3, "2-3"])
+ def test_func(arg):
+ pass
+ """)
+ file_name = os.path.basename(py_file.strpath)
+ rec = testdir.inline_run(file_name + "::" + "test_func")
+ rec.assertoutcome(passed=3)
+
+
+def test_parametrized_collect_with_wrong_args(testdir):
+ """Test collect parametrized func with wrong number of args."""
+ py_file = testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.parametrize('foo, bar', [(1, 2, 3)])
+ def test_func(foo, bar):
+ pass
+ """)
+
+ result = testdir.runpytest(py_file)
+ result.stdout.fnmatch_lines([
+ 'E ValueError: In "parametrize" the number of values ((1, 2, 3)) '
+ 'must be equal to the number of names ([\'foo\', \'bar\'])'
+ ])
+
+
+def test_parametrized_with_kwargs(testdir):
+ """Test collect parametrized func with wrong number of args."""
+ py_file = testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture(params=[1,2])
+ def a(request):
+ return request.param
+
+ @pytest.mark.parametrize(argnames='b', argvalues=[1, 2])
+ def test_func(a, b):
+ pass
+ """)
+
+ result = testdir.runpytest(py_file)
+ assert(result.ret == 0)
+
+
+class TestFunctional(object):
+
+ def test_mark_per_function(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ @pytest.mark.hello
+ def test_hello():
+ assert hasattr(test_hello, 'hello')
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_mark_per_module(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ pytestmark = pytest.mark.hello
+ def test_func():
+ pass
+ """)
+ keywords = item.keywords
+ assert 'hello' in keywords
+
+ def test_marklist_per_class(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ class TestClass(object):
+ pytestmark = [pytest.mark.hello, pytest.mark.world]
+ def test_func(self):
+ assert TestClass.test_func.hello
+ assert TestClass.test_func.world
+ """)
+ keywords = item.keywords
+ assert 'hello' in keywords
+
+ def test_marklist_per_module(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ pytestmark = [pytest.mark.hello, pytest.mark.world]
+ class TestClass(object):
+ def test_func(self):
+ assert TestClass.test_func.hello
+ assert TestClass.test_func.world
+ """)
+ keywords = item.keywords
+ assert 'hello' in keywords
+ assert 'world' in keywords
+
+ def test_mark_per_class_decorator(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.hello
+ class TestClass(object):
+ def test_func(self):
+ assert TestClass.test_func.hello
+ """)
+ keywords = item.keywords
+ assert 'hello' in keywords
+
+ def test_mark_per_class_decorator_plus_existing_dec(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.hello
+ class TestClass(object):
+ pytestmark = pytest.mark.world
+ def test_func(self):
+ assert TestClass.test_func.hello
+ assert TestClass.test_func.world
+ """)
+ keywords = item.keywords
+ assert 'hello' in keywords
+ assert 'world' in keywords
+
+ def test_merging_markers(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ pytestmark = pytest.mark.hello("pos1", x=1, y=2)
+ class TestClass(object):
+ # classlevel overrides module level
+ pytestmark = pytest.mark.hello(x=3)
+ @pytest.mark.hello("pos0", z=4)
+ def test_func(self):
+ pass
+ """)
+ items, rec = testdir.inline_genitems(p)
+ item, = items
+ keywords = item.keywords
+ marker = keywords['hello']
+ assert marker.args == ("pos0", "pos1")
+ assert marker.kwargs == {'x': 1, 'y': 2, 'z': 4}
+
+ # test the new __iter__ interface
+ values = list(marker)
+ assert len(values) == 3
+ assert values[0].args == ("pos0",)
+ assert values[1].args == ()
+ assert values[2].args == ("pos1", )
+
+ @pytest.mark.xfail(reason='unfixed')
+ def test_merging_markers_deep(self, testdir):
+ # issue 199 - propagate markers into nested classes
+ p = testdir.makepyfile("""
+ import pytest
+ class TestA(object):
+ pytestmark = pytest.mark.a
+ def test_b(self):
+ assert True
+ class TestC(object):
+ # this one didnt get marked
+ def test_d(self):
+ assert True
+ """)
+ items, rec = testdir.inline_genitems(p)
+ for item in items:
+ print(item, item.keywords)
+ assert 'a' in item.keywords
+
+ def test_mark_decorator_subclass_does_not_propagate_to_base(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.a
+ class Base(object): pass
+
+ @pytest.mark.b
+ class Test1(Base):
+ def test_foo(self): pass
+
+ class Test2(Base):
+ def test_bar(self): pass
+ """)
+ items, rec = testdir.inline_genitems(p)
+ self.assert_markers(items, test_foo=('a', 'b'), test_bar=('a',))
+
+ @pytest.mark.issue568
+ @pytest.mark.xfail(reason="markers smear on methods of base classes")
+ def test_mark_should_not_pass_to_siebling_class(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+
+ class TestBase(object):
+ def test_foo(self):
+ pass
+
+ @pytest.mark.b
+ class TestSub(TestBase):
+ pass
+
+
+ class TestOtherSub(TestBase):
+ pass
+
+ """)
+ items, rec = testdir.inline_genitems(p)
+ base_item, sub_item, sub_item_other = items
+ assert not hasattr(base_item.obj, 'b')
+ assert not hasattr(sub_item_other.obj, 'b')
+
+ def test_mark_decorator_baseclasses_merged(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.a
+ class Base(object): pass
+
+ @pytest.mark.b
+ class Base2(Base): pass
+
+ @pytest.mark.c
+ class Test1(Base2):
+ def test_foo(self): pass
+
+ class Test2(Base2):
+ @pytest.mark.d
+ def test_bar(self): pass
+ """)
+ items, rec = testdir.inline_genitems(p)
+ self.assert_markers(items, test_foo=('a', 'b', 'c'),
+ test_bar=('a', 'b', 'd'))
+
+ def test_mark_with_wrong_marker(self, testdir):
+ reprec = testdir.inline_runsource("""
+ import pytest
+ class pytestmark(object):
+ pass
+ def test_func():
+ pass
+ """)
+ values = reprec.getfailedcollections()
+ assert len(values) == 1
+ assert "TypeError" in str(values[0].longrepr)
+
+ def test_mark_dynamically_in_funcarg(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ @pytest.fixture
+ def arg(request):
+ request.applymarker(pytest.mark.hello)
+ def pytest_terminal_summary(terminalreporter):
+ values = terminalreporter.stats['passed']
+ terminalreporter._tw.line("keyword: %s" % values[0].keywords)
+ """)
+ testdir.makepyfile("""
+ def test_func(arg):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "keyword: *hello*"
+ ])
+
+ def test_merging_markers_two_functions(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ @pytest.mark.hello("pos1", z=4)
+ @pytest.mark.hello("pos0", z=3)
+ def test_func():
+ pass
+ """)
+ items, rec = testdir.inline_genitems(p)
+ item, = items
+ keywords = item.keywords
+ marker = keywords['hello']
+ values = list(marker)
+ assert len(values) == 2
+ assert values[0].args == ("pos0",)
+ assert values[1].args == ("pos1",)
+
+ def test_no_marker_match_on_unmarked_names(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ @pytest.mark.shouldmatch
+ def test_marked():
+ assert 1
+
+ def test_unmarked():
+ assert 1
+ """)
+ reprec = testdir.inline_run("-m", "test_unmarked", p)
+ passed, skipped, failed = reprec.listoutcomes()
+ assert len(passed) + len(skipped) + len(failed) == 0
+ dlist = reprec.getcalls("pytest_deselected")
+ deselected_tests = dlist[0].items
+ assert len(deselected_tests) == 2
+
+ def test_keywords_at_node_level(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope="session", autouse=True)
+ def some(request):
+ request.keywords["hello"] = 42
+ assert "world" not in request.keywords
+
+ @pytest.fixture(scope="function", autouse=True)
+ def funcsetup(request):
+ assert "world" in request.keywords
+ assert "hello" in request.keywords
+
+ @pytest.mark.world
+ def test_function():
+ pass
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+ def test_keyword_added_for_session(self, testdir):
+ testdir.makeconftest("""
+ import pytest
+ def pytest_collection_modifyitems(session):
+ session.add_marker("mark1")
+ session.add_marker(pytest.mark.mark2)
+ session.add_marker(pytest.mark.mark3)
+ pytest.raises(ValueError, lambda:
+ session.add_marker(10))
+ """)
+ testdir.makepyfile("""
+ def test_some(request):
+ assert "mark1" in request.keywords
+ assert "mark2" in request.keywords
+ assert "mark3" in request.keywords
+ assert 10 not in request.keywords
+ marker = request.node.get_marker("mark1")
+ assert marker.name == "mark1"
+ assert marker.args == ()
+ assert marker.kwargs == {}
+ """)
+ reprec = testdir.inline_run("-m", "mark1")
+ reprec.assertoutcome(passed=1)
+
+ def assert_markers(self, items, **expected):
+ """assert that given items have expected marker names applied to them.
+ expected should be a dict of (item name -> seq of expected marker names)
+
+ .. note:: this could be moved to ``testdir`` if proven to be useful
+ to other modules.
+ """
+ from _pytest.mark import MarkInfo
+ items = dict((x.name, x) for x in items)
+ for name, expected_markers in expected.items():
+ markers = items[name].keywords._markers
+ marker_names = set([name for (name, v) in markers.items()
+ if isinstance(v, MarkInfo)])
+ assert marker_names == set(expected_markers)
+
+ @pytest.mark.xfail(reason='callspec2.setmulti misuses keywords')
+ @pytest.mark.issue1540
+ def test_mark_from_parameters(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ pytestmark = pytest.mark.skipif(True, reason='skip all')
+
+ # skipifs inside fixture params
+ params = [pytest.mark.skipif(False, reason='dont skip')('parameter')]
+
+
+ @pytest.fixture(params=params)
+ def parameter(request):
+ return request.param
+
+
+ def test_1(parameter):
+ assert True
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(skipped=1)
+
+
+class TestKeywordSelection(object):
+
+ def test_select_simple(self, testdir):
+ file_test = testdir.makepyfile("""
+ def test_one():
+ assert 0
+ class TestClass(object):
+ def test_method_one(self):
+ assert 42 == 43
+ """)
+
+ def check(keyword, name):
+ reprec = testdir.inline_run("-s", "-k", keyword, file_test)
+ passed, skipped, failed = reprec.listoutcomes()
+ assert len(failed) == 1
+ assert failed[0].nodeid.split("::")[-1] == name
+ assert len(reprec.getcalls('pytest_deselected')) == 1
+
+ for keyword in ['test_one', 'est_on']:
+ check(keyword, 'test_one')
+ check('TestClass and test', 'test_method_one')
+
+ @pytest.mark.parametrize("keyword", [
+ 'xxx', 'xxx and test_2', 'TestClass', 'xxx and not test_1',
+ 'TestClass and test_2', 'xxx and TestClass and test_2'])
+ def test_select_extra_keywords(self, testdir, keyword):
+ p = testdir.makepyfile(test_select="""
+ def test_1():
+ pass
+ class TestClass(object):
+ def test_2(self):
+ pass
+ """)
+ testdir.makepyfile(conftest="""
+ import pytest
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_pycollect_makeitem(name):
+ outcome = yield
+ if name == "TestClass":
+ item = outcome.get_result()
+ item.extra_keyword_matches.add("xxx")
+ """)
+ reprec = testdir.inline_run(p.dirpath(), '-s', '-k', keyword)
+ print("keyword", repr(keyword))
+ passed, skipped, failed = reprec.listoutcomes()
+ assert len(passed) == 1
+ assert passed[0].nodeid.endswith("test_2")
+ dlist = reprec.getcalls("pytest_deselected")
+ assert len(dlist) == 1
+ assert dlist[0].items[0].name == 'test_1'
+
+ def test_select_starton(self, testdir):
+ threepass = testdir.makepyfile(test_threepass="""
+ def test_one(): assert 1
+ def test_two(): assert 1
+ def test_three(): assert 1
+ """)
+ reprec = testdir.inline_run("-k", "test_two:", threepass)
+ passed, skipped, failed = reprec.listoutcomes()
+ assert len(passed) == 2
+ assert not failed
+ dlist = reprec.getcalls("pytest_deselected")
+ assert len(dlist) == 1
+ item = dlist[0].items[0]
+ assert item.name == "test_one"
+
+ def test_keyword_extra(self, testdir):
+ p = testdir.makepyfile("""
+ def test_one():
+ assert 0
+ test_one.mykeyword = True
+ """)
+ reprec = testdir.inline_run("-k", "mykeyword", p)
+ passed, skipped, failed = reprec.countoutcomes()
+ assert failed == 1
+
+ @pytest.mark.xfail
+ def test_keyword_extra_dash(self, testdir):
+ p = testdir.makepyfile("""
+ def test_one():
+ assert 0
+ test_one.mykeyword = True
+ """)
+ # with argparse the argument to an option cannot
+ # start with '-'
+ reprec = testdir.inline_run("-k", "-mykeyword", p)
+ passed, skipped, failed = reprec.countoutcomes()
+ assert passed + skipped + failed == 0
+
+ def test_no_magic_values(self, testdir):
+ """Make sure the tests do not match on magic values,
+ no double underscored values, like '__dict__',
+ and no instance values, like '()'.
+ """
+ p = testdir.makepyfile("""
+ def test_one(): assert 1
+ """)
+
+ def assert_test_is_not_selected(keyword):
+ reprec = testdir.inline_run("-k", keyword, p)
+ passed, skipped, failed = reprec.countoutcomes()
+ dlist = reprec.getcalls("pytest_deselected")
+ assert passed + skipped + failed == 0
+ deselected_tests = dlist[0].items
+ assert len(deselected_tests) == 1
+
+ assert_test_is_not_selected("__")
+ assert_test_is_not_selected("()")
+
+
+@pytest.mark.parametrize('argval, expected', [
+ (pytest.mark.skip()((1, 2)),
+ ParameterSet(values=(1, 2), marks=[pytest.mark.skip], id=None)),
+ (pytest.mark.xfail(pytest.mark.skip()((1, 2))),
+ ParameterSet(values=(1, 2),
+ marks=[pytest.mark.xfail, pytest.mark.skip], id=None)),
+
+])
+@pytest.mark.filterwarnings('ignore')
+def test_parameterset_extractfrom(argval, expected):
+ extracted = ParameterSet.extract_from(argval)
+ assert extracted == expected
+
+
+def test_legacy_transfer():
+
+ class FakeModule(object):
+ pytestmark = []
+
+ class FakeClass(object):
+ pytestmark = pytest.mark.nofun
+
+ @pytest.mark.fun
+ def fake_method(self):
+ pass
+
+ transfer_markers(fake_method, FakeClass, FakeModule)
+
+ # legacy marks transfer smeared
+ assert fake_method.nofun
+ assert fake_method.fun
+ # pristine marks dont transfer
+ assert fake_method.pytestmark == [pytest.mark.fun.mark]
+
+
+class TestMarkDecorator(object):
+
+ @pytest.mark.parametrize('lhs, rhs, expected', [
+ (pytest.mark.foo(), pytest.mark.foo(), True),
+ (pytest.mark.foo(), pytest.mark.bar(), False),
+ (pytest.mark.foo(), 'bar', False),
+ ('foo', pytest.mark.bar(), False)
+ ])
+ def test__eq__(self, lhs, rhs, expected):
+ assert (lhs == rhs) == expected
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_modimport.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_modimport.py
new file mode 100644
index 00000000000..2ab86bf7af1
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_modimport.py
@@ -0,0 +1,25 @@
+import py
+import subprocess
+import sys
+import pytest
+import _pytest
+
+MODSET = [
+ x for x in py.path.local(_pytest.__file__).dirpath().visit('*.py')
+ if x.purebasename != '__init__'
+]
+
+
+@pytest.mark.parametrize('modfile', MODSET, ids=lambda x: x.purebasename)
+def test_fileimport(modfile):
+ # this test ensures all internal packages can import
+ # without needing the pytest namespace being set
+ # this is critical for the initialization of xdist
+
+ res = subprocess.call([
+ sys.executable,
+ '-c', 'import sys, py; py.path.local(sys.argv[1]).pyimport()',
+ modfile.strpath,
+ ])
+ if res:
+ pytest.fail("command result %s" % res)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_monkeypatch.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_monkeypatch.py
new file mode 100644
index 00000000000..4427908ab3b
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_monkeypatch.py
@@ -0,0 +1,329 @@
+from __future__ import absolute_import, division, print_function
+import os
+import sys
+import textwrap
+
+import pytest
+from _pytest.monkeypatch import MonkeyPatch
+
+
+@pytest.fixture
+def mp():
+ cwd = os.getcwd()
+ sys_path = list(sys.path)
+ yield MonkeyPatch()
+ sys.path[:] = sys_path
+ os.chdir(cwd)
+
+
+def test_setattr():
+ class A(object):
+ x = 1
+
+ monkeypatch = MonkeyPatch()
+ pytest.raises(AttributeError, "monkeypatch.setattr(A, 'notexists', 2)")
+ monkeypatch.setattr(A, 'y', 2, raising=False)
+ assert A.y == 2
+ monkeypatch.undo()
+ assert not hasattr(A, 'y')
+
+ monkeypatch = MonkeyPatch()
+ monkeypatch.setattr(A, 'x', 2)
+ assert A.x == 2
+ monkeypatch.setattr(A, 'x', 3)
+ assert A.x == 3
+ monkeypatch.undo()
+ assert A.x == 1
+
+ A.x = 5
+ monkeypatch.undo() # double-undo makes no modification
+ assert A.x == 5
+
+
+class TestSetattrWithImportPath(object):
+ def test_string_expression(self, monkeypatch):
+ monkeypatch.setattr("os.path.abspath", lambda x: "hello2")
+ assert os.path.abspath("123") == "hello2"
+
+ def test_string_expression_class(self, monkeypatch):
+ monkeypatch.setattr("_pytest.config.Config", 42)
+ import _pytest
+ assert _pytest.config.Config == 42
+
+ def test_unicode_string(self, monkeypatch):
+ monkeypatch.setattr("_pytest.config.Config", 42)
+ import _pytest
+ assert _pytest.config.Config == 42
+ monkeypatch.delattr("_pytest.config.Config")
+
+ def test_wrong_target(self, monkeypatch):
+ pytest.raises(TypeError, lambda: monkeypatch.setattr(None, None))
+
+ def test_unknown_import(self, monkeypatch):
+ pytest.raises(ImportError,
+ lambda: monkeypatch.setattr("unkn123.classx", None))
+
+ def test_unknown_attr(self, monkeypatch):
+ pytest.raises(AttributeError,
+ lambda: monkeypatch.setattr("os.path.qweqwe", None))
+
+ def test_unknown_attr_non_raising(self, monkeypatch):
+ # https://github.com/pytest-dev/pytest/issues/746
+ monkeypatch.setattr('os.path.qweqwe', 42, raising=False)
+ assert os.path.qweqwe == 42
+
+ def test_delattr(self, monkeypatch):
+ monkeypatch.delattr("os.path.abspath")
+ assert not hasattr(os.path, "abspath")
+ monkeypatch.undo()
+ assert os.path.abspath
+
+
+def test_delattr():
+ class A(object):
+ x = 1
+
+ monkeypatch = MonkeyPatch()
+ monkeypatch.delattr(A, 'x')
+ assert not hasattr(A, 'x')
+ monkeypatch.undo()
+ assert A.x == 1
+
+ monkeypatch = MonkeyPatch()
+ monkeypatch.delattr(A, 'x')
+ pytest.raises(AttributeError, "monkeypatch.delattr(A, 'y')")
+ monkeypatch.delattr(A, 'y', raising=False)
+ monkeypatch.setattr(A, 'x', 5, raising=False)
+ assert A.x == 5
+ monkeypatch.undo()
+ assert A.x == 1
+
+
+def test_setitem():
+ d = {'x': 1}
+ monkeypatch = MonkeyPatch()
+ monkeypatch.setitem(d, 'x', 2)
+ monkeypatch.setitem(d, 'y', 1700)
+ monkeypatch.setitem(d, 'y', 1700)
+ assert d['x'] == 2
+ assert d['y'] == 1700
+ monkeypatch.setitem(d, 'x', 3)
+ assert d['x'] == 3
+ monkeypatch.undo()
+ assert d['x'] == 1
+ assert 'y' not in d
+ d['x'] = 5
+ monkeypatch.undo()
+ assert d['x'] == 5
+
+
+def test_setitem_deleted_meanwhile():
+ d = {}
+ monkeypatch = MonkeyPatch()
+ monkeypatch.setitem(d, 'x', 2)
+ del d['x']
+ monkeypatch.undo()
+ assert not d
+
+
+@pytest.mark.parametrize("before", [True, False])
+def test_setenv_deleted_meanwhile(before):
+ key = "qwpeoip123"
+ if before:
+ os.environ[key] = "world"
+ monkeypatch = MonkeyPatch()
+ monkeypatch.setenv(key, 'hello')
+ del os.environ[key]
+ monkeypatch.undo()
+ if before:
+ assert os.environ[key] == "world"
+ del os.environ[key]
+ else:
+ assert key not in os.environ
+
+
+def test_delitem():
+ d = {'x': 1}
+ monkeypatch = MonkeyPatch()
+ monkeypatch.delitem(d, 'x')
+ assert 'x' not in d
+ monkeypatch.delitem(d, 'y', raising=False)
+ pytest.raises(KeyError, "monkeypatch.delitem(d, 'y')")
+ assert not d
+ monkeypatch.setitem(d, 'y', 1700)
+ assert d['y'] == 1700
+ d['hello'] = 'world'
+ monkeypatch.setitem(d, 'x', 1500)
+ assert d['x'] == 1500
+ monkeypatch.undo()
+ assert d == {'hello': 'world', 'x': 1}
+
+
+def test_setenv():
+ monkeypatch = MonkeyPatch()
+ monkeypatch.setenv('XYZ123', 2)
+ import os
+ assert os.environ['XYZ123'] == "2"
+ monkeypatch.undo()
+ assert 'XYZ123' not in os.environ
+
+
+def test_delenv():
+ name = 'xyz1234'
+ assert name not in os.environ
+ monkeypatch = MonkeyPatch()
+ pytest.raises(KeyError, "monkeypatch.delenv(%r, raising=True)" % name)
+ monkeypatch.delenv(name, raising=False)
+ monkeypatch.undo()
+ os.environ[name] = "1"
+ try:
+ monkeypatch = MonkeyPatch()
+ monkeypatch.delenv(name)
+ assert name not in os.environ
+ monkeypatch.setenv(name, "3")
+ assert os.environ[name] == "3"
+ monkeypatch.undo()
+ assert os.environ[name] == "1"
+ finally:
+ if name in os.environ:
+ del os.environ[name]
+
+
+def test_setenv_prepend():
+ import os
+ monkeypatch = MonkeyPatch()
+ monkeypatch.setenv('XYZ123', 2, prepend="-")
+ assert os.environ['XYZ123'] == "2"
+ monkeypatch.setenv('XYZ123', 3, prepend="-")
+ assert os.environ['XYZ123'] == "3-2"
+ monkeypatch.undo()
+ assert 'XYZ123' not in os.environ
+
+
+def test_monkeypatch_plugin(testdir):
+ reprec = testdir.inline_runsource("""
+ def test_method(monkeypatch):
+ assert monkeypatch.__class__.__name__ == "MonkeyPatch"
+ """)
+ res = reprec.countoutcomes()
+ assert tuple(res) == (1, 0, 0), res
+
+
+def test_syspath_prepend(mp):
+ old = list(sys.path)
+ mp.syspath_prepend('world')
+ mp.syspath_prepend('hello')
+ assert sys.path[0] == "hello"
+ assert sys.path[1] == "world"
+ mp.undo()
+ assert sys.path == old
+ mp.undo()
+ assert sys.path == old
+
+
+def test_syspath_prepend_double_undo(mp):
+ mp.syspath_prepend('hello world')
+ mp.undo()
+ sys.path.append('more hello world')
+ mp.undo()
+ assert sys.path[-1] == 'more hello world'
+
+
+def test_chdir_with_path_local(mp, tmpdir):
+ mp.chdir(tmpdir)
+ assert os.getcwd() == tmpdir.strpath
+
+
+def test_chdir_with_str(mp, tmpdir):
+ mp.chdir(tmpdir.strpath)
+ assert os.getcwd() == tmpdir.strpath
+
+
+def test_chdir_undo(mp, tmpdir):
+ cwd = os.getcwd()
+ mp.chdir(tmpdir)
+ mp.undo()
+ assert os.getcwd() == cwd
+
+
+def test_chdir_double_undo(mp, tmpdir):
+ mp.chdir(tmpdir.strpath)
+ mp.undo()
+ tmpdir.chdir()
+ mp.undo()
+ assert os.getcwd() == tmpdir.strpath
+
+
+def test_issue185_time_breaks(testdir):
+ testdir.makepyfile("""
+ import time
+ def test_m(monkeypatch):
+ def f():
+ raise Exception
+ monkeypatch.setattr(time, "time", f)
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ *1 passed*
+ """)
+
+
+def test_importerror(testdir):
+ p = testdir.mkpydir("package")
+ p.join("a.py").write(textwrap.dedent("""\
+ import doesnotexist
+
+ x = 1
+ """))
+ testdir.tmpdir.join("test_importerror.py").write(textwrap.dedent("""\
+ def test_importerror(monkeypatch):
+ monkeypatch.setattr('package.a.x', 2)
+ """))
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ *import error in package.a: No module named {0}doesnotexist{0}*
+ """.format("'" if sys.version_info > (3, 0) else ""))
+
+
+class SampleNew(object):
+ @staticmethod
+ def hello():
+ return True
+
+
+class SampleNewInherit(SampleNew):
+ pass
+
+
+class SampleOld(object):
+ # oldstyle on python2
+ @staticmethod
+ def hello():
+ return True
+
+
+class SampleOldInherit(SampleOld):
+ pass
+
+
+@pytest.mark.parametrize('Sample', [
+ SampleNew, SampleNewInherit,
+ SampleOld, SampleOldInherit,
+], ids=['new', 'new-inherit', 'old', 'old-inherit'])
+def test_issue156_undo_staticmethod(Sample):
+ monkeypatch = MonkeyPatch()
+
+ monkeypatch.setattr(Sample, 'hello', None)
+ assert Sample.hello is None
+
+ monkeypatch.undo()
+ assert Sample.hello()
+
+
+def test_issue1338_name_resolving():
+ pytest.importorskip('requests')
+ monkeypatch = MonkeyPatch()
+ try:
+ monkeypatch.delattr('requests.sessions.Session.request')
+ finally:
+ monkeypatch.undo()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_nodes.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_nodes.py
new file mode 100644
index 00000000000..6f4540f99b9
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_nodes.py
@@ -0,0 +1,18 @@
+import pytest
+
+from _pytest import nodes
+
+
+@pytest.mark.parametrize("baseid, nodeid, expected", (
+ ('', '', True),
+ ('', 'foo', True),
+ ('', 'foo/bar', True),
+ ('', 'foo/bar::TestBaz::()', True),
+ ('foo', 'food', False),
+ ('foo/bar::TestBaz::()', 'foo/bar', False),
+ ('foo/bar::TestBaz::()', 'foo/bar::TestBop::()', False),
+ ('foo/bar', 'foo/bar::TestBop::()', True),
+))
+def test_ischildnode(baseid, nodeid, expected):
+ result = nodes.ischildnode(baseid, nodeid)
+ assert result is expected
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_nose.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_nose.py
new file mode 100644
index 00000000000..df3e1a94b05
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_nose.py
@@ -0,0 +1,409 @@
+from __future__ import absolute_import, division, print_function
+import pytest
+
+
+def setup_module(mod):
+ mod.nose = pytest.importorskip("nose")
+
+
+def test_nose_setup(testdir):
+ p = testdir.makepyfile("""
+ values = []
+ from nose.tools import with_setup
+
+ @with_setup(lambda: values.append(1), lambda: values.append(2))
+ def test_hello():
+ assert values == [1]
+
+ def test_world():
+ assert values == [1,2]
+
+ test_hello.setup = lambda: values.append(1)
+ test_hello.teardown = lambda: values.append(2)
+ """)
+ result = testdir.runpytest(p, '-p', 'nose')
+ result.assert_outcomes(passed=2)
+
+
+def test_setup_func_with_setup_decorator():
+ from _pytest.nose import call_optional
+ values = []
+
+ class A(object):
+ @pytest.fixture(autouse=True)
+ def f(self):
+ values.append(1)
+
+ call_optional(A(), "f")
+ assert not values
+
+
+def test_setup_func_not_callable():
+ from _pytest.nose import call_optional
+
+ class A(object):
+ f = 1
+
+ call_optional(A(), "f")
+
+
+def test_nose_setup_func(testdir):
+ p = testdir.makepyfile("""
+ from nose.tools import with_setup
+
+ values = []
+
+ def my_setup():
+ a = 1
+ values.append(a)
+
+ def my_teardown():
+ b = 2
+ values.append(b)
+
+ @with_setup(my_setup, my_teardown)
+ def test_hello():
+ print (values)
+ assert values == [1]
+
+ def test_world():
+ print (values)
+ assert values == [1,2]
+
+ """)
+ result = testdir.runpytest(p, '-p', 'nose')
+ result.assert_outcomes(passed=2)
+
+
+def test_nose_setup_func_failure(testdir):
+ p = testdir.makepyfile("""
+ from nose.tools import with_setup
+
+ values = []
+ my_setup = lambda x: 1
+ my_teardown = lambda x: 2
+
+ @with_setup(my_setup, my_teardown)
+ def test_hello():
+ print (values)
+ assert values == [1]
+
+ def test_world():
+ print (values)
+ assert values == [1,2]
+
+ """)
+ result = testdir.runpytest(p, '-p', 'nose')
+ result.stdout.fnmatch_lines([
+ "*TypeError: <lambda>()*"
+ ])
+
+
+def test_nose_setup_func_failure_2(testdir):
+ testdir.makepyfile("""
+ values = []
+
+ my_setup = 1
+ my_teardown = 2
+
+ def test_hello():
+ assert values == []
+
+ test_hello.setup = my_setup
+ test_hello.teardown = my_teardown
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+
+def test_nose_setup_partial(testdir):
+ pytest.importorskip("functools")
+ p = testdir.makepyfile("""
+ from functools import partial
+
+ values = []
+
+ def my_setup(x):
+ a = x
+ values.append(a)
+
+ def my_teardown(x):
+ b = x
+ values.append(b)
+
+ my_setup_partial = partial(my_setup, 1)
+ my_teardown_partial = partial(my_teardown, 2)
+
+ def test_hello():
+ print (values)
+ assert values == [1]
+
+ def test_world():
+ print (values)
+ assert values == [1,2]
+
+ test_hello.setup = my_setup_partial
+ test_hello.teardown = my_teardown_partial
+ """)
+ result = testdir.runpytest(p, '-p', 'nose')
+ result.stdout.fnmatch_lines([
+ "*2 passed*"
+ ])
+
+
+def test_nose_test_generator_fixtures(testdir):
+ p = testdir.makepyfile("""
+ # taken from nose-0.11.1 unit_tests/test_generator_fixtures.py
+ from nose.tools import eq_
+ called = []
+
+ def outer_setup():
+ called.append('outer_setup')
+
+ def outer_teardown():
+ called.append('outer_teardown')
+
+ def inner_setup():
+ called.append('inner_setup')
+
+ def inner_teardown():
+ called.append('inner_teardown')
+
+ def test_gen():
+ called[:] = []
+ for i in range(0, 5):
+ yield check, i
+
+ def check(i):
+ expect = ['outer_setup']
+ for x in range(0, i):
+ expect.append('inner_setup')
+ expect.append('inner_teardown')
+ expect.append('inner_setup')
+ eq_(called, expect)
+
+
+ test_gen.setup = outer_setup
+ test_gen.teardown = outer_teardown
+ check.setup = inner_setup
+ check.teardown = inner_teardown
+
+ class TestClass(object):
+ def setup(self):
+ print ("setup called in %s" % self)
+ self.called = ['setup']
+
+ def teardown(self):
+ print ("teardown called in %s" % self)
+ eq_(self.called, ['setup'])
+ self.called.append('teardown')
+
+ def test(self):
+ print ("test called in %s" % self)
+ for i in range(0, 5):
+ yield self.check, i
+
+ def check(self, i):
+ print ("check called in %s" % self)
+ expect = ['setup']
+ #for x in range(0, i):
+ # expect.append('setup')
+ # expect.append('teardown')
+ #expect.append('setup')
+ eq_(self.called, expect)
+ """)
+ result = testdir.runpytest(p, '-p', 'nose')
+ result.stdout.fnmatch_lines([
+ "*10 passed*"
+ ])
+
+
+def test_module_level_setup(testdir):
+ testdir.makepyfile("""
+ from nose.tools import with_setup
+ items = {}
+
+ def setup():
+ items[1]=1
+
+ def teardown():
+ del items[1]
+
+ def setup2():
+ items[2] = 2
+
+ def teardown2():
+ del items[2]
+
+ def test_setup_module_setup():
+ assert items[1] == 1
+
+ @with_setup(setup2, teardown2)
+ def test_local_setup():
+ assert items[2] == 2
+ assert 1 not in items
+ """)
+ result = testdir.runpytest('-p', 'nose')
+ result.stdout.fnmatch_lines([
+ "*2 passed*",
+ ])
+
+
+def test_nose_style_setup_teardown(testdir):
+ testdir.makepyfile("""
+ values = []
+
+ def setup_module():
+ values.append(1)
+
+ def teardown_module():
+ del values[0]
+
+ def test_hello():
+ assert values == [1]
+
+ def test_world():
+ assert values == [1]
+ """)
+ result = testdir.runpytest('-p', 'nose')
+ result.stdout.fnmatch_lines([
+ "*2 passed*",
+ ])
+
+
+def test_nose_setup_ordering(testdir):
+ testdir.makepyfile("""
+ def setup_module(mod):
+ mod.visited = True
+
+ class TestClass(object):
+ def setup(self):
+ assert visited
+ def test_first(self):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*1 passed*",
+ ])
+
+
+def test_apiwrapper_problem_issue260(testdir):
+ # this would end up trying a call a optional teardown on the class
+ # for plain unittests we dont want nose behaviour
+ testdir.makepyfile("""
+ import unittest
+ class TestCase(unittest.TestCase):
+ def setup(self):
+ #should not be called in unittest testcases
+ assert 0, 'setup'
+ def teardown(self):
+ #should not be called in unittest testcases
+ assert 0, 'teardown'
+ def setUp(self):
+ print('setup')
+ def tearDown(self):
+ print('teardown')
+ def test_fun(self):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+def test_setup_teardown_linking_issue265(testdir):
+ # we accidentally didnt integrate nose setupstate with normal setupstate
+ # this test ensures that won't happen again
+ testdir.makepyfile('''
+ import pytest
+
+ class TestGeneric(object):
+ def test_nothing(self):
+ """Tests the API of the implementation (for generic and specialized)."""
+
+ @pytest.mark.skipif("True", reason=
+ "Skip tests to check if teardown is skipped as well.")
+ class TestSkipTeardown(TestGeneric):
+
+ def setup(self):
+ """Sets up my specialized implementation for $COOL_PLATFORM."""
+ raise Exception("should not call setup for skipped tests")
+
+ def teardown(self):
+ """Undoes the setup."""
+ raise Exception("should not call teardown for skipped tests")
+ ''')
+ reprec = testdir.runpytest()
+ reprec.assert_outcomes(passed=1, skipped=1)
+
+
+def test_SkipTest_during_collection(testdir):
+ p = testdir.makepyfile("""
+ import nose
+ raise nose.SkipTest("during collection")
+ def test_failing():
+ assert False
+ """)
+ result = testdir.runpytest(p)
+ result.assert_outcomes(skipped=1)
+
+
+def test_SkipTest_in_test(testdir):
+ testdir.makepyfile("""
+ import nose
+
+ def test_skipping():
+ raise nose.SkipTest("in test")
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(skipped=1)
+
+
+def test_istest_function_decorator(testdir):
+ p = testdir.makepyfile("""
+ import nose.tools
+ @nose.tools.istest
+ def not_test_prefix():
+ pass
+ """)
+ result = testdir.runpytest(p)
+ result.assert_outcomes(passed=1)
+
+
+def test_nottest_function_decorator(testdir):
+ testdir.makepyfile("""
+ import nose.tools
+ @nose.tools.nottest
+ def test_prefix():
+ pass
+ """)
+ reprec = testdir.inline_run()
+ assert not reprec.getfailedcollections()
+ calls = reprec.getreports("pytest_runtest_logreport")
+ assert not calls
+
+
+def test_istest_class_decorator(testdir):
+ p = testdir.makepyfile("""
+ import nose.tools
+ @nose.tools.istest
+ class NotTestPrefix(object):
+ def test_method(self):
+ pass
+ """)
+ result = testdir.runpytest(p)
+ result.assert_outcomes(passed=1)
+
+
+def test_nottest_class_decorator(testdir):
+ testdir.makepyfile("""
+ import nose.tools
+ @nose.tools.nottest
+ class TestPrefix(object):
+ def test_method(self):
+ pass
+ """)
+ reprec = testdir.inline_run()
+ assert not reprec.getfailedcollections()
+ calls = reprec.getreports("pytest_runtest_logreport")
+ assert not calls
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_parseopt.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_parseopt.py
new file mode 100644
index 00000000000..92159257019
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_parseopt.py
@@ -0,0 +1,309 @@
+from __future__ import absolute_import, division, print_function
+import sys
+import os
+import py
+import pytest
+from _pytest import config as parseopt
+
+
+@pytest.fixture
+def parser():
+ return parseopt.Parser()
+
+
+class TestParser(object):
+ def test_no_help_by_default(self, capsys):
+ parser = parseopt.Parser(usage="xyz")
+ pytest.raises(SystemExit, lambda: parser.parse(["-h"]))
+ out, err = capsys.readouterr()
+ assert err.find("error: unrecognized arguments") != -1
+
+ def test_argument(self):
+ with pytest.raises(parseopt.ArgumentError):
+ # need a short or long option
+ argument = parseopt.Argument()
+ argument = parseopt.Argument('-t')
+ assert argument._short_opts == ['-t']
+ assert argument._long_opts == []
+ assert argument.dest == 't'
+ argument = parseopt.Argument('-t', '--test')
+ assert argument._short_opts == ['-t']
+ assert argument._long_opts == ['--test']
+ assert argument.dest == 'test'
+ argument = parseopt.Argument('-t', '--test', dest='abc')
+ assert argument.dest == 'abc'
+ assert str(argument) == (
+ "Argument(_short_opts: ['-t'], _long_opts: ['--test'], dest: 'abc')"
+ )
+
+ def test_argument_type(self):
+ argument = parseopt.Argument('-t', dest='abc', type=int)
+ assert argument.type is int
+ argument = parseopt.Argument('-t', dest='abc', type=str)
+ assert argument.type is str
+ argument = parseopt.Argument('-t', dest='abc', type=float)
+ assert argument.type is float
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(KeyError):
+ argument = parseopt.Argument('-t', dest='abc', type='choice')
+ argument = parseopt.Argument('-t', dest='abc', type=str,
+ choices=['red', 'blue'])
+ assert argument.type is str
+
+ def test_argument_processopt(self):
+ argument = parseopt.Argument('-t', type=int)
+ argument.default = 42
+ argument.dest = 'abc'
+ res = argument.attrs()
+ assert res['default'] == 42
+ assert res['dest'] == 'abc'
+
+ def test_group_add_and_get(self, parser):
+ group = parser.getgroup("hello", description="desc")
+ assert group.name == "hello"
+ assert group.description == "desc"
+
+ def test_getgroup_simple(self, parser):
+ group = parser.getgroup("hello", description="desc")
+ assert group.name == "hello"
+ assert group.description == "desc"
+ group2 = parser.getgroup("hello")
+ assert group2 is group
+
+ def test_group_ordering(self, parser):
+ parser.getgroup("1")
+ parser.getgroup("2")
+ parser.getgroup("3", after="1")
+ groups = parser._groups
+ groups_names = [x.name for x in groups]
+ assert groups_names == list("132")
+
+ def test_group_addoption(self):
+ group = parseopt.OptionGroup("hello")
+ group.addoption("--option1", action="store_true")
+ assert len(group.options) == 1
+ assert isinstance(group.options[0], parseopt.Argument)
+
+ def test_group_addoption_conflict(self):
+ group = parseopt.OptionGroup("hello again")
+ group.addoption("--option1", "--option-1", action="store_true")
+ with pytest.raises(ValueError) as err:
+ group.addoption("--option1", "--option-one", action="store_true")
+ assert str(set(["--option1"])) in str(err.value)
+
+ def test_group_shortopt_lowercase(self, parser):
+ group = parser.getgroup("hello")
+ pytest.raises(ValueError, """
+ group.addoption("-x", action="store_true")
+ """)
+ assert len(group.options) == 0
+ group._addoption("-x", action="store_true")
+ assert len(group.options) == 1
+
+ def test_parser_addoption(self, parser):
+ group = parser.getgroup("custom options")
+ assert len(group.options) == 0
+ group.addoption("--option1", action="store_true")
+ assert len(group.options) == 1
+
+ def test_parse(self, parser):
+ parser.addoption("--hello", dest="hello", action="store")
+ args = parser.parse(['--hello', 'world'])
+ assert args.hello == "world"
+ assert not getattr(args, parseopt.FILE_OR_DIR)
+
+ def test_parse2(self, parser):
+ args = parser.parse([py.path.local()])
+ assert getattr(args, parseopt.FILE_OR_DIR)[0] == py.path.local()
+
+ def test_parse_known_args(self, parser):
+ parser.parse_known_args([py.path.local()])
+ parser.addoption("--hello", action="store_true")
+ ns = parser.parse_known_args(["x", "--y", "--hello", "this"])
+ assert ns.hello
+ assert ns.file_or_dir == ['x']
+
+ def test_parse_known_and_unknown_args(self, parser):
+ parser.addoption("--hello", action="store_true")
+ ns, unknown = parser.parse_known_and_unknown_args(["x", "--y",
+ "--hello", "this"])
+ assert ns.hello
+ assert ns.file_or_dir == ['x']
+ assert unknown == ['--y', 'this']
+
+ def test_parse_will_set_default(self, parser):
+ parser.addoption("--hello", dest="hello", default="x", action="store")
+ option = parser.parse([])
+ assert option.hello == "x"
+ del option.hello
+ parser.parse_setoption([], option)
+ assert option.hello == "x"
+
+ def test_parse_setoption(self, parser):
+ parser.addoption("--hello", dest="hello", action="store")
+ parser.addoption("--world", dest="world", default=42)
+
+ class A(object):
+ pass
+
+ option = A()
+ args = parser.parse_setoption(['--hello', 'world'], option)
+ assert option.hello == "world"
+ assert option.world == 42
+ assert not args
+
+ def test_parse_special_destination(self, parser):
+ parser.addoption("--ultimate-answer", type=int)
+ args = parser.parse(['--ultimate-answer', '42'])
+ assert args.ultimate_answer == 42
+
+ def test_parse_split_positional_arguments(self, parser):
+ parser.addoption("-R", action='store_true')
+ parser.addoption("-S", action='store_false')
+ args = parser.parse(['-R', '4', '2', '-S'])
+ assert getattr(args, parseopt.FILE_OR_DIR) == ['4', '2']
+ args = parser.parse(['-R', '-S', '4', '2', '-R'])
+ assert getattr(args, parseopt.FILE_OR_DIR) == ['4', '2']
+ assert args.R is True
+ assert args.S is False
+ args = parser.parse(['-R', '4', '-S', '2'])
+ assert getattr(args, parseopt.FILE_OR_DIR) == ['4', '2']
+ assert args.R is True
+ assert args.S is False
+
+ def test_parse_defaultgetter(self):
+ def defaultget(option):
+ if not hasattr(option, 'type'):
+ return
+ if option.type is int:
+ option.default = 42
+ elif option.type is str:
+ option.default = "world"
+ parser = parseopt.Parser(processopt=defaultget)
+ parser.addoption("--this", dest="this", type=int, action="store")
+ parser.addoption("--hello", dest="hello", type=str, action="store")
+ parser.addoption("--no", dest="no", action="store_true")
+ option = parser.parse([])
+ assert option.hello == "world"
+ assert option.this == 42
+ assert option.no is False
+
+ def test_drop_short_helper(self):
+ parser = py.std.argparse.ArgumentParser(formatter_class=parseopt.DropShorterLongHelpFormatter)
+ parser.add_argument('-t', '--twoword', '--duo', '--two-word', '--two',
+ help='foo').map_long_option = {'two': 'two-word'}
+ # throws error on --deux only!
+ parser.add_argument('-d', '--deuxmots', '--deux-mots',
+ action='store_true', help='foo').map_long_option = {'deux': 'deux-mots'}
+ parser.add_argument('-s', action='store_true', help='single short')
+ parser.add_argument('--abc', '-a',
+ action='store_true', help='bar')
+ parser.add_argument('--klm', '-k', '--kl-m',
+ action='store_true', help='bar')
+ parser.add_argument('-P', '--pq-r', '-p', '--pqr',
+ action='store_true', help='bar')
+ parser.add_argument('--zwei-wort', '--zweiwort', '--zweiwort',
+ action='store_true', help='bar')
+ parser.add_argument('-x', '--exit-on-first', '--exitfirst',
+ action='store_true', help='spam').map_long_option = {'exitfirst': 'exit-on-first'}
+ parser.add_argument('files_and_dirs', nargs='*')
+ args = parser.parse_args(['-k', '--duo', 'hallo', '--exitfirst'])
+ assert args.twoword == 'hallo'
+ assert args.klm is True
+ assert args.zwei_wort is False
+ assert args.exit_on_first is True
+ assert args.s is False
+ args = parser.parse_args(['--deux-mots'])
+ with pytest.raises(AttributeError):
+ assert args.deux_mots is True
+ assert args.deuxmots is True
+ args = parser.parse_args(['file', 'dir'])
+ assert '|'.join(args.files_and_dirs) == 'file|dir'
+
+ def test_drop_short_0(self, parser):
+ parser.addoption('--funcarg', '--func-arg', action='store_true')
+ parser.addoption('--abc-def', '--abc-def', action='store_true')
+ parser.addoption('--klm-hij', action='store_true')
+ args = parser.parse(['--funcarg', '--k'])
+ assert args.funcarg is True
+ assert args.abc_def is False
+ assert args.klm_hij is True
+
+ def test_drop_short_2(self, parser):
+ parser.addoption('--func-arg', '--doit', action='store_true')
+ args = parser.parse(['--doit'])
+ assert args.func_arg is True
+
+ def test_drop_short_3(self, parser):
+ parser.addoption('--func-arg', '--funcarg', '--doit', action='store_true')
+ args = parser.parse(['abcd'])
+ assert args.func_arg is False
+ assert args.file_or_dir == ['abcd']
+
+ def test_drop_short_help0(self, parser, capsys):
+ parser.addoption('--func-args', '--doit', help='foo',
+ action='store_true')
+ parser.parse([])
+ help = parser.optparser.format_help()
+ assert '--func-args, --doit foo' in help
+
+ # testing would be more helpful with all help generated
+ def test_drop_short_help1(self, parser, capsys):
+ group = parser.getgroup("general")
+ group.addoption('--doit', '--func-args', action='store_true', help='foo')
+ group._addoption("-h", "--help", action="store_true", dest="help",
+ help="show help message and configuration info")
+ parser.parse(['-h'])
+ help = parser.optparser.format_help()
+ assert '-doit, --func-args foo' in help
+
+ def test_multiple_metavar_help(self, parser):
+ """
+ Help text for options with a metavar tuple should display help
+ in the form "--preferences=value1 value2 value3" (#2004).
+ """
+ group = parser.getgroup("general")
+ group.addoption('--preferences', metavar=('value1', 'value2', 'value3'), nargs=3)
+ group._addoption("-h", "--help", action="store_true", dest="help")
+ parser.parse(['-h'])
+ help = parser.optparser.format_help()
+ assert '--preferences=value1 value2 value3' in help
+
+
+def test_argcomplete(testdir, monkeypatch):
+ if not py.path.local.sysfind('bash'):
+ pytest.skip("bash not available")
+ script = str(testdir.tmpdir.join("test_argcomplete"))
+ pytest_bin = sys.argv[0]
+ if "pytest" not in os.path.basename(pytest_bin):
+ pytest.skip("need to be run with pytest executable, not %s" % (pytest_bin,))
+
+ with open(str(script), 'w') as fp:
+ # redirect output from argcomplete to stdin and stderr is not trivial
+ # http://stackoverflow.com/q/12589419/1307905
+ # so we use bash
+ fp.write('COMP_WORDBREAKS="$COMP_WORDBREAKS" %s 8>&1 9>&2' % pytest_bin)
+ # alternative would be exteneded Testdir.{run(),_run(),popen()} to be able
+ # to handle a keyword argument env that replaces os.environ in popen or
+ # extends the copy, advantage: could not forget to restore
+ monkeypatch.setenv('_ARGCOMPLETE', "1")
+ monkeypatch.setenv('_ARGCOMPLETE_IFS', "\x0b")
+ monkeypatch.setenv('COMP_WORDBREAKS', ' \\t\\n"\\\'><=;|&(:')
+
+ arg = '--fu'
+ monkeypatch.setenv('COMP_LINE', "pytest " + arg)
+ monkeypatch.setenv('COMP_POINT', str(len("pytest " + arg)))
+ result = testdir.run('bash', str(script), arg)
+ if result.ret == 255:
+ # argcomplete not found
+ pytest.skip("argcomplete not available")
+ elif not result.stdout.str():
+ pytest.skip("bash provided no output, argcomplete not available?")
+ else:
+ result.stdout.fnmatch_lines(["--funcargs", "--fulltrace"])
+ os.mkdir('test_argcomplete.d')
+ arg = 'test_argc'
+ monkeypatch.setenv('COMP_LINE', "pytest " + arg)
+ monkeypatch.setenv('COMP_POINT', str(len('pytest ' + arg)))
+ result = testdir.run('bash', str(script), arg)
+ result.stdout.fnmatch_lines(["test_argcomplete", "test_argcomplete.d/"])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pastebin.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pastebin.py
new file mode 100644
index 00000000000..6b1742d1415
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pastebin.py
@@ -0,0 +1,117 @@
+# encoding: utf-8
+from __future__ import absolute_import, division, print_function
+import sys
+import pytest
+
+
+class TestPasteCapture(object):
+
+ @pytest.fixture
+ def pastebinlist(self, monkeypatch, request):
+ pastebinlist = []
+ plugin = request.config.pluginmanager.getplugin('pastebin')
+ monkeypatch.setattr(plugin, 'create_new_paste', pastebinlist.append)
+ return pastebinlist
+
+ def test_failed(self, testdir, pastebinlist):
+ testpath = testdir.makepyfile("""
+ import pytest
+ def test_pass():
+ pass
+ def test_fail():
+ assert 0
+ def test_skip():
+ pytest.skip("")
+ """)
+ reprec = testdir.inline_run(testpath, "--paste=failed")
+ assert len(pastebinlist) == 1
+ s = pastebinlist[0]
+ assert s.find("def test_fail") != -1
+ assert reprec.countoutcomes() == [1, 1, 1]
+
+ def test_all(self, testdir, pastebinlist):
+ from _pytest.pytester import LineMatcher
+ testpath = testdir.makepyfile("""
+ import pytest
+ def test_pass():
+ pass
+ def test_fail():
+ assert 0
+ def test_skip():
+ pytest.skip("")
+ """)
+ reprec = testdir.inline_run(testpath, "--pastebin=all", '-v')
+ assert reprec.countoutcomes() == [1, 1, 1]
+ assert len(pastebinlist) == 1
+ contents = pastebinlist[0].decode('utf-8')
+ matcher = LineMatcher(contents.splitlines())
+ matcher.fnmatch_lines([
+ '*test_pass PASSED*',
+ '*test_fail FAILED*',
+ '*test_skip SKIPPED*',
+ '*== 1 failed, 1 passed, 1 skipped in *'
+ ])
+
+ def test_non_ascii_paste_text(self, testdir):
+ """Make sure that text which contains non-ascii characters is pasted
+ correctly. See #1219.
+ """
+ testdir.makepyfile(test_unicode="""
+ # encoding: utf-8
+ def test():
+ assert '☺' == 1
+ """)
+ result = testdir.runpytest('--pastebin=all')
+ if sys.version_info[0] == 3:
+ expected_msg = "*assert '☺' == 1*"
+ else:
+ expected_msg = "*assert '\\xe2\\x98\\xba' == 1*"
+ result.stdout.fnmatch_lines([
+ expected_msg,
+ "*== 1 failed in *",
+ '*Sending information to Paste Service*',
+ ])
+
+
+class TestPaste(object):
+
+ @pytest.fixture
+ def pastebin(self, request):
+ return request.config.pluginmanager.getplugin('pastebin')
+
+ @pytest.fixture
+ def mocked_urlopen(self, monkeypatch):
+ """
+ monkeypatch the actual urlopen calls done by the internal plugin
+ function that connects to bpaste service.
+ """
+ calls = []
+
+ def mocked(url, data):
+ calls.append((url, data))
+
+ class DummyFile(object):
+ def read(self):
+ # part of html of a normal response
+ return b'View <a href="/raw/3c0c6750bd">raw</a>.'
+ return DummyFile()
+
+ if sys.version_info < (3, 0):
+ import urllib
+ monkeypatch.setattr(urllib, 'urlopen', mocked)
+ else:
+ import urllib.request
+ monkeypatch.setattr(urllib.request, 'urlopen', mocked)
+ return calls
+
+ def test_create_new_paste(self, pastebin, mocked_urlopen):
+ result = pastebin.create_new_paste(b'full-paste-contents')
+ assert result == 'https://bpaste.net/show/3c0c6750bd'
+ assert len(mocked_urlopen) == 1
+ url, data = mocked_urlopen[0]
+ assert type(data) is bytes
+ lexer = 'python3' if sys.version_info[0] == 3 else 'python'
+ assert url == 'https://bpaste.net'
+ assert 'lexer=%s' % lexer in data.decode()
+ assert 'code=full-paste-contents' in data.decode()
+ assert 'expiry=1week' in data.decode()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pdb.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pdb.py
new file mode 100644
index 00000000000..70a5c3c5bdb
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pdb.py
@@ -0,0 +1,406 @@
+from __future__ import absolute_import, division, print_function
+import sys
+import platform
+
+import _pytest._code
+import pytest
+
+
+def runpdb_and_get_report(testdir, source):
+ p = testdir.makepyfile(source)
+ result = testdir.runpytest_inprocess("--pdb", p)
+ reports = result.reprec.getreports("pytest_runtest_logreport")
+ assert len(reports) == 3, reports # setup/call/teardown
+ return reports[1]
+
+
+@pytest.fixture
+def custom_pdb_calls():
+ called = []
+
+ # install dummy debugger class and track which methods were called on it
+ class _CustomPdb(object):
+ def __init__(self, *args, **kwargs):
+ called.append("init")
+
+ def reset(self):
+ called.append("reset")
+
+ def interaction(self, *args):
+ called.append("interaction")
+
+ _pytest._CustomPdb = _CustomPdb
+ return called
+
+
+class TestPDB(object):
+
+ @pytest.fixture
+ def pdblist(self, request):
+ monkeypatch = request.getfixturevalue("monkeypatch")
+ pdblist = []
+
+ def mypdb(*args):
+ pdblist.append(args)
+
+ plugin = request.config.pluginmanager.getplugin('debugging')
+ monkeypatch.setattr(plugin, 'post_mortem', mypdb)
+ return pdblist
+
+ def test_pdb_on_fail(self, testdir, pdblist):
+ rep = runpdb_and_get_report(testdir, """
+ def test_func():
+ assert 0
+ """)
+ assert rep.failed
+ assert len(pdblist) == 1
+ tb = _pytest._code.Traceback(pdblist[0][0])
+ assert tb[-1].name == "test_func"
+
+ def test_pdb_on_xfail(self, testdir, pdblist):
+ rep = runpdb_and_get_report(testdir, """
+ import pytest
+ @pytest.mark.xfail
+ def test_func():
+ assert 0
+ """)
+ assert "xfail" in rep.keywords
+ assert not pdblist
+
+ def test_pdb_on_skip(self, testdir, pdblist):
+ rep = runpdb_and_get_report(testdir, """
+ import pytest
+ def test_func():
+ pytest.skip("hello")
+ """)
+ assert rep.skipped
+ assert len(pdblist) == 0
+
+ def test_pdb_on_BdbQuit(self, testdir, pdblist):
+ rep = runpdb_and_get_report(testdir, """
+ import bdb
+ def test_func():
+ raise bdb.BdbQuit
+ """)
+ assert rep.failed
+ assert len(pdblist) == 0
+
+ def test_pdb_interaction(self, testdir):
+ p1 = testdir.makepyfile("""
+ def test_1():
+ i = 0
+ assert i == 1
+ """)
+ child = testdir.spawn_pytest("--pdb %s" % p1)
+ child.expect(".*def test_1")
+ child.expect(".*i = 0")
+ child.expect("(Pdb)")
+ child.sendeof()
+ rest = child.read().decode("utf8")
+ assert "1 failed" in rest
+ assert "def test_1" not in rest
+ self.flush(child)
+
+ @staticmethod
+ def flush(child):
+ if platform.system() == 'Darwin':
+ return
+ if child.isalive():
+ child.wait()
+
+ def test_pdb_unittest_postmortem(self, testdir):
+ p1 = testdir.makepyfile("""
+ import unittest
+ class Blub(unittest.TestCase):
+ def tearDown(self):
+ self.filename = None
+ def test_false(self):
+ self.filename = 'debug' + '.me'
+ assert 0
+ """)
+ child = testdir.spawn_pytest("--pdb %s" % p1)
+ child.expect('(Pdb)')
+ child.sendline('p self.filename')
+ child.sendeof()
+ rest = child.read().decode("utf8")
+ assert 'debug.me' in rest
+ self.flush(child)
+
+ def test_pdb_unittest_skip(self, testdir):
+ """Test for issue #2137"""
+ p1 = testdir.makepyfile("""
+ import unittest
+ @unittest.skipIf(True, 'Skipping also with pdb active')
+ class MyTestCase(unittest.TestCase):
+ def test_one(self):
+ assert 0
+ """)
+ child = testdir.spawn_pytest("-rs --pdb %s" % p1)
+ child.expect('Skipping also with pdb active')
+ child.expect('1 skipped in')
+ child.sendeof()
+ self.flush(child)
+
+ def test_pdb_interaction_capture(self, testdir):
+ p1 = testdir.makepyfile("""
+ def test_1():
+ print("getrekt")
+ assert False
+ """)
+ child = testdir.spawn_pytest("--pdb %s" % p1)
+ child.expect("getrekt")
+ child.expect("(Pdb)")
+ child.sendeof()
+ rest = child.read().decode("utf8")
+ assert "1 failed" in rest
+ assert "getrekt" not in rest
+ self.flush(child)
+
+ def test_pdb_interaction_exception(self, testdir):
+ p1 = testdir.makepyfile("""
+ import pytest
+ def globalfunc():
+ pass
+ def test_1():
+ pytest.raises(ValueError, globalfunc)
+ """)
+ child = testdir.spawn_pytest("--pdb %s" % p1)
+ child.expect(".*def test_1")
+ child.expect(".*pytest.raises.*globalfunc")
+ child.expect("(Pdb)")
+ child.sendline("globalfunc")
+ child.expect(".*function")
+ child.sendeof()
+ child.expect("1 failed")
+ self.flush(child)
+
+ def test_pdb_interaction_on_collection_issue181(self, testdir):
+ p1 = testdir.makepyfile("""
+ import pytest
+ xxx
+ """)
+ child = testdir.spawn_pytest("--pdb %s" % p1)
+ # child.expect(".*import pytest.*")
+ child.expect("(Pdb)")
+ child.sendeof()
+ child.expect("1 error")
+ self.flush(child)
+
+ def test_pdb_interaction_on_internal_error(self, testdir):
+ testdir.makeconftest("""
+ def pytest_runtest_protocol():
+ 0/0
+ """)
+ p1 = testdir.makepyfile("def test_func(): pass")
+ child = testdir.spawn_pytest("--pdb %s" % p1)
+ # child.expect(".*import pytest.*")
+ child.expect("(Pdb)")
+ child.sendeof()
+ self.flush(child)
+
+ def test_pdb_interaction_capturing_simple(self, testdir):
+ p1 = testdir.makepyfile("""
+ import pytest
+ def test_1():
+ i = 0
+ print ("hello17")
+ pytest.set_trace()
+ x = 3
+ """)
+ child = testdir.spawn_pytest(str(p1))
+ child.expect("test_1")
+ child.expect("x = 3")
+ child.expect("(Pdb)")
+ child.sendeof()
+ rest = child.read().decode("utf-8")
+ assert "1 failed" in rest
+ assert "def test_1" in rest
+ assert "hello17" in rest # out is captured
+ self.flush(child)
+
+ def test_pdb_set_trace_interception(self, testdir):
+ p1 = testdir.makepyfile("""
+ import pdb
+ def test_1():
+ pdb.set_trace()
+ """)
+ child = testdir.spawn_pytest(str(p1))
+ child.expect("test_1")
+ child.expect("(Pdb)")
+ child.sendeof()
+ rest = child.read().decode("utf8")
+ assert "1 failed" in rest
+ assert "reading from stdin while output" not in rest
+ self.flush(child)
+
+ def test_pdb_and_capsys(self, testdir):
+ p1 = testdir.makepyfile("""
+ import pytest
+ def test_1(capsys):
+ print ("hello1")
+ pytest.set_trace()
+ """)
+ child = testdir.spawn_pytest(str(p1))
+ child.expect("test_1")
+ child.send("capsys.readouterr()\n")
+ child.expect("hello1")
+ child.sendeof()
+ child.read()
+ self.flush(child)
+
+ def test_set_trace_capturing_afterwards(self, testdir):
+ p1 = testdir.makepyfile("""
+ import pdb
+ def test_1():
+ pdb.set_trace()
+ def test_2():
+ print ("hello")
+ assert 0
+ """)
+ child = testdir.spawn_pytest(str(p1))
+ child.expect("test_1")
+ child.send("c\n")
+ child.expect("test_2")
+ child.expect("Captured")
+ child.expect("hello")
+ child.sendeof()
+ child.read()
+ self.flush(child)
+
+ def test_pdb_interaction_doctest(self, testdir):
+ p1 = testdir.makepyfile("""
+ import pytest
+ def function_1():
+ '''
+ >>> i = 0
+ >>> assert i == 1
+ '''
+ """)
+ child = testdir.spawn_pytest("--doctest-modules --pdb %s" % p1)
+ child.expect("(Pdb)")
+ child.sendline('i')
+ child.expect("0")
+ child.expect("(Pdb)")
+ child.sendeof()
+ rest = child.read().decode("utf8")
+ assert "1 failed" in rest
+ self.flush(child)
+
+ def test_pdb_interaction_capturing_twice(self, testdir):
+ p1 = testdir.makepyfile("""
+ import pytest
+ def test_1():
+ i = 0
+ print ("hello17")
+ pytest.set_trace()
+ x = 3
+ print ("hello18")
+ pytest.set_trace()
+ x = 4
+ """)
+ child = testdir.spawn_pytest(str(p1))
+ child.expect("test_1")
+ child.expect("x = 3")
+ child.expect("(Pdb)")
+ child.sendline('c')
+ child.expect("x = 4")
+ child.sendeof()
+ rest = child.read().decode("utf8")
+ assert "1 failed" in rest
+ assert "def test_1" in rest
+ assert "hello17" in rest # out is captured
+ assert "hello18" in rest # out is captured
+ self.flush(child)
+
+ def test_pdb_used_outside_test(self, testdir):
+ p1 = testdir.makepyfile("""
+ import pytest
+ pytest.set_trace()
+ x = 5
+ """)
+ child = testdir.spawn("%s %s" % (sys.executable, p1))
+ child.expect("x = 5")
+ child.sendeof()
+ self.flush(child)
+
+ def test_pdb_used_in_generate_tests(self, testdir):
+ p1 = testdir.makepyfile("""
+ import pytest
+ def pytest_generate_tests(metafunc):
+ pytest.set_trace()
+ x = 5
+ def test_foo(a):
+ pass
+ """)
+ child = testdir.spawn_pytest(str(p1))
+ child.expect("x = 5")
+ child.sendeof()
+ self.flush(child)
+
+ def test_pdb_collection_failure_is_shown(self, testdir):
+ p1 = testdir.makepyfile("""xxx """)
+ result = testdir.runpytest_subprocess("--pdb", p1)
+ result.stdout.fnmatch_lines([
+ "*NameError*xxx*",
+ "*1 error*",
+ ])
+
+ def test_enter_pdb_hook_is_called(self, testdir):
+ testdir.makeconftest("""
+ def pytest_enter_pdb(config):
+ assert config.testing_verification == 'configured'
+ print 'enter_pdb_hook'
+
+ def pytest_configure(config):
+ config.testing_verification = 'configured'
+ """)
+ p1 = testdir.makepyfile("""
+ import pytest
+
+ def test_foo():
+ pytest.set_trace()
+ """)
+ child = testdir.spawn_pytest(str(p1))
+ child.expect("enter_pdb_hook")
+ child.send('c\n')
+ child.sendeof()
+ self.flush(child)
+
+ def test_pdb_custom_cls(self, testdir, custom_pdb_calls):
+ p1 = testdir.makepyfile("""xxx """)
+ result = testdir.runpytest_inprocess(
+ "--pdb", "--pdbcls=_pytest:_CustomPdb", p1)
+ result.stdout.fnmatch_lines([
+ "*NameError*xxx*",
+ "*1 error*",
+ ])
+ assert custom_pdb_calls == ["init", "reset", "interaction"]
+
+ def test_pdb_custom_cls_without_pdb(self, testdir, custom_pdb_calls):
+ p1 = testdir.makepyfile("""xxx """)
+ result = testdir.runpytest_inprocess(
+ "--pdbcls=_pytest:_CustomPdb", p1)
+ result.stdout.fnmatch_lines([
+ "*NameError*xxx*",
+ "*1 error*",
+ ])
+ assert custom_pdb_calls == []
+
+ def test_pdb_custom_cls_with_settrace(self, testdir, monkeypatch):
+ testdir.makepyfile(custom_pdb="""
+ class CustomPdb(object):
+ def set_trace(*args, **kwargs):
+ print 'custom set_trace>'
+ """)
+ p1 = testdir.makepyfile("""
+ import pytest
+
+ def test_foo():
+ pytest.set_trace()
+ """)
+ monkeypatch.setenv('PYTHONPATH', str(testdir.tmpdir))
+ child = testdir.spawn_pytest("--pdbcls=custom_pdb:CustomPdb %s" % str(p1))
+
+ child.expect('custom set_trace>')
+ if child.isalive():
+ child.wait()
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pluginmanager.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pluginmanager.py
new file mode 100644
index 00000000000..6192176e8a8
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pluginmanager.py
@@ -0,0 +1,361 @@
+# encoding: UTF-8
+from __future__ import absolute_import, division, print_function
+import pytest
+import py
+import os
+
+from _pytest.config import get_config, PytestPluginManager
+from _pytest.main import EXIT_NOTESTSCOLLECTED, Session
+
+
+@pytest.fixture
+def pytestpm():
+ return PytestPluginManager()
+
+
+class TestPytestPluginInteractions(object):
+ def test_addhooks_conftestplugin(self, testdir):
+ testdir.makepyfile(newhooks="""
+ def pytest_myhook(xyz):
+ "new hook"
+ """)
+ conf = testdir.makeconftest("""
+ import sys ; sys.path.insert(0, '.')
+ import newhooks
+ def pytest_addhooks(pluginmanager):
+ pluginmanager.addhooks(newhooks)
+ def pytest_myhook(xyz):
+ return xyz + 1
+ """)
+ config = get_config()
+ pm = config.pluginmanager
+ pm.hook.pytest_addhooks.call_historic(
+ kwargs=dict(pluginmanager=config.pluginmanager))
+ config.pluginmanager._importconftest(conf)
+ # print(config.pluginmanager.get_plugins())
+ res = config.hook.pytest_myhook(xyz=10)
+ assert res == [11]
+
+ def test_addhooks_nohooks(self, testdir):
+ testdir.makeconftest("""
+ import sys
+ def pytest_addhooks(pluginmanager):
+ pluginmanager.addhooks(sys)
+ """)
+ res = testdir.runpytest()
+ assert res.ret != 0
+ res.stderr.fnmatch_lines([
+ "*did not find*sys*"
+ ])
+
+ def test_namespace_early_from_import(self, testdir):
+ p = testdir.makepyfile("""
+ from pytest import Item
+ from pytest import Item as Item2
+ assert Item is Item2
+ """)
+ result = testdir.runpython(p)
+ assert result.ret == 0
+
+ def test_do_ext_namespace(self, testdir):
+ testdir.makeconftest("""
+ def pytest_namespace():
+ return {'hello': 'world'}
+ """)
+ p = testdir.makepyfile("""
+ from pytest import hello
+ import pytest
+ def test_hello():
+ assert hello == "world"
+ assert 'hello' in pytest.__all__
+ """)
+ reprec = testdir.inline_run(p)
+ reprec.assertoutcome(passed=1)
+
+ def test_do_option_postinitialize(self, testdir):
+ config = testdir.parseconfigure()
+ assert not hasattr(config.option, 'test123')
+ p = testdir.makepyfile("""
+ def pytest_addoption(parser):
+ parser.addoption('--test123', action="store_true",
+ default=True)
+ """)
+ config.pluginmanager._importconftest(p)
+ assert config.option.test123
+
+ def test_configure(self, testdir):
+ config = testdir.parseconfig()
+ values = []
+
+ class A(object):
+ def pytest_configure(self, config):
+ values.append(self)
+
+ config.pluginmanager.register(A())
+ assert len(values) == 0
+ config._do_configure()
+ assert len(values) == 1
+ config.pluginmanager.register(A()) # leads to a configured() plugin
+ assert len(values) == 2
+ assert values[0] != values[1]
+
+ config._ensure_unconfigure()
+ config.pluginmanager.register(A())
+ assert len(values) == 2
+
+ def test_hook_tracing(self):
+ pytestpm = get_config().pluginmanager # fully initialized with plugins
+ saveindent = []
+
+ class api1(object):
+ def pytest_plugin_registered(self):
+ saveindent.append(pytestpm.trace.root.indent)
+
+ class api2(object):
+ def pytest_plugin_registered(self):
+ saveindent.append(pytestpm.trace.root.indent)
+ raise ValueError()
+
+ values = []
+ pytestpm.trace.root.setwriter(values.append)
+ undo = pytestpm.enable_tracing()
+ try:
+ indent = pytestpm.trace.root.indent
+ p = api1()
+ pytestpm.register(p)
+ assert pytestpm.trace.root.indent == indent
+ assert len(values) >= 2
+ assert 'pytest_plugin_registered' in values[0]
+ assert 'finish' in values[1]
+
+ values[:] = []
+ with pytest.raises(ValueError):
+ pytestpm.register(api2())
+ assert pytestpm.trace.root.indent == indent
+ assert saveindent[0] > indent
+ finally:
+ undo()
+
+ def test_hook_proxy(self, testdir):
+ """Test the gethookproxy function(#2016)"""
+ config = testdir.parseconfig()
+ session = Session(config)
+ testdir.makepyfile(**{
+ 'tests/conftest.py': '',
+ 'tests/subdir/conftest.py': '',
+ })
+
+ conftest1 = testdir.tmpdir.join('tests/conftest.py')
+ conftest2 = testdir.tmpdir.join('tests/subdir/conftest.py')
+
+ config.pluginmanager._importconftest(conftest1)
+ ihook_a = session.gethookproxy(testdir.tmpdir.join('tests'))
+ assert ihook_a is not None
+ config.pluginmanager._importconftest(conftest2)
+ ihook_b = session.gethookproxy(testdir.tmpdir.join('tests'))
+ assert ihook_a is not ihook_b
+
+ def test_warn_on_deprecated_addhooks(self, pytestpm):
+ warnings = []
+
+ class get_warnings(object):
+ def pytest_logwarning(self, code, fslocation, message, nodeid):
+ warnings.append(message)
+
+ class Plugin(object):
+ def pytest_testhook():
+ pass
+
+ pytestpm.register(get_warnings())
+ before = list(warnings)
+ pytestpm.addhooks(Plugin())
+ assert len(warnings) == len(before) + 1
+ assert "deprecated" in warnings[-1]
+
+
+def test_namespace_has_default_and_env_plugins(testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ pytest.mark
+ """)
+ result = testdir.runpython(p)
+ assert result.ret == 0
+
+
+def test_default_markers(testdir):
+ result = testdir.runpytest("--markers")
+ result.stdout.fnmatch_lines([
+ "*tryfirst*first*",
+ "*trylast*last*",
+ ])
+
+
+def test_importplugin_error_message(testdir, pytestpm):
+ """Don't hide import errors when importing plugins and provide
+ an easy to debug message.
+
+ See #375 and #1998.
+ """
+ testdir.syspathinsert(testdir.tmpdir)
+ testdir.makepyfile(qwe="""
+ # encoding: UTF-8
+ def test_traceback():
+ raise ImportError(u'Not possible to import: ☺')
+ test_traceback()
+ """)
+ with pytest.raises(ImportError) as excinfo:
+ pytestpm.import_plugin("qwe")
+
+ expected_message = '.*Error importing plugin "qwe": Not possible to import: .'
+ expected_traceback = ".*in test_traceback"
+ assert py.std.re.match(expected_message, str(excinfo.value))
+ assert py.std.re.match(expected_traceback, str(excinfo.traceback[-1]))
+
+
+class TestPytestPluginManager(object):
+ def test_register_imported_modules(self):
+ pm = PytestPluginManager()
+ mod = py.std.types.ModuleType("x.y.pytest_hello")
+ pm.register(mod)
+ assert pm.is_registered(mod)
+ values = pm.get_plugins()
+ assert mod in values
+ pytest.raises(ValueError, "pm.register(mod)")
+ pytest.raises(ValueError, lambda: pm.register(mod))
+ # assert not pm.is_registered(mod2)
+ assert pm.get_plugins() == values
+
+ def test_canonical_import(self, monkeypatch):
+ mod = py.std.types.ModuleType("pytest_xyz")
+ monkeypatch.setitem(py.std.sys.modules, 'pytest_xyz', mod)
+ pm = PytestPluginManager()
+ pm.import_plugin('pytest_xyz')
+ assert pm.get_plugin('pytest_xyz') == mod
+ assert pm.is_registered(mod)
+
+ def test_consider_module(self, testdir, pytestpm):
+ testdir.syspathinsert()
+ testdir.makepyfile(pytest_p1="#")
+ testdir.makepyfile(pytest_p2="#")
+ mod = py.std.types.ModuleType("temp")
+ mod.pytest_plugins = ["pytest_p1", "pytest_p2"]
+ pytestpm.consider_module(mod)
+ assert pytestpm.get_plugin("pytest_p1").__name__ == "pytest_p1"
+ assert pytestpm.get_plugin("pytest_p2").__name__ == "pytest_p2"
+
+ def test_consider_module_import_module(self, testdir):
+ pytestpm = get_config().pluginmanager
+ mod = py.std.types.ModuleType("x")
+ mod.pytest_plugins = "pytest_a"
+ aplugin = testdir.makepyfile(pytest_a="#")
+ reprec = testdir.make_hook_recorder(pytestpm)
+ # syspath.prepend(aplugin.dirpath())
+ py.std.sys.path.insert(0, str(aplugin.dirpath()))
+ pytestpm.consider_module(mod)
+ call = reprec.getcall(pytestpm.hook.pytest_plugin_registered.name)
+ assert call.plugin.__name__ == "pytest_a"
+
+ # check that it is not registered twice
+ pytestpm.consider_module(mod)
+ values = reprec.getcalls("pytest_plugin_registered")
+ assert len(values) == 1
+
+ def test_consider_env_fails_to_import(self, monkeypatch, pytestpm):
+ monkeypatch.setenv('PYTEST_PLUGINS', 'nonexisting', prepend=",")
+ with pytest.raises(ImportError):
+ pytestpm.consider_env()
+
+ def test_plugin_skip(self, testdir, monkeypatch):
+ p = testdir.makepyfile(skipping1="""
+ import pytest
+ pytest.skip("hello")
+ """)
+ p.copy(p.dirpath("skipping2.py"))
+ monkeypatch.setenv("PYTEST_PLUGINS", "skipping2")
+ result = testdir.runpytest("-rw", "-p", "skipping1", syspathinsert=True)
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ result.stdout.fnmatch_lines([
+ "*skipped plugin*skipping1*hello*",
+ "*skipped plugin*skipping2*hello*",
+ ])
+
+ def test_consider_env_plugin_instantiation(self, testdir, monkeypatch, pytestpm):
+ testdir.syspathinsert()
+ testdir.makepyfile(xy123="#")
+ monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'xy123')
+ l1 = len(pytestpm.get_plugins())
+ pytestpm.consider_env()
+ l2 = len(pytestpm.get_plugins())
+ assert l2 == l1 + 1
+ assert pytestpm.get_plugin('xy123')
+ pytestpm.consider_env()
+ l3 = len(pytestpm.get_plugins())
+ assert l2 == l3
+
+ def test_pluginmanager_ENV_startup(self, testdir, monkeypatch):
+ testdir.makepyfile(pytest_x500="#")
+ p = testdir.makepyfile("""
+ import pytest
+ def test_hello(pytestconfig):
+ plugin = pytestconfig.pluginmanager.get_plugin('pytest_x500')
+ assert plugin is not None
+ """)
+ monkeypatch.setenv('PYTEST_PLUGINS', 'pytest_x500', prepend=",")
+ result = testdir.runpytest(p, syspathinsert=True)
+ assert result.ret == 0
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_import_plugin_importname(self, testdir, pytestpm):
+ pytest.raises(ImportError, 'pytestpm.import_plugin("qweqwex.y")')
+ pytest.raises(ImportError, 'pytestpm.import_plugin("pytest_qweqwx.y")')
+
+ testdir.syspathinsert()
+ pluginname = "pytest_hello"
+ testdir.makepyfile(**{pluginname: ""})
+ pytestpm.import_plugin("pytest_hello")
+ len1 = len(pytestpm.get_plugins())
+ pytestpm.import_plugin("pytest_hello")
+ len2 = len(pytestpm.get_plugins())
+ assert len1 == len2
+ plugin1 = pytestpm.get_plugin("pytest_hello")
+ assert plugin1.__name__.endswith('pytest_hello')
+ plugin2 = pytestpm.get_plugin("pytest_hello")
+ assert plugin2 is plugin1
+
+ def test_import_plugin_dotted_name(self, testdir, pytestpm):
+ pytest.raises(ImportError, 'pytestpm.import_plugin("qweqwex.y")')
+ pytest.raises(ImportError, 'pytestpm.import_plugin("pytest_qweqwex.y")')
+
+ testdir.syspathinsert()
+ testdir.mkpydir("pkg").join("plug.py").write("x=3")
+ pluginname = "pkg.plug"
+ pytestpm.import_plugin(pluginname)
+ mod = pytestpm.get_plugin("pkg.plug")
+ assert mod.x == 3
+
+ def test_consider_conftest_deps(self, testdir, pytestpm):
+ mod = testdir.makepyfile("pytest_plugins='xyz'").pyimport()
+ with pytest.raises(ImportError):
+ pytestpm.consider_conftest(mod)
+
+
+class TestPytestPluginManagerBootstrapming(object):
+ def test_preparse_args(self, pytestpm):
+ pytest.raises(ImportError, lambda:
+ pytestpm.consider_preparse(["xyz", "-p", "hello123"]))
+
+ def test_plugin_prevent_register(self, pytestpm):
+ pytestpm.consider_preparse(["xyz", "-p", "no:abc"])
+ l1 = pytestpm.get_plugins()
+ pytestpm.register(42, name="abc")
+ l2 = pytestpm.get_plugins()
+ assert len(l2) == len(l1)
+ assert 42 not in l2
+
+ def test_plugin_prevent_register_unregistered_alredy_registered(self, pytestpm):
+ pytestpm.register(42, name="abc")
+ l1 = pytestpm.get_plugins()
+ assert 42 in l1
+ pytestpm.consider_preparse(["xyz", "-p", "no:abc"])
+ l2 = pytestpm.get_plugins()
+ assert 42 not in l2
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pytester.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pytester.py
new file mode 100644
index 00000000000..9508c2954e8
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_pytester.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+import pytest
+import os
+from _pytest.pytester import HookRecorder
+from _pytest.config import PytestPluginManager
+from _pytest.main import EXIT_OK, EXIT_TESTSFAILED
+
+
+def test_make_hook_recorder(testdir):
+ item = testdir.getitem("def test_func(): pass")
+ recorder = testdir.make_hook_recorder(item.config.pluginmanager)
+ assert not recorder.getfailures()
+
+ pytest.xfail("internal reportrecorder tests need refactoring")
+
+ class rep(object):
+ excinfo = None
+ passed = False
+ failed = True
+ skipped = False
+ when = "call"
+
+ recorder.hook.pytest_runtest_logreport(report=rep)
+ failures = recorder.getfailures()
+ assert failures == [rep]
+ failures = recorder.getfailures()
+ assert failures == [rep]
+
+ class rep(object):
+ excinfo = None
+ passed = False
+ failed = False
+ skipped = True
+ when = "call"
+ rep.passed = False
+ rep.skipped = True
+ recorder.hook.pytest_runtest_logreport(report=rep)
+
+ modcol = testdir.getmodulecol("")
+ rep = modcol.config.hook.pytest_make_collect_report(collector=modcol)
+ rep.passed = False
+ rep.failed = True
+ rep.skipped = False
+ recorder.hook.pytest_collectreport(report=rep)
+
+ passed, skipped, failed = recorder.listoutcomes()
+ assert not passed and skipped and failed
+
+ numpassed, numskipped, numfailed = recorder.countoutcomes()
+ assert numpassed == 0
+ assert numskipped == 1
+ assert numfailed == 1
+ assert len(recorder.getfailedcollections()) == 1
+
+ recorder.unregister()
+ recorder.clear()
+ recorder.hook.pytest_runtest_logreport(report=rep)
+ pytest.raises(ValueError, "recorder.getfailures()")
+
+
+def test_parseconfig(testdir):
+ config1 = testdir.parseconfig()
+ config2 = testdir.parseconfig()
+ assert config2 != config1
+ assert config1 != pytest.config
+
+
+def test_testdir_runs_with_plugin(testdir):
+ testdir.makepyfile("""
+ pytest_plugins = "pytester"
+ def test_hello(testdir):
+ assert 1
+ """)
+ result = testdir.runpytest()
+ result.assert_outcomes(passed=1)
+
+
+def make_holder():
+ class apiclass(object):
+ def pytest_xyz(self, arg):
+ "x"
+
+ def pytest_xyz_noarg(self):
+ "x"
+
+ apimod = type(os)('api')
+
+ def pytest_xyz(arg):
+ "x"
+
+ def pytest_xyz_noarg():
+ "x"
+
+ apimod.pytest_xyz = pytest_xyz
+ apimod.pytest_xyz_noarg = pytest_xyz_noarg
+ return apiclass, apimod
+
+
+@pytest.mark.parametrize("holder", make_holder())
+def test_hookrecorder_basic(holder):
+ pm = PytestPluginManager()
+ pm.addhooks(holder)
+ rec = HookRecorder(pm)
+ pm.hook.pytest_xyz(arg=123)
+ call = rec.popcall("pytest_xyz")
+ assert call.arg == 123
+ assert call._name == "pytest_xyz"
+ pytest.raises(pytest.fail.Exception, "rec.popcall('abc')")
+ pm.hook.pytest_xyz_noarg()
+ call = rec.popcall("pytest_xyz_noarg")
+ assert call._name == "pytest_xyz_noarg"
+
+
+def test_makepyfile_unicode(testdir):
+ global unichr
+ try:
+ unichr(65)
+ except NameError:
+ unichr = chr
+ testdir.makepyfile(unichr(0xfffd))
+
+
+def test_makepyfile_utf8(testdir):
+ """Ensure makepyfile accepts utf-8 bytes as input (#2738)"""
+ utf8_contents = u"""
+ def setup_function(function):
+ mixed_encoding = u'São Paulo'
+ """.encode('utf-8')
+ p = testdir.makepyfile(utf8_contents)
+ assert u"mixed_encoding = u'São Paulo'".encode('utf-8') in p.read('rb')
+
+
+def test_inline_run_clean_modules(testdir):
+ test_mod = testdir.makepyfile("def test_foo(): assert True")
+ result = testdir.inline_run(str(test_mod))
+ assert result.ret == EXIT_OK
+ # rewrite module, now test should fail if module was re-imported
+ test_mod.write("def test_foo(): assert False")
+ result2 = testdir.inline_run(str(test_mod))
+ assert result2.ret == EXIT_TESTSFAILED
+
+
+def test_assert_outcomes_after_pytest_erro(testdir):
+ testdir.makepyfile("def test_foo(): assert True")
+
+ result = testdir.runpytest('--unexpected-argument')
+ with pytest.raises(ValueError, message="Pytest terminal report not found"):
+ result.assert_outcomes(passed=0)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_recwarn.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_recwarn.py
new file mode 100644
index 00000000000..31e70460fe4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_recwarn.py
@@ -0,0 +1,310 @@
+from __future__ import absolute_import, division, print_function
+import warnings
+import re
+import py
+
+import pytest
+from _pytest.recwarn import WarningsRecorder
+
+
+def test_recwarn_functional(testdir):
+ reprec = testdir.inline_runsource("""
+ import warnings
+ def test_method(recwarn):
+ warnings.warn("hello")
+ warn = recwarn.pop()
+ assert isinstance(warn.message, UserWarning)
+ """)
+ res = reprec.countoutcomes()
+ assert tuple(res) == (1, 0, 0), res
+
+
+class TestWarningsRecorderChecker(object):
+ def test_recording(self):
+ rec = WarningsRecorder()
+ with rec:
+ assert not rec.list
+ py.std.warnings.warn_explicit("hello", UserWarning, "xyz", 13)
+ assert len(rec.list) == 1
+ py.std.warnings.warn(DeprecationWarning("hello"))
+ assert len(rec.list) == 2
+ warn = rec.pop()
+ assert str(warn.message) == "hello"
+ values = rec.list
+ rec.clear()
+ assert len(rec.list) == 0
+ assert values is rec.list
+ pytest.raises(AssertionError, "rec.pop()")
+
+ def test_typechecking(self):
+ from _pytest.recwarn import WarningsChecker
+ with pytest.raises(TypeError):
+ WarningsChecker(5)
+ with pytest.raises(TypeError):
+ WarningsChecker(('hi', RuntimeWarning))
+ with pytest.raises(TypeError):
+ WarningsChecker([DeprecationWarning, RuntimeWarning])
+
+ def test_invalid_enter_exit(self):
+ # wrap this test in WarningsRecorder to ensure warning state gets reset
+ with WarningsRecorder():
+ with pytest.raises(RuntimeError):
+ rec = WarningsRecorder()
+ rec.__exit__(None, None, None) # can't exit before entering
+
+ with pytest.raises(RuntimeError):
+ rec = WarningsRecorder()
+ with rec:
+ with rec:
+ pass # can't enter twice
+
+
+class TestDeprecatedCall(object):
+ """test pytest.deprecated_call()"""
+
+ def dep(self, i, j=None):
+ if i == 0:
+ py.std.warnings.warn("is deprecated", DeprecationWarning,
+ stacklevel=1)
+ return 42
+
+ def dep_explicit(self, i):
+ if i == 0:
+ py.std.warnings.warn_explicit("dep_explicit", category=DeprecationWarning,
+ filename="hello", lineno=3)
+
+ def test_deprecated_call_raises(self):
+ with pytest.raises(AssertionError) as excinfo:
+ pytest.deprecated_call(self.dep, 3, 5)
+ assert 'Did not produce' in str(excinfo)
+
+ def test_deprecated_call(self):
+ pytest.deprecated_call(self.dep, 0, 5)
+
+ def test_deprecated_call_ret(self):
+ ret = pytest.deprecated_call(self.dep, 0)
+ assert ret == 42
+
+ def test_deprecated_call_preserves(self):
+ onceregistry = py.std.warnings.onceregistry.copy()
+ filters = py.std.warnings.filters[:]
+ warn = py.std.warnings.warn
+ warn_explicit = py.std.warnings.warn_explicit
+ self.test_deprecated_call_raises()
+ self.test_deprecated_call()
+ assert onceregistry == py.std.warnings.onceregistry
+ assert filters == py.std.warnings.filters
+ assert warn is py.std.warnings.warn
+ assert warn_explicit is py.std.warnings.warn_explicit
+
+ def test_deprecated_explicit_call_raises(self):
+ with pytest.raises(AssertionError):
+ pytest.deprecated_call(self.dep_explicit, 3)
+
+ def test_deprecated_explicit_call(self):
+ pytest.deprecated_call(self.dep_explicit, 0)
+ pytest.deprecated_call(self.dep_explicit, 0)
+
+ @pytest.mark.parametrize('mode', ['context_manager', 'call'])
+ def test_deprecated_call_no_warning(self, mode):
+ """Ensure deprecated_call() raises the expected failure when its block/function does
+ not raise a deprecation warning.
+ """
+ def f():
+ pass
+
+ msg = 'Did not produce DeprecationWarning or PendingDeprecationWarning'
+ with pytest.raises(AssertionError, matches=msg):
+ if mode == 'call':
+ pytest.deprecated_call(f)
+ else:
+ with pytest.deprecated_call():
+ f()
+
+ @pytest.mark.parametrize('warning_type', [PendingDeprecationWarning, DeprecationWarning])
+ @pytest.mark.parametrize('mode', ['context_manager', 'call'])
+ @pytest.mark.parametrize('call_f_first', [True, False])
+ @pytest.mark.filterwarnings('ignore')
+ def test_deprecated_call_modes(self, warning_type, mode, call_f_first):
+ """Ensure deprecated_call() captures a deprecation warning as expected inside its
+ block/function.
+ """
+ def f():
+ warnings.warn(warning_type("hi"))
+ return 10
+
+ # ensure deprecated_call() can capture the warning even if it has already been triggered
+ if call_f_first:
+ assert f() == 10
+ if mode == 'call':
+ assert pytest.deprecated_call(f) == 10
+ else:
+ with pytest.deprecated_call():
+ assert f() == 10
+
+ @pytest.mark.parametrize('mode', ['context_manager', 'call'])
+ def test_deprecated_call_exception_is_raised(self, mode):
+ """If the block of the code being tested by deprecated_call() raises an exception,
+ it must raise the exception undisturbed.
+ """
+ def f():
+ raise ValueError('some exception')
+
+ with pytest.raises(ValueError, match='some exception'):
+ if mode == 'call':
+ pytest.deprecated_call(f)
+ else:
+ with pytest.deprecated_call():
+ f()
+
+ def test_deprecated_call_specificity(self):
+ other_warnings = [Warning, UserWarning, SyntaxWarning, RuntimeWarning,
+ FutureWarning, ImportWarning, UnicodeWarning]
+ for warning in other_warnings:
+ def f():
+ warnings.warn(warning("hi"))
+
+ with pytest.raises(AssertionError):
+ pytest.deprecated_call(f)
+ with pytest.raises(AssertionError):
+ with pytest.deprecated_call():
+ f()
+
+
+class TestWarns(object):
+ def test_strings(self):
+ # different messages, b/c Python suppresses multiple identical warnings
+ source1 = "warnings.warn('w1', RuntimeWarning)"
+ source2 = "warnings.warn('w2', RuntimeWarning)"
+ source3 = "warnings.warn('w3', RuntimeWarning)"
+ pytest.warns(RuntimeWarning, source1)
+ pytest.raises(pytest.fail.Exception,
+ lambda: pytest.warns(UserWarning, source2))
+ pytest.warns(RuntimeWarning, source3)
+
+ def test_function(self):
+ pytest.warns(SyntaxWarning,
+ lambda msg: warnings.warn(msg, SyntaxWarning), "syntax")
+
+ def test_warning_tuple(self):
+ pytest.warns((RuntimeWarning, SyntaxWarning),
+ lambda: warnings.warn('w1', RuntimeWarning))
+ pytest.warns((RuntimeWarning, SyntaxWarning),
+ lambda: warnings.warn('w2', SyntaxWarning))
+ pytest.raises(pytest.fail.Exception,
+ lambda: pytest.warns(
+ (RuntimeWarning, SyntaxWarning),
+ lambda: warnings.warn('w3', UserWarning)))
+
+ def test_as_contextmanager(self):
+ with pytest.warns(RuntimeWarning):
+ warnings.warn("runtime", RuntimeWarning)
+
+ with pytest.warns(UserWarning):
+ warnings.warn("user", UserWarning)
+
+ with pytest.raises(pytest.fail.Exception) as excinfo:
+ with pytest.warns(RuntimeWarning):
+ warnings.warn("user", UserWarning)
+ excinfo.match(r"DID NOT WARN. No warnings of type \(.+RuntimeWarning.+,\) was emitted. "
+ r"The list of emitted warnings is: \[UserWarning\('user',\)\].")
+
+ with pytest.raises(pytest.fail.Exception) as excinfo:
+ with pytest.warns(UserWarning):
+ warnings.warn("runtime", RuntimeWarning)
+ excinfo.match(r"DID NOT WARN. No warnings of type \(.+UserWarning.+,\) was emitted. "
+ r"The list of emitted warnings is: \[RuntimeWarning\('runtime',\)\].")
+
+ with pytest.raises(pytest.fail.Exception) as excinfo:
+ with pytest.warns(UserWarning):
+ pass
+ excinfo.match(r"DID NOT WARN. No warnings of type \(.+UserWarning.+,\) was emitted. "
+ r"The list of emitted warnings is: \[\].")
+
+ warning_classes = (UserWarning, FutureWarning)
+ with pytest.raises(pytest.fail.Exception) as excinfo:
+ with pytest.warns(warning_classes) as warninfo:
+ warnings.warn("runtime", RuntimeWarning)
+ warnings.warn("import", ImportWarning)
+
+ message_template = ("DID NOT WARN. No warnings of type {0} was emitted. "
+ "The list of emitted warnings is: {1}.")
+ excinfo.match(re.escape(message_template.format(warning_classes,
+ [each.message for each in warninfo])))
+
+ def test_record(self):
+ with pytest.warns(UserWarning) as record:
+ warnings.warn("user", UserWarning)
+
+ assert len(record) == 1
+ assert str(record[0].message) == "user"
+
+ def test_record_only(self):
+ with pytest.warns(None) as record:
+ warnings.warn("user", UserWarning)
+ warnings.warn("runtime", RuntimeWarning)
+
+ assert len(record) == 2
+ assert str(record[0].message) == "user"
+ assert str(record[1].message) == "runtime"
+
+ def test_record_by_subclass(self):
+ with pytest.warns(Warning) as record:
+ warnings.warn("user", UserWarning)
+ warnings.warn("runtime", RuntimeWarning)
+
+ assert len(record) == 2
+ assert str(record[0].message) == "user"
+ assert str(record[1].message) == "runtime"
+
+ class MyUserWarning(UserWarning):
+ pass
+
+ class MyRuntimeWarning(RuntimeWarning):
+ pass
+
+ with pytest.warns((UserWarning, RuntimeWarning)) as record:
+ warnings.warn("user", MyUserWarning)
+ warnings.warn("runtime", MyRuntimeWarning)
+
+ assert len(record) == 2
+ assert str(record[0].message) == "user"
+ assert str(record[1].message) == "runtime"
+
+ def test_double_test(self, testdir):
+ """If a test is run again, the warning should still be raised"""
+ testdir.makepyfile('''
+ import pytest
+ import warnings
+
+ @pytest.mark.parametrize('run', [1, 2])
+ def test(run):
+ with pytest.warns(RuntimeWarning):
+ warnings.warn("runtime", RuntimeWarning)
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['*2 passed in*'])
+
+ def test_match_regex(self):
+ with pytest.warns(UserWarning, match=r'must be \d+$'):
+ warnings.warn("value must be 42", UserWarning)
+
+ with pytest.raises(pytest.fail.Exception):
+ with pytest.warns(UserWarning, match=r'must be \d+$'):
+ warnings.warn("this is not here", UserWarning)
+
+ with pytest.raises(pytest.fail.Exception):
+ with pytest.warns(FutureWarning, match=r'must be \d+$'):
+ warnings.warn("value must be 42", UserWarning)
+
+ def test_one_from_multiple_warns(self):
+ with pytest.warns(UserWarning, match=r'aaa'):
+ warnings.warn("cccccccccc", UserWarning)
+ warnings.warn("bbbbbbbbbb", UserWarning)
+ warnings.warn("aaaaaaaaaa", UserWarning)
+
+ def test_none_of_multiple_warns(self):
+ with pytest.raises(pytest.fail.Exception):
+ with pytest.warns(UserWarning, match=r'aaa'):
+ warnings.warn("bbbbbbbbbb", UserWarning)
+ warnings.warn("cccccccccc", UserWarning)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_resultlog.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_resultlog.py
new file mode 100644
index 00000000000..b7dd2687cdf
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_resultlog.py
@@ -0,0 +1,228 @@
+from __future__ import absolute_import, division, print_function
+import os
+
+import _pytest._code
+import py
+import pytest
+from _pytest.main import Node, Item, FSCollector
+from _pytest.resultlog import generic_path, ResultLog, \
+ pytest_configure, pytest_unconfigure
+
+
+def test_generic_path(testdir):
+ from _pytest.main import Session
+ config = testdir.parseconfig()
+ session = Session(config)
+ p1 = Node('a', config=config, session=session)
+ # assert p1.fspath is None
+ p2 = Node('B', parent=p1)
+ p3 = Node('()', parent=p2)
+ item = Item('c', parent=p3)
+
+ res = generic_path(item)
+ assert res == 'a.B().c'
+
+ p0 = FSCollector('proj/test', config=config, session=session)
+ p1 = FSCollector('proj/test/a', parent=p0)
+ p2 = Node('B', parent=p1)
+ p3 = Node('()', parent=p2)
+ p4 = Node('c', parent=p3)
+ item = Item('[1]', parent=p4)
+
+ res = generic_path(item)
+ assert res == 'test/a:B().c[1]'
+
+
+def test_write_log_entry():
+ reslog = ResultLog(None, None)
+ reslog.logfile = py.io.TextIO()
+ reslog.write_log_entry('name', '.', '')
+ entry = reslog.logfile.getvalue()
+ assert entry[-1] == '\n'
+ entry_lines = entry.splitlines()
+ assert len(entry_lines) == 1
+ assert entry_lines[0] == '. name'
+
+ reslog.logfile = py.io.TextIO()
+ reslog.write_log_entry('name', 's', 'Skipped')
+ entry = reslog.logfile.getvalue()
+ assert entry[-1] == '\n'
+ entry_lines = entry.splitlines()
+ assert len(entry_lines) == 2
+ assert entry_lines[0] == 's name'
+ assert entry_lines[1] == ' Skipped'
+
+ reslog.logfile = py.io.TextIO()
+ reslog.write_log_entry('name', 's', 'Skipped\n')
+ entry = reslog.logfile.getvalue()
+ assert entry[-1] == '\n'
+ entry_lines = entry.splitlines()
+ assert len(entry_lines) == 2
+ assert entry_lines[0] == 's name'
+ assert entry_lines[1] == ' Skipped'
+
+ reslog.logfile = py.io.TextIO()
+ longrepr = ' tb1\n tb 2\nE tb3\nSome Error'
+ reslog.write_log_entry('name', 'F', longrepr)
+ entry = reslog.logfile.getvalue()
+ assert entry[-1] == '\n'
+ entry_lines = entry.splitlines()
+ assert len(entry_lines) == 5
+ assert entry_lines[0] == 'F name'
+ assert entry_lines[1:] == [' ' + line for line in longrepr.splitlines()]
+
+
+class TestWithFunctionIntegration(object):
+ # XXX (hpk) i think that the resultlog plugin should
+ # provide a Parser object so that one can remain
+ # ignorant regarding formatting details.
+ def getresultlog(self, testdir, arg):
+ resultlog = testdir.tmpdir.join("resultlog")
+ testdir.plugins.append("resultlog")
+ args = ["--resultlog=%s" % resultlog] + [arg]
+ testdir.runpytest(*args)
+ return [x for x in resultlog.readlines(cr=0) if x]
+
+ def test_collection_report(self, testdir):
+ ok = testdir.makepyfile(test_collection_ok="")
+ fail = testdir.makepyfile(test_collection_fail="XXX")
+ lines = self.getresultlog(testdir, ok)
+ assert not lines
+
+ lines = self.getresultlog(testdir, fail)
+ assert lines
+ assert lines[0].startswith("F ")
+ assert lines[0].endswith("test_collection_fail.py"), lines[0]
+ for x in lines[1:]:
+ assert x.startswith(" ")
+ assert "XXX" in "".join(lines[1:])
+
+ def test_log_test_outcomes(self, testdir):
+ mod = testdir.makepyfile(test_mod="""
+ import pytest
+ def test_pass(): pass
+ def test_skip(): pytest.skip("hello")
+ def test_fail(): raise ValueError("FAIL")
+
+ @pytest.mark.xfail
+ def test_xfail(): raise ValueError("XFAIL")
+ @pytest.mark.xfail
+ def test_xpass(): pass
+
+ """)
+ lines = self.getresultlog(testdir, mod)
+ assert len(lines) >= 3
+ assert lines[0].startswith(". ")
+ assert lines[0].endswith("test_pass")
+ assert lines[1].startswith("s "), lines[1]
+ assert lines[1].endswith("test_skip")
+ assert lines[2].find("hello") != -1
+
+ assert lines[3].startswith("F ")
+ assert lines[3].endswith("test_fail")
+ tb = "".join(lines[4:8])
+ assert tb.find('raise ValueError("FAIL")') != -1
+
+ assert lines[8].startswith('x ')
+ tb = "".join(lines[8:14])
+ assert tb.find('raise ValueError("XFAIL")') != -1
+
+ assert lines[14].startswith('X ')
+ assert len(lines) == 15
+
+ @pytest.mark.parametrize("style", ("native", "long", "short"))
+ def test_internal_exception(self, style):
+ # they are produced for example by a teardown failing
+ # at the end of the run or a failing hook invocation
+ try:
+ raise ValueError
+ except ValueError:
+ excinfo = _pytest._code.ExceptionInfo()
+ reslog = ResultLog(None, py.io.TextIO())
+ reslog.pytest_internalerror(excinfo.getrepr(style=style))
+ entry = reslog.logfile.getvalue()
+ entry_lines = entry.splitlines()
+
+ assert entry_lines[0].startswith('! ')
+ if style != "native":
+ assert os.path.basename(__file__)[:-9] in entry_lines[0] # .pyc/class
+ assert entry_lines[-1][0] == ' '
+ assert 'ValueError' in entry
+
+
+def test_generic(testdir, LineMatcher):
+ testdir.plugins.append("resultlog")
+ testdir.makepyfile("""
+ import pytest
+ def test_pass():
+ pass
+ def test_fail():
+ assert 0
+ def test_skip():
+ pytest.skip("")
+ @pytest.mark.xfail
+ def test_xfail():
+ assert 0
+ @pytest.mark.xfail(run=False)
+ def test_xfail_norun():
+ assert 0
+ """)
+ testdir.runpytest("--resultlog=result.log")
+ lines = testdir.tmpdir.join("result.log").readlines(cr=0)
+ LineMatcher(lines).fnmatch_lines([
+ ". *:test_pass",
+ "F *:test_fail",
+ "s *:test_skip",
+ "x *:test_xfail",
+ "x *:test_xfail_norun",
+ ])
+
+
+def test_makedir_for_resultlog(testdir, LineMatcher):
+ """--resultlog should automatically create directories for the log file"""
+ testdir.plugins.append("resultlog")
+ testdir.makepyfile("""
+ import pytest
+ def test_pass():
+ pass
+ """)
+ testdir.runpytest("--resultlog=path/to/result.log")
+ lines = testdir.tmpdir.join("path/to/result.log").readlines(cr=0)
+ LineMatcher(lines).fnmatch_lines([
+ ". *:test_pass",
+ ])
+
+
+def test_no_resultlog_on_slaves(testdir):
+ config = testdir.parseconfig("-p", "resultlog", "--resultlog=resultlog")
+
+ assert not hasattr(config, '_resultlog')
+ pytest_configure(config)
+ assert hasattr(config, '_resultlog')
+ pytest_unconfigure(config)
+ assert not hasattr(config, '_resultlog')
+
+ config.slaveinput = {}
+ pytest_configure(config)
+ assert not hasattr(config, '_resultlog')
+ pytest_unconfigure(config)
+ assert not hasattr(config, '_resultlog')
+
+
+def test_failure_issue380(testdir):
+ testdir.makeconftest("""
+ import pytest
+ class MyCollector(pytest.File):
+ def collect(self):
+ raise ValueError()
+ def repr_failure(self, excinfo):
+ return "somestring"
+ def pytest_collect_file(path, parent):
+ return MyCollector(parent=parent, fspath=path)
+ """)
+ testdir.makepyfile("""
+ def test_func():
+ pass
+ """)
+ result = testdir.runpytest("--resultlog=log")
+ assert result.ret == 2
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_runner.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_runner.py
new file mode 100644
index 00000000000..c8e2a6463a0
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_runner.py
@@ -0,0 +1,813 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+
+import _pytest._code
+import os
+import py
+import pytest
+import sys
+from _pytest import runner, main, outcomes
+
+
+class TestSetupState(object):
+ def test_setup(self, testdir):
+ ss = runner.SetupState()
+ item = testdir.getitem("def test_func(): pass")
+ values = [1]
+ ss.prepare(item)
+ ss.addfinalizer(values.pop, colitem=item)
+ assert values
+ ss._pop_and_teardown()
+ assert not values
+
+ def test_teardown_exact_stack_empty(self, testdir):
+ item = testdir.getitem("def test_func(): pass")
+ ss = runner.SetupState()
+ ss.teardown_exact(item, None)
+ ss.teardown_exact(item, None)
+ ss.teardown_exact(item, None)
+
+ def test_setup_fails_and_failure_is_cached(self, testdir):
+ item = testdir.getitem("""
+ def setup_module(mod):
+ raise ValueError(42)
+ def test_func(): pass
+ """) # noqa
+ ss = runner.SetupState()
+ pytest.raises(ValueError, lambda: ss.prepare(item))
+ pytest.raises(ValueError, lambda: ss.prepare(item))
+
+ def test_teardown_multiple_one_fails(self, testdir):
+ r = []
+
+ def fin1():
+ r.append('fin1')
+
+ def fin2():
+ raise Exception('oops')
+
+ def fin3():
+ r.append('fin3')
+
+ item = testdir.getitem("def test_func(): pass")
+ ss = runner.SetupState()
+ ss.addfinalizer(fin1, item)
+ ss.addfinalizer(fin2, item)
+ ss.addfinalizer(fin3, item)
+ with pytest.raises(Exception) as err:
+ ss._callfinalizers(item)
+ assert err.value.args == ('oops',)
+ assert r == ['fin3', 'fin1']
+
+ def test_teardown_multiple_fail(self, testdir):
+ # Ensure the first exception is the one which is re-raised.
+ # Ideally both would be reported however.
+ def fin1():
+ raise Exception('oops1')
+
+ def fin2():
+ raise Exception('oops2')
+
+ item = testdir.getitem("def test_func(): pass")
+ ss = runner.SetupState()
+ ss.addfinalizer(fin1, item)
+ ss.addfinalizer(fin2, item)
+ with pytest.raises(Exception) as err:
+ ss._callfinalizers(item)
+ assert err.value.args == ('oops2',)
+
+
+class BaseFunctionalTests(object):
+ def test_passfunction(self, testdir):
+ reports = testdir.runitem("""
+ def test_func():
+ pass
+ """)
+ rep = reports[1]
+ assert rep.passed
+ assert not rep.failed
+ assert rep.outcome == "passed"
+ assert not rep.longrepr
+
+ def test_failfunction(self, testdir):
+ reports = testdir.runitem("""
+ def test_func():
+ assert 0
+ """)
+ rep = reports[1]
+ assert not rep.passed
+ assert not rep.skipped
+ assert rep.failed
+ assert rep.when == "call"
+ assert rep.outcome == "failed"
+ # assert isinstance(rep.longrepr, ReprExceptionInfo)
+
+ def test_skipfunction(self, testdir):
+ reports = testdir.runitem("""
+ import pytest
+ def test_func():
+ pytest.skip("hello")
+ """)
+ rep = reports[1]
+ assert not rep.failed
+ assert not rep.passed
+ assert rep.skipped
+ assert rep.outcome == "skipped"
+ # assert rep.skipped.when == "call"
+ # assert rep.skipped.when == "call"
+ # assert rep.skipped == "%sreason == "hello"
+ # assert rep.skipped.location.lineno == 3
+ # assert rep.skipped.location.path
+ # assert not rep.skipped.failurerepr
+
+ def test_skip_in_setup_function(self, testdir):
+ reports = testdir.runitem("""
+ import pytest
+ def setup_function(func):
+ pytest.skip("hello")
+ def test_func():
+ pass
+ """)
+ print(reports)
+ rep = reports[0]
+ assert not rep.failed
+ assert not rep.passed
+ assert rep.skipped
+ # assert rep.skipped.reason == "hello"
+ # assert rep.skipped.location.lineno == 3
+ # assert rep.skipped.location.lineno == 3
+ assert len(reports) == 2
+ assert reports[1].passed # teardown
+
+ def test_failure_in_setup_function(self, testdir):
+ reports = testdir.runitem("""
+ import pytest
+ def setup_function(func):
+ raise ValueError(42)
+ def test_func():
+ pass
+ """)
+ rep = reports[0]
+ assert not rep.skipped
+ assert not rep.passed
+ assert rep.failed
+ assert rep.when == "setup"
+ assert len(reports) == 2
+
+ def test_failure_in_teardown_function(self, testdir):
+ reports = testdir.runitem("""
+ import pytest
+ def teardown_function(func):
+ raise ValueError(42)
+ def test_func():
+ pass
+ """)
+ print(reports)
+ assert len(reports) == 3
+ rep = reports[2]
+ assert not rep.skipped
+ assert not rep.passed
+ assert rep.failed
+ assert rep.when == "teardown"
+ # assert rep.longrepr.reprcrash.lineno == 3
+ # assert rep.longrepr.reprtraceback.reprentries
+
+ def test_custom_failure_repr(self, testdir):
+ testdir.makepyfile(conftest="""
+ import pytest
+ class Function(pytest.Function):
+ def repr_failure(self, excinfo):
+ return "hello"
+ """)
+ reports = testdir.runitem("""
+ import pytest
+ def test_func():
+ assert 0
+ """)
+ rep = reports[1]
+ assert not rep.skipped
+ assert not rep.passed
+ assert rep.failed
+ # assert rep.outcome.when == "call"
+ # assert rep.failed.where.lineno == 3
+ # assert rep.failed.where.path.basename == "test_func.py"
+ # assert rep.failed.failurerepr == "hello"
+
+ def test_teardown_final_returncode(self, testdir):
+ rec = testdir.inline_runsource("""
+ def test_func():
+ pass
+ def teardown_function(func):
+ raise ValueError(42)
+ """)
+ assert rec.ret == 1
+
+ def test_exact_teardown_issue90(self, testdir):
+ rec = testdir.inline_runsource("""
+ import pytest
+
+ class TestClass(object):
+ def test_method(self):
+ pass
+ def teardown_class(cls):
+ raise Exception()
+
+ def test_func():
+ import sys
+ # on python2 exc_info is keept till a function exits
+ # so we would end up calling test functions while
+ # sys.exc_info would return the indexerror
+ # from guessing the lastitem
+ excinfo = sys.exc_info()
+ import traceback
+ assert excinfo[0] is None, \
+ traceback.format_exception(*excinfo)
+ def teardown_function(func):
+ raise ValueError(42)
+ """)
+ reps = rec.getreports("pytest_runtest_logreport")
+ print(reps)
+ for i in range(2):
+ assert reps[i].nodeid.endswith("test_method")
+ assert reps[i].passed
+ assert reps[2].when == "teardown"
+ assert reps[2].failed
+ assert len(reps) == 6
+ for i in range(3, 5):
+ assert reps[i].nodeid.endswith("test_func")
+ assert reps[i].passed
+ assert reps[5].when == "teardown"
+ assert reps[5].nodeid.endswith("test_func")
+ assert reps[5].failed
+
+ def test_exact_teardown_issue1206(self, testdir):
+ """issue shadowing error with wrong number of arguments on teardown_method."""
+ rec = testdir.inline_runsource("""
+ import pytest
+
+ class TestClass(object):
+ def teardown_method(self, x, y, z):
+ pass
+
+ def test_method(self):
+ assert True
+ """)
+ reps = rec.getreports("pytest_runtest_logreport")
+ print(reps)
+ assert len(reps) == 3
+ #
+ assert reps[0].nodeid.endswith("test_method")
+ assert reps[0].passed
+ assert reps[0].when == 'setup'
+ #
+ assert reps[1].nodeid.endswith("test_method")
+ assert reps[1].passed
+ assert reps[1].when == 'call'
+ #
+ assert reps[2].nodeid.endswith("test_method")
+ assert reps[2].failed
+ assert reps[2].when == "teardown"
+ assert reps[2].longrepr.reprcrash.message in (
+ # python3 error
+ "TypeError: teardown_method() missing 2 required positional arguments: 'y' and 'z'",
+ # python2 error
+ 'TypeError: teardown_method() takes exactly 4 arguments (2 given)'
+ )
+
+ def test_failure_in_setup_function_ignores_custom_repr(self, testdir):
+ testdir.makepyfile(conftest="""
+ import pytest
+ class Function(pytest.Function):
+ def repr_failure(self, excinfo):
+ assert 0
+ """)
+ reports = testdir.runitem("""
+ def setup_function(func):
+ raise ValueError(42)
+ def test_func():
+ pass
+ """)
+ assert len(reports) == 2
+ rep = reports[0]
+ print(rep)
+ assert not rep.skipped
+ assert not rep.passed
+ assert rep.failed
+ # assert rep.outcome.when == "setup"
+ # assert rep.outcome.where.lineno == 3
+ # assert rep.outcome.where.path.basename == "test_func.py"
+ # assert instanace(rep.failed.failurerepr, PythonFailureRepr)
+
+ def test_systemexit_does_not_bail_out(self, testdir):
+ try:
+ reports = testdir.runitem("""
+ def test_func():
+ raise SystemExit(42)
+ """)
+ except SystemExit:
+ pytest.fail("runner did not catch SystemExit")
+ rep = reports[1]
+ assert rep.failed
+ assert rep.when == "call"
+
+ def test_exit_propagates(self, testdir):
+ try:
+ testdir.runitem("""
+ import pytest
+ def test_func():
+ raise pytest.exit.Exception()
+ """)
+ except pytest.exit.Exception:
+ pass
+ else:
+ pytest.fail("did not raise")
+
+
+class TestExecutionNonForked(BaseFunctionalTests):
+ def getrunner(self):
+ def f(item):
+ return runner.runtestprotocol(item, log=False)
+ return f
+
+ def test_keyboardinterrupt_propagates(self, testdir):
+ try:
+ testdir.runitem("""
+ def test_func():
+ raise KeyboardInterrupt("fake")
+ """)
+ except KeyboardInterrupt:
+ pass
+ else:
+ pytest.fail("did not raise")
+
+
+class TestExecutionForked(BaseFunctionalTests):
+ pytestmark = pytest.mark.skipif("not hasattr(os, 'fork')")
+
+ def getrunner(self):
+ # XXX re-arrange this test to live in pytest-xdist
+ boxed = pytest.importorskip("xdist.boxed")
+ return boxed.forked_run_report
+
+ def test_suicide(self, testdir):
+ reports = testdir.runitem("""
+ def test_func():
+ import os
+ os.kill(os.getpid(), 15)
+ """)
+ rep = reports[0]
+ assert rep.failed
+ assert rep.when == "???"
+
+
+class TestSessionReports(object):
+ def test_collect_result(self, testdir):
+ col = testdir.getmodulecol("""
+ def test_func1():
+ pass
+ class TestClass(object):
+ pass
+ """)
+ rep = runner.collect_one_node(col)
+ assert not rep.failed
+ assert not rep.skipped
+ assert rep.passed
+ locinfo = rep.location
+ assert locinfo[0] == col.fspath.basename
+ assert not locinfo[1]
+ assert locinfo[2] == col.fspath.basename
+ res = rep.result
+ assert len(res) == 2
+ assert res[0].name == "test_func1"
+ assert res[1].name == "TestClass"
+
+
+reporttypes = [
+ runner.BaseReport,
+ runner.TestReport,
+ runner.TeardownErrorReport,
+ runner.CollectReport,
+]
+
+
+@pytest.mark.parametrize('reporttype', reporttypes, ids=[x.__name__ for x in reporttypes])
+def test_report_extra_parameters(reporttype):
+ if hasattr(py.std.inspect, 'signature'):
+ args = list(py.std.inspect.signature(reporttype.__init__).parameters.keys())[1:]
+ else:
+ args = py.std.inspect.getargspec(reporttype.__init__)[0][1:]
+ basekw = dict.fromkeys(args, [])
+ report = reporttype(newthing=1, **basekw)
+ assert report.newthing == 1
+
+
+def test_callinfo():
+ ci = runner.CallInfo(lambda: 0, '123')
+ assert ci.when == "123"
+ assert ci.result == 0
+ assert "result" in repr(ci)
+ ci = runner.CallInfo(lambda: 0 / 0, '123')
+ assert ci.when == "123"
+ assert not hasattr(ci, 'result')
+ assert ci.excinfo
+ assert "exc" in repr(ci)
+
+# design question: do we want general hooks in python files?
+# then something like the following functional tests makes sense
+
+
+@pytest.mark.xfail
+def test_runtest_in_module_ordering(testdir):
+ p1 = testdir.makepyfile("""
+ import pytest
+ def pytest_runtest_setup(item): # runs after class-level!
+ item.function.mylist.append("module")
+ class TestClass(object):
+ def pytest_runtest_setup(self, item):
+ assert not hasattr(item.function, 'mylist')
+ item.function.mylist = ['class']
+ @pytest.fixture
+ def mylist(self, request):
+ return request.function.mylist
+ def pytest_runtest_call(self, item, __multicall__):
+ try:
+ __multicall__.execute()
+ except ValueError:
+ pass
+ def test_hello1(self, mylist):
+ assert mylist == ['class', 'module'], mylist
+ raise ValueError()
+ def test_hello2(self, mylist):
+ assert mylist == ['class', 'module'], mylist
+ def pytest_runtest_teardown(item):
+ del item.function.mylist
+ """)
+ result = testdir.runpytest(p1)
+ result.stdout.fnmatch_lines([
+ "*2 passed*"
+ ])
+
+
+def test_outcomeexception_exceptionattributes():
+ outcome = outcomes.OutcomeException('test')
+ assert outcome.args[0] == outcome.msg
+
+
+def test_outcomeexception_passes_except_Exception():
+ with pytest.raises(outcomes.OutcomeException):
+ try:
+ raise outcomes.OutcomeException('test')
+ except Exception:
+ pass
+
+
+def test_pytest_exit():
+ try:
+ pytest.exit("hello")
+ except pytest.exit.Exception:
+ excinfo = _pytest._code.ExceptionInfo()
+ assert excinfo.errisinstance(KeyboardInterrupt)
+
+
+def test_pytest_fail():
+ try:
+ pytest.fail("hello")
+ except pytest.fail.Exception:
+ excinfo = _pytest._code.ExceptionInfo()
+ s = excinfo.exconly(tryshort=True)
+ assert s.startswith("Failed")
+
+
+def test_pytest_exit_msg(testdir):
+ testdir.makeconftest("""
+ import pytest
+
+ def pytest_configure(config):
+ pytest.exit('oh noes')
+ """)
+ result = testdir.runpytest()
+ result.stderr.fnmatch_lines([
+ "Exit: oh noes",
+ ])
+
+
+def test_pytest_fail_notrace(testdir):
+ testdir.makepyfile("""
+ import pytest
+ def test_hello():
+ pytest.fail("hello", pytrace=False)
+ def teardown_function(function):
+ pytest.fail("world", pytrace=False)
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "world",
+ "hello",
+ ])
+ assert 'def teardown_function' not in result.stdout.str()
+
+
+@pytest.mark.parametrize('str_prefix', ['u', ''])
+def test_pytest_fail_notrace_non_ascii(testdir, str_prefix):
+ """Fix pytest.fail with pytrace=False with non-ascii characters (#1178).
+
+ This tests with native and unicode strings containing non-ascii chars.
+ """
+ testdir.makepyfile(u"""
+ # coding: utf-8
+ import pytest
+
+ def test_hello():
+ pytest.fail(%s'oh oh: ☺', pytrace=False)
+ """ % str_prefix)
+ result = testdir.runpytest()
+ if sys.version_info[0] >= 3:
+ result.stdout.fnmatch_lines(['*test_hello*', "oh oh: ☺"])
+ else:
+ result.stdout.fnmatch_lines(['*test_hello*', "oh oh: *"])
+ assert 'def test_hello' not in result.stdout.str()
+
+
+def test_pytest_no_tests_collected_exit_status(testdir):
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('*collected 0 items*')
+ assert result.ret == main.EXIT_NOTESTSCOLLECTED
+
+ testdir.makepyfile(test_foo="""
+ def test_foo():
+ assert 1
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines('*collected 1 item*')
+ result.stdout.fnmatch_lines('*1 passed*')
+ assert result.ret == main.EXIT_OK
+
+ result = testdir.runpytest('-k nonmatch')
+ result.stdout.fnmatch_lines('*collected 1 item*')
+ result.stdout.fnmatch_lines('*1 deselected*')
+ assert result.ret == main.EXIT_NOTESTSCOLLECTED
+
+
+def test_exception_printing_skip():
+ try:
+ pytest.skip("hello")
+ except pytest.skip.Exception:
+ excinfo = _pytest._code.ExceptionInfo()
+ s = excinfo.exconly(tryshort=True)
+ assert s.startswith("Skipped")
+
+
+def test_importorskip(monkeypatch):
+ importorskip = pytest.importorskip
+
+ def f():
+ importorskip("asdlkj")
+
+ try:
+ sys = importorskip("sys") # noqa
+ assert sys == py.std.sys
+ # path = pytest.importorskip("os.path")
+ # assert path == py.std.os.path
+ excinfo = pytest.raises(pytest.skip.Exception, f)
+ path = py.path.local(excinfo.getrepr().reprcrash.path)
+ # check that importorskip reports the actual call
+ # in this test the test_runner.py file
+ assert path.purebasename == "test_runner"
+ pytest.raises(SyntaxError, "pytest.importorskip('x y z')")
+ pytest.raises(SyntaxError, "pytest.importorskip('x=y')")
+ mod = py.std.types.ModuleType("hello123")
+ mod.__version__ = "1.3"
+ monkeypatch.setitem(sys.modules, "hello123", mod)
+ pytest.raises(pytest.skip.Exception, """
+ pytest.importorskip("hello123", minversion="1.3.1")
+ """)
+ mod2 = pytest.importorskip("hello123", minversion="1.3")
+ assert mod2 == mod
+ except pytest.skip.Exception:
+ print(_pytest._code.ExceptionInfo())
+ pytest.fail("spurious skip")
+
+
+def test_importorskip_imports_last_module_part():
+ ospath = pytest.importorskip("os.path")
+ assert os.path == ospath
+
+
+def test_importorskip_dev_module(monkeypatch):
+ try:
+ mod = py.std.types.ModuleType("mockmodule")
+ mod.__version__ = '0.13.0.dev-43290'
+ monkeypatch.setitem(sys.modules, 'mockmodule', mod)
+ mod2 = pytest.importorskip('mockmodule', minversion='0.12.0')
+ assert mod2 == mod
+ pytest.raises(pytest.skip.Exception, """
+ pytest.importorskip('mockmodule1', minversion='0.14.0')""")
+ except pytest.skip.Exception:
+ print(_pytest._code.ExceptionInfo())
+ pytest.fail("spurious skip")
+
+
+def test_importorskip_module_level(testdir):
+ """importorskip must be able to skip entire modules when used at module level"""
+ testdir.makepyfile('''
+ import pytest
+ foobarbaz = pytest.importorskip("foobarbaz")
+
+ def test_foo():
+ pass
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['*collected 0 items / 1 skipped*'])
+
+
+def test_pytest_cmdline_main(testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ def test_hello():
+ assert 1
+ if __name__ == '__main__':
+ pytest.cmdline.main([__file__])
+ """)
+ import subprocess
+ popen = subprocess.Popen([sys.executable, str(p)], stdout=subprocess.PIPE)
+ popen.communicate()
+ ret = popen.wait()
+ assert ret == 0
+
+
+def test_unicode_in_longrepr(testdir):
+ testdir.makeconftest("""
+ # -*- coding: utf-8 -*-
+ import pytest
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_runtest_makereport():
+ outcome = yield
+ rep = outcome.get_result()
+ if rep.when == "call":
+ rep.longrepr = u'ä'
+ """)
+ testdir.makepyfile("""
+ def test_out():
+ assert 0
+ """)
+ result = testdir.runpytest()
+ assert result.ret == 1
+ assert "UnicodeEncodeError" not in result.stderr.str()
+
+
+def test_failure_in_setup(testdir):
+ testdir.makepyfile("""
+ def setup_module():
+ 0/0
+ def test_func():
+ pass
+ """)
+ result = testdir.runpytest("--tb=line")
+ assert "def setup_module" not in result.stdout.str()
+
+
+def test_makereport_getsource(testdir):
+ testdir.makepyfile("""
+ def test_foo():
+ if False: pass
+ else: assert False
+ """)
+ result = testdir.runpytest()
+ assert 'INTERNALERROR' not in result.stdout.str()
+ result.stdout.fnmatch_lines(['*else: assert False*'])
+
+
+def test_makereport_getsource_dynamic_code(testdir, monkeypatch):
+ """Test that exception in dynamically generated code doesn't break getting the source line."""
+ import inspect
+ original_findsource = inspect.findsource
+
+ def findsource(obj, *args, **kwargs):
+ # Can be triggered by dynamically created functions
+ if obj.__name__ == 'foo':
+ raise IndexError()
+ return original_findsource(obj, *args, **kwargs)
+
+ monkeypatch.setattr(inspect, 'findsource', findsource)
+
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def foo(missing):
+ pass
+
+ def test_fix(foo):
+ assert False
+ """)
+ result = testdir.runpytest('-vv')
+ assert 'INTERNALERROR' not in result.stdout.str()
+ result.stdout.fnmatch_lines(["*test_fix*", "*fixture*'missing'*not found*"])
+
+
+def test_store_except_info_on_eror():
+ """ Test that upon test failure, the exception info is stored on
+ sys.last_traceback and friends.
+ """
+ # Simulate item that raises a specific exception
+ class ItemThatRaises(object):
+ nodeid = 'item_that_raises'
+
+ def runtest(self):
+ raise IndexError('TEST')
+ try:
+ runner.pytest_runtest_call(ItemThatRaises())
+ except IndexError:
+ pass
+ # Check that exception info is stored on sys
+ assert sys.last_type is IndexError
+ assert sys.last_value.args[0] == 'TEST'
+ assert sys.last_traceback
+
+
+def test_current_test_env_var(testdir, monkeypatch):
+ pytest_current_test_vars = []
+ monkeypatch.setattr(sys, 'pytest_current_test_vars', pytest_current_test_vars, raising=False)
+ testdir.makepyfile('''
+ import pytest
+ import sys
+ import os
+
+ @pytest.fixture
+ def fix():
+ sys.pytest_current_test_vars.append(('setup', os.environ['PYTEST_CURRENT_TEST']))
+ yield
+ sys.pytest_current_test_vars.append(('teardown', os.environ['PYTEST_CURRENT_TEST']))
+
+ def test(fix):
+ sys.pytest_current_test_vars.append(('call', os.environ['PYTEST_CURRENT_TEST']))
+ ''')
+ result = testdir.runpytest_inprocess()
+ assert result.ret == 0
+ test_id = 'test_current_test_env_var.py::test'
+ assert pytest_current_test_vars == [
+ ('setup', test_id + ' (setup)'), ('call', test_id + ' (call)'), ('teardown', test_id + ' (teardown)')]
+ assert 'PYTEST_CURRENT_TEST' not in os.environ
+
+
+class TestReportContents(object):
+ """
+ Test user-level API of ``TestReport`` objects.
+ """
+
+ def getrunner(self):
+ return lambda item: runner.runtestprotocol(item, log=False)
+
+ def test_longreprtext_pass(self, testdir):
+ reports = testdir.runitem("""
+ def test_func():
+ pass
+ """)
+ rep = reports[1]
+ assert rep.longreprtext == ''
+
+ def test_longreprtext_failure(self, testdir):
+ reports = testdir.runitem("""
+ def test_func():
+ x = 1
+ assert x == 4
+ """)
+ rep = reports[1]
+ assert 'assert 1 == 4' in rep.longreprtext
+
+ def test_captured_text(self, testdir):
+ reports = testdir.runitem("""
+ import pytest
+ import sys
+
+ @pytest.fixture
+ def fix():
+ sys.stdout.write('setup: stdout\\n')
+ sys.stderr.write('setup: stderr\\n')
+ yield
+ sys.stdout.write('teardown: stdout\\n')
+ sys.stderr.write('teardown: stderr\\n')
+ assert 0
+
+ def test_func(fix):
+ sys.stdout.write('call: stdout\\n')
+ sys.stderr.write('call: stderr\\n')
+ assert 0
+ """)
+ setup, call, teardown = reports
+ assert setup.capstdout == 'setup: stdout\n'
+ assert call.capstdout == 'setup: stdout\ncall: stdout\n'
+ assert teardown.capstdout == 'setup: stdout\ncall: stdout\nteardown: stdout\n'
+
+ assert setup.capstderr == 'setup: stderr\n'
+ assert call.capstderr == 'setup: stderr\ncall: stderr\n'
+ assert teardown.capstderr == 'setup: stderr\ncall: stderr\nteardown: stderr\n'
+
+ def test_no_captured_text(self, testdir):
+ reports = testdir.runitem("""
+ def test_func():
+ pass
+ """)
+ rep = reports[1]
+ assert rep.capstdout == ''
+ assert rep.capstderr == ''
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_runner_xunit.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_runner_xunit.py
new file mode 100644
index 00000000000..fc931f86720
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_runner_xunit.py
@@ -0,0 +1,319 @@
+"""
+ test correct setup/teardowns at
+ module, class, and instance level
+"""
+from __future__ import absolute_import, division, print_function
+import pytest
+
+
+def test_module_and_function_setup(testdir):
+ reprec = testdir.inline_runsource("""
+ modlevel = []
+ def setup_module(module):
+ assert not modlevel
+ module.modlevel.append(42)
+
+ def teardown_module(module):
+ modlevel.pop()
+
+ def setup_function(function):
+ function.answer = 17
+
+ def teardown_function(function):
+ del function.answer
+
+ def test_modlevel():
+ assert modlevel[0] == 42
+ assert test_modlevel.answer == 17
+
+ class TestFromClass(object):
+ def test_module(self):
+ assert modlevel[0] == 42
+ assert not hasattr(test_modlevel, 'answer')
+ """)
+ rep = reprec.matchreport("test_modlevel")
+ assert rep.passed
+ rep = reprec.matchreport("test_module")
+ assert rep.passed
+
+
+def test_module_setup_failure_no_teardown(testdir):
+ reprec = testdir.inline_runsource("""
+ values = []
+ def setup_module(module):
+ values.append(1)
+ 0/0
+
+ def test_nothing():
+ pass
+
+ def teardown_module(module):
+ values.append(2)
+ """)
+ reprec.assertoutcome(failed=1)
+ calls = reprec.getcalls("pytest_runtest_setup")
+ assert calls[0].item.module.values == [1]
+
+
+def test_setup_function_failure_no_teardown(testdir):
+ reprec = testdir.inline_runsource("""
+ modlevel = []
+ def setup_function(function):
+ modlevel.append(1)
+ 0/0
+
+ def teardown_function(module):
+ modlevel.append(2)
+
+ def test_func():
+ pass
+ """)
+ calls = reprec.getcalls("pytest_runtest_setup")
+ assert calls[0].item.module.modlevel == [1]
+
+
+def test_class_setup(testdir):
+ reprec = testdir.inline_runsource("""
+ class TestSimpleClassSetup(object):
+ clslevel = []
+ def setup_class(cls):
+ cls.clslevel.append(23)
+
+ def teardown_class(cls):
+ cls.clslevel.pop()
+
+ def test_classlevel(self):
+ assert self.clslevel[0] == 23
+
+ class TestInheritedClassSetupStillWorks(TestSimpleClassSetup):
+ def test_classlevel_anothertime(self):
+ assert self.clslevel == [23]
+
+ def test_cleanup():
+ assert not TestSimpleClassSetup.clslevel
+ assert not TestInheritedClassSetupStillWorks.clslevel
+ """)
+ reprec.assertoutcome(passed=1 + 2 + 1)
+
+
+def test_class_setup_failure_no_teardown(testdir):
+ reprec = testdir.inline_runsource("""
+ class TestSimpleClassSetup(object):
+ clslevel = []
+ def setup_class(cls):
+ 0/0
+
+ def teardown_class(cls):
+ cls.clslevel.append(1)
+
+ def test_classlevel(self):
+ pass
+
+ def test_cleanup():
+ assert not TestSimpleClassSetup.clslevel
+ """)
+ reprec.assertoutcome(failed=1, passed=1)
+
+
+def test_method_setup(testdir):
+ reprec = testdir.inline_runsource("""
+ class TestSetupMethod(object):
+ def setup_method(self, meth):
+ self.methsetup = meth
+ def teardown_method(self, meth):
+ del self.methsetup
+
+ def test_some(self):
+ assert self.methsetup == self.test_some
+
+ def test_other(self):
+ assert self.methsetup == self.test_other
+ """)
+ reprec.assertoutcome(passed=2)
+
+
+def test_method_setup_failure_no_teardown(testdir):
+ reprec = testdir.inline_runsource("""
+ class TestMethodSetup(object):
+ clslevel = []
+ def setup_method(self, method):
+ self.clslevel.append(1)
+ 0/0
+
+ def teardown_method(self, method):
+ self.clslevel.append(2)
+
+ def test_method(self):
+ pass
+
+ def test_cleanup():
+ assert TestMethodSetup.clslevel == [1]
+ """)
+ reprec.assertoutcome(failed=1, passed=1)
+
+
+def test_method_generator_setup(testdir):
+ reprec = testdir.inline_runsource("""
+ class TestSetupTeardownOnInstance(object):
+ def setup_class(cls):
+ cls.classsetup = True
+
+ def setup_method(self, method):
+ self.methsetup = method
+
+ def test_generate(self):
+ assert self.classsetup
+ assert self.methsetup == self.test_generate
+ yield self.generated, 5
+ yield self.generated, 2
+
+ def generated(self, value):
+ assert self.classsetup
+ assert self.methsetup == self.test_generate
+ assert value == 5
+ """)
+ reprec.assertoutcome(passed=1, failed=1)
+
+
+def test_func_generator_setup(testdir):
+ reprec = testdir.inline_runsource("""
+ import sys
+
+ def setup_module(mod):
+ print ("setup_module")
+ mod.x = []
+
+ def setup_function(fun):
+ print ("setup_function")
+ x.append(1)
+
+ def teardown_function(fun):
+ print ("teardown_function")
+ x.pop()
+
+ def test_one():
+ assert x == [1]
+ def check():
+ print ("check")
+ sys.stderr.write("e\\n")
+ assert x == [1]
+ yield check
+ assert x == [1]
+ """)
+ rep = reprec.matchreport("test_one", names="pytest_runtest_logreport")
+ assert rep.passed
+
+
+def test_method_setup_uses_fresh_instances(testdir):
+ reprec = testdir.inline_runsource("""
+ class TestSelfState1(object):
+ memory = []
+ def test_hello(self):
+ self.memory.append(self)
+
+ def test_afterhello(self):
+ assert self != self.memory[0]
+ """)
+ reprec.assertoutcome(passed=2, failed=0)
+
+
+def test_setup_that_skips_calledagain(testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ def setup_module(mod):
+ pytest.skip("x")
+ def test_function1():
+ pass
+ def test_function2():
+ pass
+ """)
+ reprec = testdir.inline_run(p)
+ reprec.assertoutcome(skipped=2)
+
+
+def test_setup_fails_again_on_all_tests(testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ def setup_module(mod):
+ raise ValueError(42)
+ def test_function1():
+ pass
+ def test_function2():
+ pass
+ """)
+ reprec = testdir.inline_run(p)
+ reprec.assertoutcome(failed=2)
+
+
+def test_setup_funcarg_setup_when_outer_scope_fails(testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ def setup_module(mod):
+ raise ValueError(42)
+ @pytest.fixture
+ def hello(request):
+ raise ValueError("xyz43")
+ def test_function1(hello):
+ pass
+ def test_function2(hello):
+ pass
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*function1*",
+ "*ValueError*42*",
+ "*function2*",
+ "*ValueError*42*",
+ "*2 error*"
+ ])
+ assert "xyz43" not in result.stdout.str()
+
+
+@pytest.mark.parametrize('arg', ['', 'arg'])
+def test_setup_teardown_function_level_with_optional_argument(testdir, monkeypatch, arg):
+ """parameter to setup/teardown xunit-style functions parameter is now optional (#1728)."""
+ import sys
+ trace_setups_teardowns = []
+ monkeypatch.setattr(sys, 'trace_setups_teardowns', trace_setups_teardowns, raising=False)
+ p = testdir.makepyfile("""
+ import pytest
+ import sys
+
+ trace = sys.trace_setups_teardowns.append
+
+ def setup_module({arg}): trace('setup_module')
+ def teardown_module({arg}): trace('teardown_module')
+
+ def setup_function({arg}): trace('setup_function')
+ def teardown_function({arg}): trace('teardown_function')
+
+ def test_function_1(): pass
+ def test_function_2(): pass
+
+ class Test(object):
+ def setup_method(self, {arg}): trace('setup_method')
+ def teardown_method(self, {arg}): trace('teardown_method')
+
+ def test_method_1(self): pass
+ def test_method_2(self): pass
+ """.format(arg=arg))
+ result = testdir.inline_run(p)
+ result.assertoutcome(passed=4)
+
+ expected = [
+ 'setup_module',
+
+ 'setup_function',
+ 'teardown_function',
+ 'setup_function',
+ 'teardown_function',
+
+ 'setup_method',
+ 'teardown_method',
+
+ 'setup_method',
+ 'teardown_method',
+
+ 'teardown_module',
+ ]
+ assert trace_setups_teardowns == expected
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_session.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_session.py
new file mode 100644
index 00000000000..9ec13f523e6
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_session.py
@@ -0,0 +1,255 @@
+from __future__ import absolute_import, division, print_function
+import pytest
+
+from _pytest.main import EXIT_NOTESTSCOLLECTED
+
+
+class SessionTests(object):
+ def test_basic_testitem_events(self, testdir):
+ tfile = testdir.makepyfile("""
+ def test_one():
+ pass
+ def test_one_one():
+ assert 0
+ def test_other():
+ raise ValueError(23)
+ class TestClass(object):
+ def test_two(self, someargs):
+ pass
+ """)
+ reprec = testdir.inline_run(tfile)
+ passed, skipped, failed = reprec.listoutcomes()
+ assert len(skipped) == 0
+ assert len(passed) == 1
+ assert len(failed) == 3
+
+ def end(x):
+ return x.nodeid.split("::")[-1]
+
+ assert end(failed[0]) == "test_one_one"
+ assert end(failed[1]) == "test_other"
+ itemstarted = reprec.getcalls("pytest_itemcollected")
+ assert len(itemstarted) == 4
+ # XXX check for failing funcarg setup
+ # colreports = reprec.getcalls("pytest_collectreport")
+ # assert len(colreports) == 4
+ # assert colreports[1].report.failed
+
+ def test_nested_import_error(self, testdir):
+ tfile = testdir.makepyfile("""
+ import import_fails
+ def test_this():
+ assert import_fails.a == 1
+ """, import_fails="""
+ import does_not_work
+ a = 1
+ """)
+ reprec = testdir.inline_run(tfile)
+ values = reprec.getfailedcollections()
+ assert len(values) == 1
+ out = str(values[0].longrepr)
+ assert out.find('does_not_work') != -1
+
+ def test_raises_output(self, testdir):
+ reprec = testdir.inline_runsource("""
+ import pytest
+ def test_raises_doesnt():
+ pytest.raises(ValueError, int, "3")
+ """)
+ passed, skipped, failed = reprec.listoutcomes()
+ assert len(failed) == 1
+ out = failed[0].longrepr.reprcrash.message
+ if not out.find("DID NOT RAISE") != -1:
+ print(out)
+ pytest.fail("incorrect raises() output")
+
+ def test_generator_yields_None(self, testdir):
+ reprec = testdir.inline_runsource("""
+ def test_1():
+ yield None
+ """)
+ failures = reprec.getfailedcollections()
+ out = failures[0].longrepr.reprcrash.message
+ i = out.find('TypeError')
+ assert i != -1
+
+ def test_syntax_error_module(self, testdir):
+ reprec = testdir.inline_runsource("this is really not python")
+ values = reprec.getfailedcollections()
+ assert len(values) == 1
+ out = str(values[0].longrepr)
+ assert out.find(str('not python')) != -1
+
+ def test_exit_first_problem(self, testdir):
+ reprec = testdir.inline_runsource("""
+ def test_one(): assert 0
+ def test_two(): assert 0
+ """, '--exitfirst')
+ passed, skipped, failed = reprec.countoutcomes()
+ assert failed == 1
+ assert passed == skipped == 0
+
+ def test_maxfail(self, testdir):
+ reprec = testdir.inline_runsource("""
+ def test_one(): assert 0
+ def test_two(): assert 0
+ def test_three(): assert 0
+ """, '--maxfail=2')
+ passed, skipped, failed = reprec.countoutcomes()
+ assert failed == 2
+ assert passed == skipped == 0
+
+ def test_broken_repr(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ class BrokenRepr1(object):
+ foo=0
+ def __repr__(self):
+ raise Exception("Ha Ha fooled you, I'm a broken repr().")
+
+ class TestBrokenClass(object):
+ def test_explicit_bad_repr(self):
+ t = BrokenRepr1()
+ pytest.raises(Exception, 'repr(t)')
+
+ def test_implicit_bad_repr1(self):
+ t = BrokenRepr1()
+ assert t.foo == 1
+
+ """)
+ reprec = testdir.inline_run(p)
+ passed, skipped, failed = reprec.listoutcomes()
+ assert len(failed) == 1
+ out = failed[0].longrepr.reprcrash.message
+ assert out.find("""[Exception("Ha Ha fooled you, I'm a broken repr().") raised in repr()]""") != -1 # '
+
+ def test_skip_file_by_conftest(self, testdir):
+ testdir.makepyfile(conftest="""
+ import pytest
+ def pytest_collect_file():
+ pytest.skip("intentional")
+ """, test_file="""
+ def test_one(): pass
+ """)
+ try:
+ reprec = testdir.inline_run(testdir.tmpdir)
+ except pytest.skip.Exception:
+ pytest.fail("wrong skipped caught")
+ reports = reprec.getreports("pytest_collectreport")
+ assert len(reports) == 1
+ assert reports[0].skipped
+
+
+class TestNewSession(SessionTests):
+
+ def test_order_of_execution(self, testdir):
+ reprec = testdir.inline_runsource("""
+ values = []
+ def test_1():
+ values.append(1)
+ def test_2():
+ values.append(2)
+ def test_3():
+ assert values == [1,2]
+ class Testmygroup(object):
+ reslist = values
+ def test_1(self):
+ self.reslist.append(1)
+ def test_2(self):
+ self.reslist.append(2)
+ def test_3(self):
+ self.reslist.append(3)
+ def test_4(self):
+ assert self.reslist == [1,2,1,2,3]
+ """)
+ passed, skipped, failed = reprec.countoutcomes()
+ assert failed == skipped == 0
+ assert passed == 7
+ # also test listnames() here ...
+
+ def test_collect_only_with_various_situations(self, testdir):
+ p = testdir.makepyfile(
+ test_one="""
+ def test_one():
+ raise ValueError()
+
+ class TestX(object):
+ def test_method_one(self):
+ pass
+
+ class TestY(TestX):
+ pass
+ """,
+ test_three="xxxdsadsadsadsa",
+ __init__=""
+ )
+ reprec = testdir.inline_run('--collect-only', p.dirpath())
+
+ itemstarted = reprec.getcalls("pytest_itemcollected")
+ assert len(itemstarted) == 3
+ assert not reprec.getreports("pytest_runtest_logreport")
+ started = reprec.getcalls("pytest_collectstart")
+ finished = reprec.getreports("pytest_collectreport")
+ assert len(started) == len(finished)
+ assert len(started) == 7 # XXX extra TopCollector
+ colfail = [x for x in finished if x.failed]
+ assert len(colfail) == 1
+
+ def test_minus_x_import_error(self, testdir):
+ testdir.makepyfile(__init__="")
+ testdir.makepyfile(test_one="xxxx", test_two="yyyy")
+ reprec = testdir.inline_run("-x", testdir.tmpdir)
+ finished = reprec.getreports("pytest_collectreport")
+ colfail = [x for x in finished if x.failed]
+ assert len(colfail) == 1
+
+ def test_minus_x_overridden_by_maxfail(self, testdir):
+ testdir.makepyfile(__init__="")
+ testdir.makepyfile(test_one="xxxx", test_two="yyyy", test_third="zzz")
+ reprec = testdir.inline_run("-x", "--maxfail=2", testdir.tmpdir)
+ finished = reprec.getreports("pytest_collectreport")
+ colfail = [x for x in finished if x.failed]
+ assert len(colfail) == 2
+
+
+def test_plugin_specify(testdir):
+ pytest.raises(ImportError, """
+ testdir.parseconfig("-p", "nqweotexistent")
+ """)
+ # pytest.raises(ImportError,
+ # "config.do_configure(config)"
+ # )
+
+
+def test_plugin_already_exists(testdir):
+ config = testdir.parseconfig("-p", "terminal")
+ assert config.option.plugins == ['terminal']
+ config._do_configure()
+ config._ensure_unconfigure()
+
+
+def test_exclude(testdir):
+ hellodir = testdir.mkdir("hello")
+ hellodir.join("test_hello.py").write("x y syntaxerror")
+ hello2dir = testdir.mkdir("hello2")
+ hello2dir.join("test_hello2.py").write("x y syntaxerror")
+ testdir.makepyfile(test_ok="def test_pass(): pass")
+ result = testdir.runpytest("--ignore=hello", "--ignore=hello2")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+
+def test_sessionfinish_with_start(testdir):
+ testdir.makeconftest("""
+ import os
+ values = []
+ def pytest_sessionstart():
+ values.append(os.getcwd())
+ os.chdir("..")
+
+ def pytest_sessionfinish():
+ assert values[0] == os.getcwd()
+
+ """)
+ res = testdir.runpytest("--collect-only")
+ assert res.ret == EXIT_NOTESTSCOLLECTED
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_skipping.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_skipping.py
new file mode 100644
index 00000000000..978944876f3
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_skipping.py
@@ -0,0 +1,1067 @@
+from __future__ import absolute_import, division, print_function
+import pytest
+import sys
+
+from _pytest.skipping import MarkEvaluator, folded_skips, pytest_runtest_setup
+from _pytest.runner import runtestprotocol
+
+
+class TestEvaluator(object):
+ def test_no_marker(self, testdir):
+ item = testdir.getitem("def test_func(): pass")
+ evalskipif = MarkEvaluator(item, 'skipif')
+ assert not evalskipif
+ assert not evalskipif.istrue()
+
+ def test_marked_no_args(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.xyz
+ def test_func():
+ pass
+ """)
+ ev = MarkEvaluator(item, 'xyz')
+ assert ev
+ assert ev.istrue()
+ expl = ev.getexplanation()
+ assert expl == ""
+ assert not ev.get("run", False)
+
+ def test_marked_one_arg(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.xyz("hasattr(os, 'sep')")
+ def test_func():
+ pass
+ """)
+ ev = MarkEvaluator(item, 'xyz')
+ assert ev
+ assert ev.istrue()
+ expl = ev.getexplanation()
+ assert expl == "condition: hasattr(os, 'sep')"
+
+ @pytest.mark.skipif('sys.version_info[0] >= 3')
+ def test_marked_one_arg_unicode(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.xyz(u"hasattr(os, 'sep')")
+ def test_func():
+ pass
+ """)
+ ev = MarkEvaluator(item, 'xyz')
+ assert ev
+ assert ev.istrue()
+ expl = ev.getexplanation()
+ assert expl == "condition: hasattr(os, 'sep')"
+
+ def test_marked_one_arg_with_reason(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.xyz("hasattr(os, 'sep')", attr=2, reason="hello world")
+ def test_func():
+ pass
+ """)
+ ev = MarkEvaluator(item, 'xyz')
+ assert ev
+ assert ev.istrue()
+ expl = ev.getexplanation()
+ assert expl == "hello world"
+ assert ev.get("attr") == 2
+
+ def test_marked_one_arg_twice(self, testdir):
+ lines = [
+ '''@pytest.mark.skipif("not hasattr(os, 'murks')")''',
+ '''@pytest.mark.skipif("hasattr(os, 'murks')")'''
+ ]
+ for i in range(0, 2):
+ item = testdir.getitem("""
+ import pytest
+ %s
+ %s
+ def test_func():
+ pass
+ """ % (lines[i], lines[(i + 1) % 2]))
+ ev = MarkEvaluator(item, 'skipif')
+ assert ev
+ assert ev.istrue()
+ expl = ev.getexplanation()
+ assert expl == "condition: not hasattr(os, 'murks')"
+
+ def test_marked_one_arg_twice2(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.skipif("hasattr(os, 'murks')")
+ @pytest.mark.skipif("not hasattr(os, 'murks')")
+ def test_func():
+ pass
+ """)
+ ev = MarkEvaluator(item, 'skipif')
+ assert ev
+ assert ev.istrue()
+ expl = ev.getexplanation()
+ assert expl == "condition: not hasattr(os, 'murks')"
+
+ def test_marked_skip_with_not_string(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.skipif(False)
+ def test_func():
+ pass
+ """)
+ ev = MarkEvaluator(item, 'skipif')
+ exc = pytest.raises(pytest.fail.Exception, ev.istrue)
+ assert """Failed: you need to specify reason=STRING when using booleans as conditions.""" in exc.value.msg
+
+ def test_skipif_class(self, testdir):
+ item, = testdir.getitems("""
+ import pytest
+ class TestClass(object):
+ pytestmark = pytest.mark.skipif("config._hackxyz")
+ def test_func(self):
+ pass
+ """)
+ item.config._hackxyz = 3
+ ev = MarkEvaluator(item, 'skipif')
+ assert ev.istrue()
+ expl = ev.getexplanation()
+ assert expl == "condition: config._hackxyz"
+
+
+class TestXFail(object):
+
+ @pytest.mark.parametrize('strict', [True, False])
+ def test_xfail_simple(self, testdir, strict):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.xfail(strict=%s)
+ def test_func():
+ assert 0
+ """ % strict)
+ reports = runtestprotocol(item, log=False)
+ assert len(reports) == 3
+ callreport = reports[1]
+ assert callreport.skipped
+ assert callreport.wasxfail == ""
+
+ def test_xfail_xpassed(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.xfail(reason="this is an xfail")
+ def test_func():
+ assert 1
+ """)
+ reports = runtestprotocol(item, log=False)
+ assert len(reports) == 3
+ callreport = reports[1]
+ assert callreport.passed
+ assert callreport.wasxfail == "this is an xfail"
+
+ def test_xfail_xpassed_strict(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.xfail(strict=True, reason="nope")
+ def test_func():
+ assert 1
+ """)
+ reports = runtestprotocol(item, log=False)
+ assert len(reports) == 3
+ callreport = reports[1]
+ assert callreport.failed
+ assert callreport.longrepr == "[XPASS(strict)] nope"
+ assert not hasattr(callreport, "wasxfail")
+
+ def test_xfail_run_anyway(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.xfail
+ def test_func():
+ assert 0
+ def test_func2():
+ pytest.xfail("hello")
+ """)
+ result = testdir.runpytest("--runxfail")
+ result.stdout.fnmatch_lines([
+ "*def test_func():*",
+ "*assert 0*",
+ "*1 failed*1 pass*",
+ ])
+
+ def test_xfail_evalfalse_but_fails(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.xfail('False')
+ def test_func():
+ assert 0
+ """)
+ reports = runtestprotocol(item, log=False)
+ callreport = reports[1]
+ assert callreport.failed
+ assert not hasattr(callreport, "wasxfail")
+ assert 'xfail' in callreport.keywords
+
+ def test_xfail_not_report_default(self, testdir):
+ p = testdir.makepyfile(test_one="""
+ import pytest
+ @pytest.mark.xfail
+ def test_this():
+ assert 0
+ """)
+ testdir.runpytest(p, '-v')
+ # result.stdout.fnmatch_lines([
+ # "*HINT*use*-r*"
+ # ])
+
+ def test_xfail_not_run_xfail_reporting(self, testdir):
+ p = testdir.makepyfile(test_one="""
+ import pytest
+ @pytest.mark.xfail(run=False, reason="noway")
+ def test_this():
+ assert 0
+ @pytest.mark.xfail("True", run=False)
+ def test_this_true():
+ assert 0
+ @pytest.mark.xfail("False", run=False, reason="huh")
+ def test_this_false():
+ assert 1
+ """)
+ result = testdir.runpytest(p, '-rx', )
+ result.stdout.fnmatch_lines([
+ "*test_one*test_this*",
+ "*NOTRUN*noway",
+ "*test_one*test_this_true*",
+ "*NOTRUN*condition:*True*",
+ "*1 passed*",
+ ])
+
+ def test_xfail_not_run_no_setup_run(self, testdir):
+ p = testdir.makepyfile(test_one="""
+ import pytest
+ @pytest.mark.xfail(run=False, reason="hello")
+ def test_this():
+ assert 0
+ def setup_module(mod):
+ raise ValueError(42)
+ """)
+ result = testdir.runpytest(p, '-rx', )
+ result.stdout.fnmatch_lines([
+ "*test_one*test_this*",
+ "*NOTRUN*hello",
+ "*1 xfailed*",
+ ])
+
+ def test_xfail_xpass(self, testdir):
+ p = testdir.makepyfile(test_one="""
+ import pytest
+ @pytest.mark.xfail
+ def test_that():
+ assert 1
+ """)
+ result = testdir.runpytest(p, '-rX')
+ result.stdout.fnmatch_lines([
+ "*XPASS*test_that*",
+ "*1 xpassed*"
+ ])
+ assert result.ret == 0
+
+ def test_xfail_imperative(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ def test_this():
+ pytest.xfail("hello")
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*1 xfailed*",
+ ])
+ result = testdir.runpytest(p, "-rx")
+ result.stdout.fnmatch_lines([
+ "*XFAIL*test_this*",
+ "*reason:*hello*",
+ ])
+ result = testdir.runpytest(p, "--runxfail")
+ result.stdout.fnmatch_lines("*1 pass*")
+
+ def test_xfail_imperative_in_setup_function(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ def setup_function(function):
+ pytest.xfail("hello")
+
+ def test_this():
+ assert 0
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*1 xfailed*",
+ ])
+ result = testdir.runpytest(p, "-rx")
+ result.stdout.fnmatch_lines([
+ "*XFAIL*test_this*",
+ "*reason:*hello*",
+ ])
+ result = testdir.runpytest(p, "--runxfail")
+ result.stdout.fnmatch_lines("""
+ *def test_this*
+ *1 fail*
+ """)
+
+ def xtest_dynamic_xfail_set_during_setup(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ def setup_function(function):
+ pytest.mark.xfail(function)
+ def test_this():
+ assert 0
+ def test_that():
+ assert 1
+ """)
+ result = testdir.runpytest(p, '-rxX')
+ result.stdout.fnmatch_lines([
+ "*XFAIL*test_this*",
+ "*XPASS*test_that*",
+ ])
+
+ def test_dynamic_xfail_no_run(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ @pytest.fixture
+ def arg(request):
+ request.applymarker(pytest.mark.xfail(run=False))
+ def test_this(arg):
+ assert 0
+ """)
+ result = testdir.runpytest(p, '-rxX')
+ result.stdout.fnmatch_lines([
+ "*XFAIL*test_this*",
+ "*NOTRUN*",
+ ])
+
+ def test_dynamic_xfail_set_during_funcarg_setup(self, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ @pytest.fixture
+ def arg(request):
+ request.applymarker(pytest.mark.xfail)
+ def test_this2(arg):
+ assert 0
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*1 xfailed*",
+ ])
+
+ @pytest.mark.parametrize('expected, actual, matchline',
+ [('TypeError', 'TypeError', "*1 xfailed*"),
+ ('(AttributeError, TypeError)', 'TypeError', "*1 xfailed*"),
+ ('TypeError', 'IndexError', "*1 failed*"),
+ ('(AttributeError, TypeError)', 'IndexError', "*1 failed*"),
+ ])
+ def test_xfail_raises(self, expected, actual, matchline, testdir):
+ p = testdir.makepyfile("""
+ import pytest
+ @pytest.mark.xfail(raises=%s)
+ def test_raises():
+ raise %s()
+ """ % (expected, actual))
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ matchline,
+ ])
+
+ def test_strict_sanity(self, testdir):
+ """sanity check for xfail(strict=True): a failing test should behave
+ exactly like a normal xfail.
+ """
+ p = testdir.makepyfile("""
+ import pytest
+ @pytest.mark.xfail(reason='unsupported feature', strict=True)
+ def test_foo():
+ assert 0
+ """)
+ result = testdir.runpytest(p, '-rxX')
+ result.stdout.fnmatch_lines([
+ '*XFAIL*',
+ '*unsupported feature*',
+ ])
+ assert result.ret == 0
+
+ @pytest.mark.parametrize('strict', [True, False])
+ def test_strict_xfail(self, testdir, strict):
+ p = testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.xfail(reason='unsupported feature', strict=%s)
+ def test_foo():
+ with open('foo_executed', 'w'): pass # make sure test executes
+ """ % strict)
+ result = testdir.runpytest(p, '-rxX')
+ if strict:
+ result.stdout.fnmatch_lines([
+ '*test_foo*',
+ '*XPASS(strict)*unsupported feature*',
+ ])
+ else:
+ result.stdout.fnmatch_lines([
+ '*test_strict_xfail*',
+ 'XPASS test_strict_xfail.py::test_foo unsupported feature',
+ ])
+ assert result.ret == (1 if strict else 0)
+ assert testdir.tmpdir.join('foo_executed').isfile()
+
+ @pytest.mark.parametrize('strict', [True, False])
+ def test_strict_xfail_condition(self, testdir, strict):
+ p = testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.xfail(False, reason='unsupported feature', strict=%s)
+ def test_foo():
+ pass
+ """ % strict)
+ result = testdir.runpytest(p, '-rxX')
+ result.stdout.fnmatch_lines('*1 passed*')
+ assert result.ret == 0
+
+ @pytest.mark.parametrize('strict', [True, False])
+ def test_xfail_condition_keyword(self, testdir, strict):
+ p = testdir.makepyfile("""
+ import pytest
+
+ @pytest.mark.xfail(condition=False, reason='unsupported feature', strict=%s)
+ def test_foo():
+ pass
+ """ % strict)
+ result = testdir.runpytest(p, '-rxX')
+ result.stdout.fnmatch_lines('*1 passed*')
+ assert result.ret == 0
+
+ @pytest.mark.parametrize('strict_val', ['true', 'false'])
+ def test_strict_xfail_default_from_file(self, testdir, strict_val):
+ testdir.makeini('''
+ [pytest]
+ xfail_strict = %s
+ ''' % strict_val)
+ p = testdir.makepyfile("""
+ import pytest
+ @pytest.mark.xfail(reason='unsupported feature')
+ def test_foo():
+ pass
+ """)
+ result = testdir.runpytest(p, '-rxX')
+ strict = strict_val == 'true'
+ result.stdout.fnmatch_lines('*1 failed*' if strict else '*1 xpassed*')
+ assert result.ret == (1 if strict else 0)
+
+
+class TestXFailwithSetupTeardown(object):
+ def test_failing_setup_issue9(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def setup_function(func):
+ assert 0
+
+ @pytest.mark.xfail
+ def test_func():
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*1 xfail*",
+ ])
+
+ def test_failing_teardown_issue9(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def teardown_function(func):
+ assert 0
+
+ @pytest.mark.xfail
+ def test_func():
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*1 xfail*",
+ ])
+
+
+class TestSkip(object):
+ def test_skip_class(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skip
+ class TestSomething(object):
+ def test_foo(self):
+ pass
+ def test_bar(self):
+ pass
+
+ def test_baz():
+ pass
+ """)
+ rec = testdir.inline_run()
+ rec.assertoutcome(skipped=2, passed=1)
+
+ def test_skips_on_false_string(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skip('False')
+ def test_foo():
+ pass
+ """)
+ rec = testdir.inline_run()
+ rec.assertoutcome(skipped=1)
+
+ def test_arg_as_reason(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skip('testing stuff')
+ def test_bar():
+ pass
+ """)
+ result = testdir.runpytest('-rs')
+ result.stdout.fnmatch_lines([
+ "*testing stuff*",
+ "*1 skipped*",
+ ])
+
+ def test_skip_no_reason(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skip
+ def test_foo():
+ pass
+ """)
+ result = testdir.runpytest('-rs')
+ result.stdout.fnmatch_lines([
+ "*unconditional skip*",
+ "*1 skipped*",
+ ])
+
+ def test_skip_with_reason(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skip(reason="for lolz")
+ def test_bar():
+ pass
+ """)
+ result = testdir.runpytest('-rs')
+ result.stdout.fnmatch_lines([
+ "*for lolz*",
+ "*1 skipped*",
+ ])
+
+ def test_only_skips_marked_test(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skip
+ def test_foo():
+ pass
+ @pytest.mark.skip(reason="nothing in particular")
+ def test_bar():
+ pass
+ def test_baz():
+ assert True
+ """)
+ result = testdir.runpytest('-rs')
+ result.stdout.fnmatch_lines([
+ "*nothing in particular*",
+ "*1 passed*2 skipped*",
+ ])
+
+ def test_strict_and_skip(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skip
+ def test_hello():
+ pass
+ """)
+ result = testdir.runpytest("-rs")
+ result.stdout.fnmatch_lines([
+ "*unconditional skip*",
+ "*1 skipped*",
+ ])
+
+
+class TestSkipif(object):
+ def test_skipif_conditional(self, testdir):
+ item = testdir.getitem("""
+ import pytest
+ @pytest.mark.skipif("hasattr(os, 'sep')")
+ def test_func():
+ pass
+ """) # noqa
+ x = pytest.raises(pytest.skip.Exception, lambda:
+ pytest_runtest_setup(item))
+ assert x.value.msg == "condition: hasattr(os, 'sep')"
+
+ @pytest.mark.parametrize('params', [
+ '"hasattr(sys, \'platform\')"',
+ 'True, reason="invalid platform"',
+ ])
+ def test_skipif_reporting(self, testdir, params):
+ p = testdir.makepyfile(test_foo="""
+ import pytest
+ @pytest.mark.skipif(%(params)s)
+ def test_that():
+ assert 0
+ """ % dict(params=params))
+ result = testdir.runpytest(p, '-s', '-rs')
+ result.stdout.fnmatch_lines([
+ "*SKIP*1*test_foo.py*platform*",
+ "*1 skipped*"
+ ])
+ assert result.ret == 0
+
+ @pytest.mark.parametrize('marker, msg1, msg2', [
+ ('skipif', 'SKIP', 'skipped'),
+ ('xfail', 'XPASS', 'xpassed'),
+ ])
+ def test_skipif_reporting_multiple(self, testdir, marker, msg1, msg2):
+ testdir.makepyfile(test_foo="""
+ import pytest
+ @pytest.mark.{marker}(False, reason='first_condition')
+ @pytest.mark.{marker}(True, reason='second_condition')
+ def test_foobar():
+ assert 1
+ """.format(marker=marker))
+ result = testdir.runpytest('-s', '-rsxX')
+ result.stdout.fnmatch_lines([
+ "*{msg1}*test_foo.py*second_condition*".format(msg1=msg1),
+ "*1 {msg2}*".format(msg2=msg2),
+ ])
+ assert result.ret == 0
+
+
+def test_skip_not_report_default(testdir):
+ p = testdir.makepyfile(test_one="""
+ import pytest
+ def test_this():
+ pytest.skip("hello")
+ """)
+ result = testdir.runpytest(p, '-v')
+ result.stdout.fnmatch_lines([
+ # "*HINT*use*-r*",
+ "*1 skipped*",
+ ])
+
+
+def test_skipif_class(testdir):
+ p = testdir.makepyfile("""
+ import pytest
+
+ class TestClass(object):
+ pytestmark = pytest.mark.skipif("True")
+ def test_that(self):
+ assert 0
+ def test_though(self):
+ assert 0
+ """)
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines([
+ "*2 skipped*"
+ ])
+
+
+def test_skip_reasons_folding():
+ path = "xyz"
+ lineno = 3
+ message = "justso"
+ longrepr = (path, lineno, message)
+
+ class X(object):
+ pass
+ ev1 = X()
+ ev1.when = "execute"
+ ev1.skipped = True
+ ev1.longrepr = longrepr
+
+ ev2 = X()
+ ev2.when = "execute"
+ ev2.longrepr = longrepr
+ ev2.skipped = True
+
+ # ev3 might be a collection report
+ ev3 = X()
+ ev3.longrepr = longrepr
+ ev3.skipped = True
+
+ values = folded_skips([ev1, ev2, ev3])
+ assert len(values) == 1
+ num, fspath, lineno, reason = values[0]
+ assert num == 3
+ assert fspath == path
+ assert lineno == lineno
+ assert reason == message
+
+
+def test_skipped_reasons_functional(testdir):
+ testdir.makepyfile(
+ test_one="""
+ from conftest import doskip
+ def setup_function(func):
+ doskip()
+ def test_func():
+ pass
+ class TestClass(object):
+ def test_method(self):
+ doskip()
+ """,
+ conftest="""
+ import pytest
+ def doskip():
+ pytest.skip('test')
+ """
+ )
+ result = testdir.runpytest('-rs')
+ result.stdout.fnmatch_lines([
+ "*SKIP*2*conftest.py:4: test",
+ ])
+ assert result.ret == 0
+
+
+def test_skipped_folding(testdir):
+ testdir.makepyfile(
+ test_one="""
+ import pytest
+ pytestmark = pytest.mark.skip("Folding")
+ def setup_function(func):
+ pass
+ def test_func():
+ pass
+ class TestClass(object):
+ def test_method(self):
+ pass
+ """,
+ )
+ result = testdir.runpytest('-rs')
+ result.stdout.fnmatch_lines([
+ "*SKIP*2*test_one.py: Folding"
+ ])
+ assert result.ret == 0
+
+
+def test_reportchars(testdir):
+ testdir.makepyfile("""
+ import pytest
+ def test_1():
+ assert 0
+ @pytest.mark.xfail
+ def test_2():
+ assert 0
+ @pytest.mark.xfail
+ def test_3():
+ pass
+ def test_4():
+ pytest.skip("four")
+ """)
+ result = testdir.runpytest("-rfxXs")
+ result.stdout.fnmatch_lines([
+ "FAIL*test_1*",
+ "XFAIL*test_2*",
+ "XPASS*test_3*",
+ "SKIP*four*",
+ ])
+
+
+def test_reportchars_error(testdir):
+ testdir.makepyfile(
+ conftest="""
+ def pytest_runtest_teardown():
+ assert 0
+ """,
+ test_simple="""
+ def test_foo():
+ pass
+ """)
+ result = testdir.runpytest('-rE')
+ result.stdout.fnmatch_lines([
+ 'ERROR*test_foo*',
+ ])
+
+
+def test_reportchars_all(testdir):
+ testdir.makepyfile("""
+ import pytest
+ def test_1():
+ assert 0
+ @pytest.mark.xfail
+ def test_2():
+ assert 0
+ @pytest.mark.xfail
+ def test_3():
+ pass
+ def test_4():
+ pytest.skip("four")
+ """)
+ result = testdir.runpytest("-ra")
+ result.stdout.fnmatch_lines([
+ "FAIL*test_1*",
+ "SKIP*four*",
+ "XFAIL*test_2*",
+ "XPASS*test_3*",
+ ])
+
+
+def test_reportchars_all_error(testdir):
+ testdir.makepyfile(
+ conftest="""
+ def pytest_runtest_teardown():
+ assert 0
+ """,
+ test_simple="""
+ def test_foo():
+ pass
+ """)
+ result = testdir.runpytest('-ra')
+ result.stdout.fnmatch_lines([
+ 'ERROR*test_foo*',
+ ])
+
+
+@pytest.mark.xfail("hasattr(sys, 'pypy_version_info')")
+def test_errors_in_xfail_skip_expressions(testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skipif("asd")
+ def test_nameerror():
+ pass
+ @pytest.mark.xfail("syntax error")
+ def test_syntax():
+ pass
+
+ def test_func():
+ pass
+ """)
+ result = testdir.runpytest()
+ markline = " ^"
+ if sys.platform.startswith("java"):
+ # XXX report this to java
+ markline = "*" + markline[8:]
+ result.stdout.fnmatch_lines([
+ "*ERROR*test_nameerror*",
+ "*evaluating*skipif*expression*",
+ "*asd*",
+ "*ERROR*test_syntax*",
+ "*evaluating*xfail*expression*",
+ " syntax error",
+ markline,
+ "SyntaxError: invalid syntax",
+ "*1 pass*2 error*",
+ ])
+
+
+def test_xfail_skipif_with_globals(testdir):
+ testdir.makepyfile("""
+ import pytest
+ x = 3
+ @pytest.mark.skipif("x == 3")
+ def test_skip1():
+ pass
+ @pytest.mark.xfail("x == 3")
+ def test_boolean():
+ assert 0
+ """)
+ result = testdir.runpytest("-rsx")
+ result.stdout.fnmatch_lines([
+ "*SKIP*x == 3*",
+ "*XFAIL*test_boolean*",
+ "*x == 3*",
+ ])
+
+
+def test_direct_gives_error(testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skipif(True)
+ def test_skip1():
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*1 error*",
+ ])
+
+
+def test_default_markers(testdir):
+ result = testdir.runpytest("--markers")
+ result.stdout.fnmatch_lines([
+ "*skipif(*condition)*skip*",
+ "*xfail(*condition, reason=None, run=True, raises=None, strict=False)*expected failure*",
+ ])
+
+
+def test_xfail_test_setup_exception(testdir):
+ testdir.makeconftest("""
+ def pytest_runtest_setup():
+ 0 / 0
+ """)
+ p = testdir.makepyfile("""
+ import pytest
+ @pytest.mark.xfail
+ def test_func():
+ assert 0
+ """)
+ result = testdir.runpytest(p)
+ assert result.ret == 0
+ assert 'xfailed' in result.stdout.str()
+ assert 'xpassed' not in result.stdout.str()
+
+
+def test_imperativeskip_on_xfail_test(testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.xfail
+ def test_that_fails():
+ assert 0
+
+ @pytest.mark.skipif("True")
+ def test_hello():
+ pass
+ """)
+ testdir.makeconftest("""
+ import pytest
+ def pytest_runtest_setup(item):
+ pytest.skip("abc")
+ """)
+ result = testdir.runpytest("-rsxX")
+ result.stdout.fnmatch_lines_random("""
+ *SKIP*abc*
+ *SKIP*condition: True*
+ *2 skipped*
+ """)
+
+
+class TestBooleanCondition(object):
+ def test_skipif(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skipif(True, reason="True123")
+ def test_func1():
+ pass
+ @pytest.mark.skipif(False, reason="True123")
+ def test_func2():
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ *1 passed*1 skipped*
+ """)
+
+ def test_skipif_noreason(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.skipif(True)
+ def test_func():
+ pass
+ """)
+ result = testdir.runpytest("-rs")
+ result.stdout.fnmatch_lines("""
+ *1 error*
+ """)
+
+ def test_xfail(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.xfail(True, reason="True123")
+ def test_func():
+ assert 0
+ """)
+ result = testdir.runpytest("-rxs")
+ result.stdout.fnmatch_lines("""
+ *XFAIL*
+ *True123*
+ *1 xfail*
+ """)
+
+
+def test_xfail_item(testdir):
+ # Ensure pytest.xfail works with non-Python Item
+ testdir.makeconftest("""
+ import pytest
+
+ class MyItem(pytest.Item):
+ nodeid = 'foo'
+ def runtest(self):
+ pytest.xfail("Expected Failure")
+
+ def pytest_collect_file(path, parent):
+ return MyItem("foo", parent)
+ """)
+ result = testdir.inline_run()
+ passed, skipped, failed = result.listoutcomes()
+ assert not failed
+ xfailed = [r for r in skipped if hasattr(r, 'wasxfail')]
+ assert xfailed
+
+
+def test_module_level_skip_error(testdir):
+ """
+ Verify that using pytest.skip at module level causes a collection error
+ """
+ testdir.makepyfile("""
+ import pytest
+ @pytest.skip
+ def test_func():
+ assert True
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(
+ "*Using pytest.skip outside of a test is not allowed*"
+ )
+
+
+def test_module_level_skip_with_allow_module_level(testdir):
+ """
+ Verify that using pytest.skip(allow_module_level=True) is allowed
+ """
+ testdir.makepyfile("""
+ import pytest
+ pytest.skip("skip_module_level", allow_module_level=True)
+
+ def test_func():
+ assert 0
+ """)
+ result = testdir.runpytest("-rxs")
+ result.stdout.fnmatch_lines(
+ "*SKIP*skip_module_level"
+ )
+
+
+def test_invalid_skip_keyword_parameter(testdir):
+ """
+ Verify that using pytest.skip() with unknown parameter raises an error
+ """
+ testdir.makepyfile("""
+ import pytest
+ pytest.skip("skip_module_level", unknown=1)
+
+ def test_func():
+ assert 0
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(
+ "*TypeError:*['unknown']*"
+ )
+
+
+def test_mark_xfail_item(testdir):
+ # Ensure pytest.mark.xfail works with non-Python Item
+ testdir.makeconftest("""
+ import pytest
+
+ class MyItem(pytest.Item):
+ nodeid = 'foo'
+ def setup(self):
+ marker = pytest.mark.xfail(True, reason="Expected failure")
+ self.add_marker(marker)
+ def runtest(self):
+ assert False
+
+ def pytest_collect_file(path, parent):
+ return MyItem("foo", parent)
+ """)
+ result = testdir.inline_run()
+ passed, skipped, failed = result.listoutcomes()
+ assert not failed
+ xfailed = [r for r in skipped if hasattr(r, 'wasxfail')]
+ assert xfailed
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_terminal.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_terminal.py
new file mode 100644
index 00000000000..97c2f71fb66
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_terminal.py
@@ -0,0 +1,1039 @@
+"""
+terminal reporting of the full testing process.
+"""
+from __future__ import absolute_import, division, print_function
+import collections
+import sys
+
+import pluggy
+import _pytest._code
+import py
+import pytest
+from _pytest.main import EXIT_NOTESTSCOLLECTED
+from _pytest.terminal import TerminalReporter, repr_pythonversion, getreportopt
+from _pytest.terminal import build_summary_stats_line, _plugin_nameversions
+
+
+DistInfo = collections.namedtuple('DistInfo', ['project_name', 'version'])
+
+
+class Option(object):
+ def __init__(self, verbose=False, fulltrace=False):
+ self.verbose = verbose
+ self.fulltrace = fulltrace
+
+ @property
+ def args(self):
+ values = []
+ if self.verbose:
+ values.append('-v')
+ if self.fulltrace:
+ values.append('--fulltrace')
+ return values
+
+
+def pytest_generate_tests(metafunc):
+ if "option" in metafunc.fixturenames:
+ metafunc.addcall(id="default",
+ funcargs={'option': Option(verbose=False)})
+ metafunc.addcall(id="verbose",
+ funcargs={'option': Option(verbose=True)})
+ metafunc.addcall(id="quiet",
+ funcargs={'option': Option(verbose=-1)})
+ metafunc.addcall(id="fulltrace",
+ funcargs={'option': Option(fulltrace=True)})
+
+
+@pytest.mark.parametrize('input,expected', [
+ ([DistInfo(project_name='test', version=1)], ['test-1']),
+ ([DistInfo(project_name='pytest-test', version=1)], ['test-1']),
+ ([
+ DistInfo(project_name='test', version=1),
+ DistInfo(project_name='test', version=1)
+ ], ['test-1']),
+], ids=['normal', 'prefix-strip', 'deduplicate'])
+def test_plugin_nameversion(input, expected):
+ pluginlist = [(None, x) for x in input]
+ result = _plugin_nameversions(pluginlist)
+ assert result == expected
+
+
+class TestTerminal(object):
+ def test_pass_skip_fail(self, testdir, option):
+ testdir.makepyfile("""
+ import pytest
+ def test_ok():
+ pass
+ def test_skip():
+ pytest.skip("xx")
+ def test_func():
+ assert 0
+ """)
+ result = testdir.runpytest(*option.args)
+ if option.verbose:
+ result.stdout.fnmatch_lines([
+ "*test_pass_skip_fail.py::test_ok PASS*",
+ "*test_pass_skip_fail.py::test_skip SKIP*",
+ "*test_pass_skip_fail.py::test_func FAIL*",
+ ])
+ else:
+ result.stdout.fnmatch_lines([
+ "*test_pass_skip_fail.py .sF*"
+ ])
+ result.stdout.fnmatch_lines([
+ " def test_func():",
+ "> assert 0",
+ "E assert 0",
+ ])
+
+ def test_internalerror(self, testdir, linecomp):
+ modcol = testdir.getmodulecol("def test_one(): pass")
+ rep = TerminalReporter(modcol.config, file=linecomp.stringio)
+ excinfo = pytest.raises(ValueError, "raise ValueError('hello')")
+ rep.pytest_internalerror(excinfo.getrepr())
+ linecomp.assert_contains_lines([
+ "INTERNALERROR> *ValueError*hello*"
+ ])
+
+ def test_writeline(self, testdir, linecomp):
+ modcol = testdir.getmodulecol("def test_one(): pass")
+ rep = TerminalReporter(modcol.config, file=linecomp.stringio)
+ rep.write_fspath_result(modcol.nodeid, ".")
+ rep.write_line("hello world")
+ lines = linecomp.stringio.getvalue().split('\n')
+ assert not lines[0]
+ assert lines[1].endswith(modcol.name + " .")
+ assert lines[2] == "hello world"
+
+ def test_show_runtest_logstart(self, testdir, linecomp):
+ item = testdir.getitem("def test_func(): pass")
+ tr = TerminalReporter(item.config, file=linecomp.stringio)
+ item.config.pluginmanager.register(tr)
+ location = item.reportinfo()
+ tr.config.hook.pytest_runtest_logstart(nodeid=item.nodeid,
+ location=location, fspath=str(item.fspath))
+ linecomp.assert_contains_lines([
+ "*test_show_runtest_logstart.py*"
+ ])
+
+ def test_runtest_location_shown_before_test_starts(self, testdir):
+ testdir.makepyfile("""
+ def test_1():
+ import time
+ time.sleep(20)
+ """)
+ child = testdir.spawn_pytest("")
+ child.expect(".*test_runtest_location.*py")
+ child.sendeof()
+ child.kill(15)
+
+ def test_itemreport_subclasses_show_subclassed_file(self, testdir):
+ testdir.makepyfile(test_p1="""
+ class BaseTests(object):
+ def test_p1(self):
+ pass
+ class TestClass(BaseTests):
+ pass
+ """)
+ p2 = testdir.makepyfile(test_p2="""
+ from test_p1 import BaseTests
+ class TestMore(BaseTests):
+ pass
+ """)
+ result = testdir.runpytest(p2)
+ result.stdout.fnmatch_lines([
+ "*test_p2.py .*",
+ "*1 passed*",
+ ])
+ result = testdir.runpytest("-v", p2)
+ result.stdout.fnmatch_lines([
+ "*test_p2.py::TestMore::test_p1* <- *test_p1.py*PASSED*",
+ ])
+
+ def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir):
+ a = testdir.mkpydir("a123")
+ a.join("test_hello123.py").write(_pytest._code.Source("""
+ class TestClass(object):
+ def test_method(self):
+ pass
+ """))
+ result = testdir.runpytest("-v")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*a123/test_hello123.py*PASS*",
+ ])
+ assert " <- " not in result.stdout.str()
+
+ def test_keyboard_interrupt(self, testdir, option):
+ testdir.makepyfile("""
+ def test_foobar():
+ assert 0
+ def test_spamegg():
+ import py; pytest.skip('skip me please!')
+ def test_interrupt_me():
+ raise KeyboardInterrupt # simulating the user
+ """)
+
+ result = testdir.runpytest(*option.args, no_reraise_ctrlc=True)
+ result.stdout.fnmatch_lines([
+ " def test_foobar():",
+ "> assert 0",
+ "E assert 0",
+ "*_keyboard_interrupt.py:6: KeyboardInterrupt*",
+ ])
+ if option.fulltrace:
+ result.stdout.fnmatch_lines([
+ "*raise KeyboardInterrupt # simulating the user*",
+ ])
+ else:
+ result.stdout.fnmatch_lines([
+ "to show a full traceback on KeyboardInterrupt use --fulltrace"
+ ])
+ result.stdout.fnmatch_lines(['*KeyboardInterrupt*'])
+
+ def test_keyboard_in_sessionstart(self, testdir):
+ testdir.makeconftest("""
+ def pytest_sessionstart():
+ raise KeyboardInterrupt
+ """)
+ testdir.makepyfile("""
+ def test_foobar():
+ pass
+ """)
+
+ result = testdir.runpytest(no_reraise_ctrlc=True)
+ assert result.ret == 2
+ result.stdout.fnmatch_lines(['*KeyboardInterrupt*'])
+
+ def test_collect_single_item(self, testdir):
+ """Use singular 'item' when reporting a single test item"""
+ testdir.makepyfile("""
+ def test_foobar():
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(['collected 1 item'])
+
+ def test_rewrite(self, testdir, monkeypatch):
+ config = testdir.parseconfig()
+ f = py.io.TextIO()
+ monkeypatch.setattr(f, 'isatty', lambda *args: True)
+ tr = TerminalReporter(config, f)
+ tr._tw.fullwidth = 10
+ tr.write('hello')
+ tr.rewrite('hey', erase=True)
+ assert f.getvalue() == 'hello' + '\r' + 'hey' + (6 * ' ')
+
+
+class TestCollectonly(object):
+ def test_collectonly_basic(self, testdir):
+ testdir.makepyfile("""
+ def test_func():
+ pass
+ """)
+ result = testdir.runpytest("--collect-only",)
+ result.stdout.fnmatch_lines([
+ "<Module 'test_collectonly_basic.py'>",
+ " <Function 'test_func'>",
+ ])
+
+ def test_collectonly_skipped_module(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ pytest.skip("hello")
+ """)
+ result = testdir.runpytest("--collect-only", "-rs")
+ result.stdout.fnmatch_lines([
+ "*ERROR collecting*",
+ ])
+
+ def test_collectonly_failed_module(self, testdir):
+ testdir.makepyfile("""raise ValueError(0)""")
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*raise ValueError*",
+ "*1 error*",
+ ])
+
+ def test_collectonly_fatal(self, testdir):
+ testdir.makeconftest("""
+ def pytest_collectstart(collector):
+ assert 0, "urgs"
+ """)
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines([
+ "*INTERNAL*args*"
+ ])
+ assert result.ret == 3
+
+ def test_collectonly_simple(self, testdir):
+ p = testdir.makepyfile("""
+ def test_func1():
+ pass
+ class TestClass(object):
+ def test_method(self):
+ pass
+ """)
+ result = testdir.runpytest("--collect-only", p)
+ # assert stderr.startswith("inserting into sys.path")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*<Module '*.py'>",
+ "* <Function 'test_func1'*>",
+ "* <Class 'TestClass'>",
+ # "* <Instance '()'>",
+ "* <Function 'test_method'*>",
+ ])
+
+ def test_collectonly_error(self, testdir):
+ p = testdir.makepyfile("import Errlkjqweqwe")
+ result = testdir.runpytest("--collect-only", p)
+ assert result.ret == 2
+ result.stdout.fnmatch_lines(_pytest._code.Source("""
+ *ERROR*
+ *ImportError*
+ *No module named *Errlk*
+ *1 error*
+ """).strip())
+
+ def test_collectonly_missing_path(self, testdir):
+ """this checks issue 115,
+ failure in parseargs will cause session
+ not to have the items attribute
+ """
+ result = testdir.runpytest("--collect-only", "uhm_missing_path")
+ assert result.ret == 4
+ result.stderr.fnmatch_lines([
+ '*ERROR: file not found*',
+ ])
+
+ def test_collectonly_quiet(self, testdir):
+ testdir.makepyfile("def test_foo(): pass")
+ result = testdir.runpytest("--collect-only", "-q")
+ result.stdout.fnmatch_lines([
+ '*test_foo*',
+ ])
+
+ def test_collectonly_more_quiet(self, testdir):
+ testdir.makepyfile(test_fun="def test_foo(): pass")
+ result = testdir.runpytest("--collect-only", "-qq")
+ result.stdout.fnmatch_lines([
+ '*test_fun.py: 1*',
+ ])
+
+
+def test_repr_python_version(monkeypatch):
+ try:
+ monkeypatch.setattr(sys, 'version_info', (2, 5, 1, 'final', 0))
+ assert repr_pythonversion() == "2.5.1-final-0"
+ py.std.sys.version_info = x = (2, 3)
+ assert repr_pythonversion() == str(x)
+ finally:
+ monkeypatch.undo() # do this early as pytest can get confused
+
+
+class TestFixtureReporting(object):
+ def test_setup_fixture_error(self, testdir):
+ testdir.makepyfile("""
+ def setup_function(function):
+ print ("setup func")
+ assert 0
+ def test_nada():
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*ERROR at setup of test_nada*",
+ "*setup_function(function):*",
+ "*setup func*",
+ "*assert 0*",
+ "*1 error*",
+ ])
+ assert result.ret != 0
+
+ def test_teardown_fixture_error(self, testdir):
+ testdir.makepyfile("""
+ def test_nada():
+ pass
+ def teardown_function(function):
+ print ("teardown func")
+ assert 0
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*ERROR at teardown*",
+ "*teardown_function(function):*",
+ "*assert 0*",
+ "*Captured stdout*",
+ "*teardown func*",
+ "*1 passed*1 error*",
+ ])
+
+ def test_teardown_fixture_error_and_test_failure(self, testdir):
+ testdir.makepyfile("""
+ def test_fail():
+ assert 0, "failingfunc"
+
+ def teardown_function(function):
+ print ("teardown func")
+ assert False
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*ERROR at teardown of test_fail*",
+ "*teardown_function(function):*",
+ "*assert False*",
+ "*Captured stdout*",
+ "*teardown func*",
+
+ "*test_fail*",
+ "*def test_fail():",
+ "*failingfunc*",
+ "*1 failed*1 error*",
+ ])
+
+ def test_setup_teardown_output_and_test_failure(self, testdir):
+ """ Test for issue #442 """
+ testdir.makepyfile("""
+ def setup_function(function):
+ print ("setup func")
+
+ def test_fail():
+ assert 0, "failingfunc"
+
+ def teardown_function(function):
+ print ("teardown func")
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*test_fail*",
+ "*def test_fail():",
+ "*failingfunc*",
+ "*Captured stdout setup*",
+ "*setup func*",
+ "*Captured stdout teardown*",
+ "*teardown func*",
+
+ "*1 failed*",
+ ])
+
+
+class TestTerminalFunctional(object):
+ def test_deselected(self, testdir):
+ testpath = testdir.makepyfile("""
+ def test_one():
+ pass
+ def test_two():
+ pass
+ def test_three():
+ pass
+ """
+ )
+ result = testdir.runpytest("-k", "test_two:", testpath)
+ result.stdout.fnmatch_lines([
+ "*test_deselected.py ..*",
+ "=* 1 test*deselected *=",
+ ])
+ assert result.ret == 0
+
+ def test_no_skip_summary_if_failure(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+ def test_ok():
+ pass
+ def test_fail():
+ assert 0
+ def test_skip():
+ pytest.skip("dontshow")
+ """)
+ result = testdir.runpytest()
+ assert result.stdout.str().find("skip test summary") == -1
+ assert result.ret == 1
+
+ def test_passes(self, testdir):
+ p1 = testdir.makepyfile("""
+ def test_passes():
+ pass
+ class TestClass(object):
+ def test_method(self):
+ pass
+ """)
+ old = p1.dirpath().chdir()
+ try:
+ result = testdir.runpytest()
+ finally:
+ old.chdir()
+ result.stdout.fnmatch_lines([
+ "test_passes.py ..*",
+ "* 2 pass*",
+ ])
+ assert result.ret == 0
+
+ def test_header_trailer_info(self, testdir):
+ testdir.makepyfile("""
+ def test_passes():
+ pass
+ """)
+ result = testdir.runpytest()
+ verinfo = ".".join(map(str, py.std.sys.version_info[:3]))
+ result.stdout.fnmatch_lines([
+ "*===== test session starts ====*",
+ "platform %s -- Python %s*pytest-%s*py-%s*pluggy-%s" % (
+ py.std.sys.platform, verinfo,
+ pytest.__version__, py.__version__, pluggy.__version__),
+ "*test_header_trailer_info.py .*",
+ "=* 1 passed*in *.[0-9][0-9] seconds *=",
+ ])
+ if pytest.config.pluginmanager.list_plugin_distinfo():
+ result.stdout.fnmatch_lines([
+ "plugins: *",
+ ])
+
+ def test_showlocals(self, testdir):
+ p1 = testdir.makepyfile("""
+ def test_showlocals():
+ x = 3
+ y = "x" * 5000
+ assert 0
+ """)
+ result = testdir.runpytest(p1, '-l')
+ result.stdout.fnmatch_lines([
+ # "_ _ * Locals *",
+ "x* = 3",
+ "y* = 'xxxxxx*"
+ ])
+
+ def test_verbose_reporting(self, testdir, pytestconfig):
+ p1 = testdir.makepyfile("""
+ import pytest
+ def test_fail():
+ raise ValueError()
+ def test_pass():
+ pass
+ class TestClass(object):
+ def test_skip(self):
+ pytest.skip("hello")
+ def test_gen():
+ def check(x):
+ assert x == 1
+ yield check, 0
+ """)
+ result = testdir.runpytest(p1, '-v')
+ result.stdout.fnmatch_lines([
+ "*test_verbose_reporting.py::test_fail *FAIL*",
+ "*test_verbose_reporting.py::test_pass *PASS*",
+ "*test_verbose_reporting.py::TestClass::test_skip *SKIP*",
+ "*test_verbose_reporting.py::test_gen*0* *FAIL*",
+ ])
+ assert result.ret == 1
+
+ if not pytestconfig.pluginmanager.get_plugin("xdist"):
+ pytest.skip("xdist plugin not installed")
+
+ result = testdir.runpytest(p1, '-v', '-n 1')
+ result.stdout.fnmatch_lines([
+ "*FAIL*test_verbose_reporting.py::test_fail*",
+ ])
+ assert result.ret == 1
+
+ def test_quiet_reporting(self, testdir):
+ p1 = testdir.makepyfile("def test_pass(): pass")
+ result = testdir.runpytest(p1, '-q')
+ s = result.stdout.str()
+ assert 'test session starts' not in s
+ assert p1.basename not in s
+ assert "===" not in s
+ assert "passed" in s
+
+ def test_more_quiet_reporting(self, testdir):
+ p1 = testdir.makepyfile("def test_pass(): pass")
+ result = testdir.runpytest(p1, '-qq')
+ s = result.stdout.str()
+ assert 'test session starts' not in s
+ assert p1.basename not in s
+ assert "===" not in s
+ assert "passed" not in s
+
+ def test_report_collectionfinish_hook(self, testdir):
+ testdir.makeconftest("""
+ def pytest_report_collectionfinish(config, startdir, items):
+ return ['hello from hook: {0} items'.format(len(items))]
+ """)
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize('i', range(3))
+ def test(i):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "collected 3 items",
+ "hello from hook: 3 items",
+ ])
+
+
+def test_fail_extra_reporting(testdir):
+ testdir.makepyfile("def test_this(): assert 0")
+ result = testdir.runpytest()
+ assert 'short test summary' not in result.stdout.str()
+ result = testdir.runpytest('-rf')
+ result.stdout.fnmatch_lines([
+ "*test summary*",
+ "FAIL*test_fail_extra_reporting*",
+ ])
+
+
+def test_fail_reporting_on_pass(testdir):
+ testdir.makepyfile("def test_this(): assert 1")
+ result = testdir.runpytest('-rf')
+ assert 'short test summary' not in result.stdout.str()
+
+
+def test_pass_extra_reporting(testdir):
+ testdir.makepyfile("def test_this(): assert 1")
+ result = testdir.runpytest()
+ assert 'short test summary' not in result.stdout.str()
+ result = testdir.runpytest('-rp')
+ result.stdout.fnmatch_lines([
+ "*test summary*",
+ "PASS*test_pass_extra_reporting*",
+ ])
+
+
+def test_pass_reporting_on_fail(testdir):
+ testdir.makepyfile("def test_this(): assert 0")
+ result = testdir.runpytest('-rp')
+ assert 'short test summary' not in result.stdout.str()
+
+
+def test_pass_output_reporting(testdir):
+ testdir.makepyfile("""
+ def test_pass_output():
+ print("Four score and seven years ago...")
+ """)
+ result = testdir.runpytest()
+ assert 'Four score and seven years ago...' not in result.stdout.str()
+ result = testdir.runpytest('-rP')
+ result.stdout.fnmatch_lines([
+ "Four score and seven years ago...",
+ ])
+
+
+def test_color_yes(testdir):
+ testdir.makepyfile("def test_this(): assert 1")
+ result = testdir.runpytest('--color=yes')
+ assert 'test session starts' in result.stdout.str()
+ assert '\x1b[1m' in result.stdout.str()
+
+
+def test_color_no(testdir):
+ testdir.makepyfile("def test_this(): assert 1")
+ result = testdir.runpytest('--color=no')
+ assert 'test session starts' in result.stdout.str()
+ assert '\x1b[1m' not in result.stdout.str()
+
+
+@pytest.mark.parametrize('verbose', [True, False])
+def test_color_yes_collection_on_non_atty(testdir, verbose):
+ """skip collect progress report when working on non-terminals.
+ #1397
+ """
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize('i', range(10))
+ def test_this(i):
+ assert 1
+ """)
+ args = ['--color=yes']
+ if verbose:
+ args.append('-vv')
+ result = testdir.runpytest(*args)
+ assert 'test session starts' in result.stdout.str()
+ assert '\x1b[1m' in result.stdout.str()
+ assert 'collecting 10 items' not in result.stdout.str()
+ if verbose:
+ assert 'collecting ...' in result.stdout.str()
+ assert 'collected 10 items' in result.stdout.str()
+
+
+def test_getreportopt():
+ class config(object):
+ class option(object):
+ reportchars = ""
+ disable_warnings = True
+
+ config.option.reportchars = "sf"
+ assert getreportopt(config) == "sf"
+
+ config.option.reportchars = "sfxw"
+ assert getreportopt(config) == "sfx"
+
+ config.option.reportchars = "sfx"
+ config.option.disable_warnings = False
+ assert getreportopt(config) == "sfxw"
+
+ config.option.reportchars = "sfxw"
+ config.option.disable_warnings = False
+ assert getreportopt(config) == "sfxw"
+
+
+def test_terminalreporter_reportopt_addopts(testdir):
+ testdir.makeini("[pytest]\naddopts=-rs")
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def tr(request):
+ tr = request.config.pluginmanager.getplugin("terminalreporter")
+ return tr
+ def test_opt(tr):
+ assert tr.hasopt('skipped')
+ assert not tr.hasopt('qwe')
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*1 passed*"
+ ])
+
+
+def test_tbstyle_short(testdir):
+ p = testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def arg(request):
+ return 42
+ def test_opt(arg):
+ x = 0
+ assert x
+ """)
+ result = testdir.runpytest("--tb=short")
+ s = result.stdout.str()
+ assert 'arg = 42' not in s
+ assert 'x = 0' not in s
+ result.stdout.fnmatch_lines([
+ "*%s:8*" % p.basename,
+ " assert x",
+ "E assert*",
+ ])
+ result = testdir.runpytest()
+ s = result.stdout.str()
+ assert 'x = 0' in s
+ assert 'assert x' in s
+
+
+def test_traceconfig(testdir, monkeypatch):
+ result = testdir.runpytest("--traceconfig")
+ result.stdout.fnmatch_lines([
+ "*active plugins*"
+ ])
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+
+
+class TestGenericReporting(object):
+ """ this test class can be subclassed with a different option
+ provider to run e.g. distributed tests.
+ """
+
+ def test_collect_fail(self, testdir, option):
+ testdir.makepyfile("import xyz\n")
+ result = testdir.runpytest(*option.args)
+ result.stdout.fnmatch_lines([
+ "ImportError while importing*",
+ "*No module named *xyz*",
+ "*1 error*",
+ ])
+
+ def test_maxfailures(self, testdir, option):
+ testdir.makepyfile("""
+ def test_1():
+ assert 0
+ def test_2():
+ assert 0
+ def test_3():
+ assert 0
+ """)
+ result = testdir.runpytest("--maxfail=2", *option.args)
+ result.stdout.fnmatch_lines([
+ "*def test_1():*",
+ "*def test_2():*",
+ "*2 failed*",
+ ])
+
+ def test_tb_option(self, testdir, option):
+ testdir.makepyfile("""
+ import pytest
+ def g():
+ raise IndexError
+ def test_func():
+ print (6*7)
+ g() # --calling--
+ """)
+ for tbopt in ["long", "short", "no"]:
+ print('testing --tb=%s...' % tbopt)
+ result = testdir.runpytest('--tb=%s' % tbopt)
+ s = result.stdout.str()
+ if tbopt == "long":
+ assert 'print (6*7)' in s
+ else:
+ assert 'print (6*7)' not in s
+ if tbopt != "no":
+ assert '--calling--' in s
+ assert 'IndexError' in s
+ else:
+ assert 'FAILURES' not in s
+ assert '--calling--' not in s
+ assert 'IndexError' not in s
+
+ def test_tb_crashline(self, testdir, option):
+ p = testdir.makepyfile("""
+ import pytest
+ def g():
+ raise IndexError
+ def test_func1():
+ print (6*7)
+ g() # --calling--
+ def test_func2():
+ assert 0, "hello"
+ """)
+ result = testdir.runpytest("--tb=line")
+ bn = p.basename
+ result.stdout.fnmatch_lines([
+ "*%s:3: IndexError*" % bn,
+ "*%s:8: AssertionError: hello*" % bn,
+ ])
+ s = result.stdout.str()
+ assert "def test_func2" not in s
+
+ def test_pytest_report_header(self, testdir, option):
+ testdir.makeconftest("""
+ def pytest_sessionstart(session):
+ session.config._somevalue = 42
+ def pytest_report_header(config):
+ return "hello: %s" % config._somevalue
+ """)
+ testdir.mkdir("a").join("conftest.py").write("""
+def pytest_report_header(config, startdir):
+ return ["line1", str(startdir)]
+""")
+ result = testdir.runpytest("a")
+ result.stdout.fnmatch_lines([
+ "*hello: 42*",
+ "line1",
+ str(testdir.tmpdir),
+ ])
+
+
+@pytest.mark.xfail("not hasattr(os, 'dup')")
+def test_fdopen_kept_alive_issue124(testdir):
+ testdir.makepyfile("""
+ import os, sys
+ k = []
+ def test_open_file_and_keep_alive(capfd):
+ stdout = os.fdopen(1, 'w', 1)
+ k.append(stdout)
+
+ def test_close_kept_alive_file():
+ stdout = k.pop()
+ stdout.close()
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*2 passed*"
+ ])
+
+
+def test_tbstyle_native_setup_error(testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture
+ def setup_error_fixture():
+ raise Exception("error in exception")
+
+ def test_error_fixture(setup_error_fixture):
+ pass
+ """)
+ result = testdir.runpytest("--tb=native")
+ result.stdout.fnmatch_lines([
+ '*File *test_tbstyle_native_setup_error.py", line *, in setup_error_fixture*'
+ ])
+
+
+def test_terminal_summary(testdir):
+ testdir.makeconftest("""
+ def pytest_terminal_summary(terminalreporter, exitstatus):
+ w = terminalreporter
+ w.section("hello")
+ w.line("world")
+ w.line("exitstatus: {0}".format(exitstatus))
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ *==== hello ====*
+ world
+ exitstatus: 5
+ """)
+
+
+def test_terminal_summary_warnings_are_displayed(testdir):
+ """Test that warnings emitted during pytest_terminal_summary are displayed.
+ (#1305).
+ """
+ testdir.makeconftest("""
+ def pytest_terminal_summary(terminalreporter):
+ config = terminalreporter.config
+ config.warn('C1', 'internal warning')
+ """)
+ result = testdir.runpytest('-rw')
+ result.stdout.fnmatch_lines([
+ '*internal warning',
+ '*== 1 warnings in *',
+ ])
+
+
+@pytest.mark.parametrize("exp_color, exp_line, stats_arg", [
+ # The method under test only cares about the length of each
+ # dict value, not the actual contents, so tuples of anything
+ # suffice
+
+ # Important statuses -- the highest priority of these always wins
+ ("red", "1 failed", {"failed": (1,)}),
+ ("red", "1 failed, 1 passed", {"failed": (1,), "passed": (1,)}),
+
+ ("red", "1 error", {"error": (1,)}),
+ ("red", "1 passed, 1 error", {"error": (1,), "passed": (1,)}),
+
+ # (a status that's not known to the code)
+ ("yellow", "1 weird", {"weird": (1,)}),
+ ("yellow", "1 passed, 1 weird", {"weird": (1,), "passed": (1,)}),
+
+ ("yellow", "1 warnings", {"warnings": (1,)}),
+ ("yellow", "1 passed, 1 warnings", {"warnings": (1,),
+ "passed": (1,)}),
+
+ ("green", "5 passed", {"passed": (1, 2, 3, 4, 5)}),
+
+
+ # "Boring" statuses. These have no effect on the color of the summary
+ # line. Thus, if *every* test has a boring status, the summary line stays
+ # at its default color, i.e. yellow, to warn the user that the test run
+ # produced no useful information
+ ("yellow", "1 skipped", {"skipped": (1,)}),
+ ("green", "1 passed, 1 skipped", {"skipped": (1,), "passed": (1,)}),
+
+ ("yellow", "1 deselected", {"deselected": (1,)}),
+ ("green", "1 passed, 1 deselected", {"deselected": (1,), "passed": (1,)}),
+
+ ("yellow", "1 xfailed", {"xfailed": (1,)}),
+ ("green", "1 passed, 1 xfailed", {"xfailed": (1,), "passed": (1,)}),
+
+ ("yellow", "1 xpassed", {"xpassed": (1,)}),
+ ("green", "1 passed, 1 xpassed", {"xpassed": (1,), "passed": (1,)}),
+
+ # Likewise if no tests were found at all
+ ("yellow", "no tests ran", {}),
+
+ # Test the empty-key special case
+ ("yellow", "no tests ran", {"": (1,)}),
+ ("green", "1 passed", {"": (1,), "passed": (1,)}),
+
+
+ # A couple more complex combinations
+ ("red", "1 failed, 2 passed, 3 xfailed",
+ {"passed": (1, 2), "failed": (1,), "xfailed": (1, 2, 3)}),
+
+ ("green", "1 passed, 2 skipped, 3 deselected, 2 xfailed",
+ {"passed": (1,),
+ "skipped": (1, 2),
+ "deselected": (1, 2, 3),
+ "xfailed": (1, 2)}),
+])
+def test_summary_stats(exp_line, exp_color, stats_arg):
+ print("Based on stats: %s" % stats_arg)
+ print("Expect summary: \"%s\"; with color \"%s\"" % (exp_line, exp_color))
+ (line, color) = build_summary_stats_line(stats_arg)
+ print("Actually got: \"%s\"; with color \"%s\"" % (line, color))
+ assert line == exp_line
+ assert color == exp_color
+
+
+def test_no_trailing_whitespace_after_inifile_word(testdir):
+ result = testdir.runpytest('')
+ assert 'inifile:\n' in result.stdout.str()
+
+ testdir.makeini('[pytest]')
+ result = testdir.runpytest('')
+ assert 'inifile: tox.ini\n' in result.stdout.str()
+
+
+class TestProgress:
+
+ @pytest.fixture
+ def many_tests_file(self, testdir):
+ testdir.makepyfile(
+ test_bar="""
+ import pytest
+ @pytest.mark.parametrize('i', range(10))
+ def test_bar(i): pass
+ """,
+ test_foo="""
+ import pytest
+ @pytest.mark.parametrize('i', range(5))
+ def test_foo(i): pass
+ """,
+ test_foobar="""
+ import pytest
+ @pytest.mark.parametrize('i', range(5))
+ def test_foobar(i): pass
+ """,
+ )
+
+ def test_zero_tests_collected(self, testdir):
+ """Some plugins (testmon for example) might issue pytest_runtest_logreport without any tests being
+ actually collected (#2971)."""
+ testdir.makeconftest("""
+ def pytest_collection_modifyitems(items, config):
+ from _pytest.runner import CollectReport
+ for node_id in ('nodeid1', 'nodeid2'):
+ rep = CollectReport(node_id, 'passed', None, None)
+ rep.when = 'passed'
+ rep.duration = 0.1
+ config.hook.pytest_runtest_logreport(report=rep)
+ """)
+ output = testdir.runpytest()
+ assert 'ZeroDivisionError' not in output.stdout.str()
+ output.stdout.fnmatch_lines([
+ '=* 2 passed in *=',
+ ])
+
+ def test_normal(self, many_tests_file, testdir):
+ output = testdir.runpytest()
+ output.stdout.re_match_lines([
+ r'test_bar.py \.{10} \s+ \[ 50%\]',
+ r'test_foo.py \.{5} \s+ \[ 75%\]',
+ r'test_foobar.py \.{5} \s+ \[100%\]',
+ ])
+
+ def test_verbose(self, many_tests_file, testdir):
+ output = testdir.runpytest('-v')
+ output.stdout.re_match_lines([
+ r'test_bar.py::test_bar\[0\] PASSED \s+ \[ 5%\]',
+ r'test_foo.py::test_foo\[4\] PASSED \s+ \[ 75%\]',
+ r'test_foobar.py::test_foobar\[4\] PASSED \s+ \[100%\]',
+ ])
+
+ def test_xdist_normal(self, many_tests_file, testdir):
+ pytest.importorskip('xdist')
+ output = testdir.runpytest('-n2')
+ output.stdout.re_match_lines([
+ r'\.{20} \s+ \[100%\]',
+ ])
+
+ def test_xdist_verbose(self, many_tests_file, testdir):
+ pytest.importorskip('xdist')
+ output = testdir.runpytest('-n2', '-v')
+ output.stdout.re_match_lines_random([
+ r'\[gw\d\] \[\s*\d+%\] PASSED test_bar.py::test_bar\[1\]',
+ r'\[gw\d\] \[\s*\d+%\] PASSED test_foo.py::test_foo\[1\]',
+ r'\[gw\d\] \[\s*\d+%\] PASSED test_foobar.py::test_foobar\[1\]',
+ ])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_tmpdir.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_tmpdir.py
new file mode 100644
index 00000000000..467e77252e7
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_tmpdir.py
@@ -0,0 +1,188 @@
+from __future__ import absolute_import, division, print_function
+import sys
+import py
+import pytest
+
+from _pytest.tmpdir import tmpdir
+
+
+def test_funcarg(testdir):
+ testdir.makepyfile("""
+ def pytest_generate_tests(metafunc):
+ metafunc.addcall(id='a')
+ metafunc.addcall(id='b')
+ def test_func(tmpdir): pass
+ """)
+ from _pytest.tmpdir import TempdirFactory
+ reprec = testdir.inline_run()
+ calls = reprec.getcalls("pytest_runtest_setup")
+ item = calls[0].item
+ config = item.config
+ tmpdirhandler = TempdirFactory(config)
+ item._initrequest()
+ p = tmpdir(item._request, tmpdirhandler)
+ assert p.check()
+ bn = p.basename.strip("0123456789")
+ assert bn.endswith("test_func_a_")
+ item.name = "qwe/\\abc"
+ p = tmpdir(item._request, tmpdirhandler)
+ assert p.check()
+ bn = p.basename.strip("0123456789")
+ assert bn == "qwe__abc"
+
+
+def test_ensuretemp(recwarn):
+ d1 = pytest.ensuretemp('hello')
+ d2 = pytest.ensuretemp('hello')
+ assert d1 == d2
+ assert d1.check(dir=1)
+
+
+class TestTempdirHandler(object):
+ def test_mktemp(self, testdir):
+ from _pytest.tmpdir import TempdirFactory
+ config = testdir.parseconfig()
+ config.option.basetemp = testdir.mkdir("hello")
+ t = TempdirFactory(config)
+ tmp = t.mktemp("world")
+ assert tmp.relto(t.getbasetemp()) == "world0"
+ tmp = t.mktemp("this")
+ assert tmp.relto(t.getbasetemp()).startswith("this")
+ tmp2 = t.mktemp("this")
+ assert tmp2.relto(t.getbasetemp()).startswith("this")
+ assert tmp2 != tmp
+
+
+class TestConfigTmpdir(object):
+ def test_getbasetemp_custom_removes_old(self, testdir):
+ mytemp = testdir.tmpdir.join("xyz")
+ p = testdir.makepyfile("""
+ def test_1(tmpdir):
+ pass
+ """)
+ testdir.runpytest(p, '--basetemp=%s' % mytemp)
+ mytemp.check()
+ mytemp.ensure("hello")
+
+ testdir.runpytest(p, '--basetemp=%s' % mytemp)
+ mytemp.check()
+ assert not mytemp.join("hello").check()
+
+
+def test_basetemp(testdir):
+ mytemp = testdir.tmpdir.mkdir("mytemp")
+ p = testdir.makepyfile("""
+ import pytest
+ def test_1():
+ pytest.ensuretemp("hello")
+ """)
+ result = testdir.runpytest(p, '--basetemp=%s' % mytemp)
+ assert result.ret == 0
+ assert mytemp.join('hello').check()
+
+
+@pytest.mark.skipif(not hasattr(py.path.local, 'mksymlinkto'),
+ reason="symlink not available on this platform")
+def test_tmpdir_always_is_realpath(testdir):
+ # the reason why tmpdir should be a realpath is that
+ # when you cd to it and do "os.getcwd()" you will anyway
+ # get the realpath. Using the symlinked path can thus
+ # easily result in path-inequality
+ # XXX if that proves to be a problem, consider using
+ # os.environ["PWD"]
+ realtemp = testdir.tmpdir.mkdir("myrealtemp")
+ linktemp = testdir.tmpdir.join("symlinktemp")
+ linktemp.mksymlinkto(realtemp)
+ p = testdir.makepyfile("""
+ def test_1(tmpdir):
+ import os
+ assert os.path.realpath(str(tmpdir)) == str(tmpdir)
+ """)
+ result = testdir.runpytest("-s", p, '--basetemp=%s/bt' % linktemp)
+ assert not result.ret
+
+
+def test_tmpdir_too_long_on_parametrization(testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.mark.parametrize("arg", ["1"*1000])
+ def test_some(arg, tmpdir):
+ tmpdir.ensure("hello")
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+
+def test_tmpdir_factory(testdir):
+ testdir.makepyfile("""
+ import pytest
+ @pytest.fixture(scope='session')
+ def session_dir(tmpdir_factory):
+ return tmpdir_factory.mktemp('data', numbered=False)
+ def test_some(session_dir):
+ session_dir.isdir()
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+
+def test_tmpdir_fallback_tox_env(testdir, monkeypatch):
+ """Test that tmpdir works even if environment variables required by getpass
+ module are missing (#1010).
+ """
+ monkeypatch.delenv('USER', raising=False)
+ monkeypatch.delenv('USERNAME', raising=False)
+ testdir.makepyfile("""
+ import pytest
+ def test_some(tmpdir):
+ assert tmpdir.isdir()
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+
+@pytest.fixture
+def break_getuser(monkeypatch):
+ monkeypatch.setattr('os.getuid', lambda: -1)
+ # taken from python 2.7/3.4
+ for envvar in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
+ monkeypatch.delenv(envvar, raising=False)
+
+
+@pytest.mark.usefixtures("break_getuser")
+@pytest.mark.skipif(sys.platform.startswith('win'), reason='no os.getuid on windows')
+def test_tmpdir_fallback_uid_not_found(testdir):
+ """Test that tmpdir works even if the current process's user id does not
+ correspond to a valid user.
+ """
+
+ testdir.makepyfile("""
+ import pytest
+ def test_some(tmpdir):
+ assert tmpdir.isdir()
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+
+
+@pytest.mark.usefixtures("break_getuser")
+@pytest.mark.skipif(sys.platform.startswith('win'), reason='no os.getuid on windows')
+def test_get_user_uid_not_found():
+ """Test that get_user() function works even if the current process's
+ user id does not correspond to a valid user (e.g. running pytest in a
+ Docker container with 'docker run -u'.
+ """
+ from _pytest.tmpdir import get_user
+ assert get_user() is None
+
+
+@pytest.mark.skipif(not sys.platform.startswith('win'), reason='win only')
+def test_get_user(monkeypatch):
+ """Test that get_user() function works even if environment variables
+ required by getpass module are missing from the environment on Windows
+ (#1010).
+ """
+ from _pytest.tmpdir import get_user
+ monkeypatch.delenv('USER', raising=False)
+ monkeypatch.delenv('USERNAME', raising=False)
+ assert get_user() is None
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_unittest.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_unittest.py
new file mode 100644
index 00000000000..e197735871f
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_unittest.py
@@ -0,0 +1,830 @@
+from __future__ import absolute_import, division, print_function
+from _pytest.main import EXIT_NOTESTSCOLLECTED
+import pytest
+import gc
+
+
+def test_simple_unittest(testdir):
+ testpath = testdir.makepyfile("""
+ import unittest
+ class MyTestCase(unittest.TestCase):
+ def testpassing(self):
+ self.assertEqual('foo', 'foo')
+ def test_failing(self):
+ self.assertEqual('foo', 'bar')
+ """)
+ reprec = testdir.inline_run(testpath)
+ assert reprec.matchreport("testpassing").passed
+ assert reprec.matchreport("test_failing").failed
+
+
+def test_runTest_method(testdir):
+ testdir.makepyfile("""
+ import unittest
+ class MyTestCaseWithRunTest(unittest.TestCase):
+ def runTest(self):
+ self.assertEqual('foo', 'foo')
+ class MyTestCaseWithoutRunTest(unittest.TestCase):
+ def runTest(self):
+ self.assertEqual('foo', 'foo')
+ def test_something(self):
+ pass
+ """)
+ result = testdir.runpytest("-v")
+ result.stdout.fnmatch_lines("""
+ *MyTestCaseWithRunTest::runTest*
+ *MyTestCaseWithoutRunTest::test_something*
+ *2 passed*
+ """)
+
+
+def test_isclasscheck_issue53(testdir):
+ testpath = testdir.makepyfile("""
+ import unittest
+ class _E(object):
+ def __getattr__(self, tag):
+ pass
+ E = _E()
+ """)
+ result = testdir.runpytest(testpath)
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+
+
+def test_setup(testdir):
+ testpath = testdir.makepyfile("""
+ import unittest
+ class MyTestCase(unittest.TestCase):
+ def setUp(self):
+ self.foo = 1
+ def setup_method(self, method):
+ self.foo2 = 1
+ def test_both(self):
+ self.assertEqual(1, self.foo)
+ assert self.foo2 == 1
+ def teardown_method(self, method):
+ assert 0, "42"
+
+ """)
+ reprec = testdir.inline_run("-s", testpath)
+ assert reprec.matchreport("test_both", when="call").passed
+ rep = reprec.matchreport("test_both", when="teardown")
+ assert rep.failed and '42' in str(rep.longrepr)
+
+
+def test_setUpModule(testdir):
+ testpath = testdir.makepyfile("""
+ values = []
+
+ def setUpModule():
+ values.append(1)
+
+ def tearDownModule():
+ del values[0]
+
+ def test_hello():
+ assert values == [1]
+
+ def test_world():
+ assert values == [1]
+ """)
+ result = testdir.runpytest(testpath)
+ result.stdout.fnmatch_lines([
+ "*2 passed*",
+ ])
+
+
+def test_setUpModule_failing_no_teardown(testdir):
+ testpath = testdir.makepyfile("""
+ values = []
+
+ def setUpModule():
+ 0/0
+
+ def tearDownModule():
+ values.append(1)
+
+ def test_hello():
+ pass
+ """)
+ reprec = testdir.inline_run(testpath)
+ reprec.assertoutcome(passed=0, failed=1)
+ call = reprec.getcalls("pytest_runtest_setup")[0]
+ assert not call.item.module.values
+
+
+def test_new_instances(testdir):
+ testpath = testdir.makepyfile("""
+ import unittest
+ class MyTestCase(unittest.TestCase):
+ def test_func1(self):
+ self.x = 2
+ def test_func2(self):
+ assert not hasattr(self, 'x')
+ """)
+ reprec = testdir.inline_run(testpath)
+ reprec.assertoutcome(passed=2)
+
+
+def test_teardown(testdir):
+ testpath = testdir.makepyfile("""
+ import unittest
+ class MyTestCase(unittest.TestCase):
+ values = []
+ def test_one(self):
+ pass
+ def tearDown(self):
+ self.values.append(None)
+ class Second(unittest.TestCase):
+ def test_check(self):
+ self.assertEqual(MyTestCase.values, [None])
+ """)
+ reprec = testdir.inline_run(testpath)
+ passed, skipped, failed = reprec.countoutcomes()
+ assert failed == 0, failed
+ assert passed == 2
+ assert passed + skipped + failed == 2
+
+
+def test_teardown_issue1649(testdir):
+ """
+ Are TestCase objects cleaned up? Often unittest TestCase objects set
+ attributes that are large and expensive during setUp.
+
+ The TestCase will not be cleaned up if the test fails, because it
+ would then exist in the stackframe.
+ """
+ testpath = testdir.makepyfile("""
+ import unittest
+ class TestCaseObjectsShouldBeCleanedUp(unittest.TestCase):
+ def setUp(self):
+ self.an_expensive_object = 1
+ def test_demo(self):
+ pass
+
+ """)
+ testdir.inline_run("-s", testpath)
+ gc.collect()
+ for obj in gc.get_objects():
+ assert type(obj).__name__ != 'TestCaseObjectsShouldBeCleanedUp'
+
+
+def test_unittest_skip_issue148(testdir):
+ testpath = testdir.makepyfile("""
+ import unittest
+
+ @unittest.skip("hello")
+ class MyTestCase(unittest.TestCase):
+ @classmethod
+ def setUpClass(self):
+ xxx
+ def test_one(self):
+ pass
+ @classmethod
+ def tearDownClass(self):
+ xxx
+ """)
+ reprec = testdir.inline_run(testpath)
+ reprec.assertoutcome(skipped=1)
+
+
+def test_method_and_teardown_failing_reporting(testdir):
+ testdir.makepyfile("""
+ import unittest, pytest
+ class TC(unittest.TestCase):
+ def tearDown(self):
+ assert 0, "down1"
+ def test_method(self):
+ assert False, "down2"
+ """)
+ result = testdir.runpytest("-s")
+ assert result.ret == 1
+ result.stdout.fnmatch_lines([
+ "*tearDown*",
+ "*assert 0*",
+ "*test_method*",
+ "*assert False*",
+ "*1 failed*1 error*",
+ ])
+
+
+def test_setup_failure_is_shown(testdir):
+ testdir.makepyfile("""
+ import unittest
+ import pytest
+ class TC(unittest.TestCase):
+ def setUp(self):
+ assert 0, "down1"
+ def test_method(self):
+ print ("never42")
+ xyz
+ """)
+ result = testdir.runpytest("-s")
+ assert result.ret == 1
+ result.stdout.fnmatch_lines([
+ "*setUp*",
+ "*assert 0*down1*",
+ "*1 failed*",
+ ])
+ assert 'never42' not in result.stdout.str()
+
+
+def test_setup_setUpClass(testdir):
+ testpath = testdir.makepyfile("""
+ import unittest
+ import pytest
+ class MyTestCase(unittest.TestCase):
+ x = 0
+ @classmethod
+ def setUpClass(cls):
+ cls.x += 1
+ def test_func1(self):
+ assert self.x == 1
+ def test_func2(self):
+ assert self.x == 1
+ @classmethod
+ def tearDownClass(cls):
+ cls.x -= 1
+ def test_teareddown():
+ assert MyTestCase.x == 0
+ """)
+ reprec = testdir.inline_run(testpath)
+ reprec.assertoutcome(passed=3)
+
+
+def test_setup_class(testdir):
+ testpath = testdir.makepyfile("""
+ import unittest
+ import pytest
+ class MyTestCase(unittest.TestCase):
+ x = 0
+ def setup_class(cls):
+ cls.x += 1
+ def test_func1(self):
+ assert self.x == 1
+ def test_func2(self):
+ assert self.x == 1
+ def teardown_class(cls):
+ cls.x -= 1
+ def test_teareddown():
+ assert MyTestCase.x == 0
+ """)
+ reprec = testdir.inline_run(testpath)
+ reprec.assertoutcome(passed=3)
+
+
+@pytest.mark.parametrize("type", ['Error', 'Failure'])
+def test_testcase_adderrorandfailure_defers(testdir, type):
+ testdir.makepyfile("""
+ from unittest import TestCase
+ import pytest
+ class MyTestCase(TestCase):
+ def run(self, result):
+ excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0)
+ try:
+ result.add%s(self, excinfo._excinfo)
+ except KeyboardInterrupt:
+ raise
+ except:
+ pytest.fail("add%s should not raise")
+ def test_hello(self):
+ pass
+ """ % (type, type))
+ result = testdir.runpytest()
+ assert 'should not raise' not in result.stdout.str()
+
+
+@pytest.mark.parametrize("type", ['Error', 'Failure'])
+def test_testcase_custom_exception_info(testdir, type):
+ testdir.makepyfile("""
+ from unittest import TestCase
+ import py, pytest
+ import _pytest._code
+ class MyTestCase(TestCase):
+ def run(self, result):
+ excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0)
+ # we fake an incompatible exception info
+ from _pytest.monkeypatch import MonkeyPatch
+ mp = MonkeyPatch()
+ def t(*args):
+ mp.undo()
+ raise TypeError()
+ mp.setattr(_pytest._code, 'ExceptionInfo', t)
+ try:
+ excinfo = excinfo._excinfo
+ result.add%(type)s(self, excinfo)
+ finally:
+ mp.undo()
+ def test_hello(self):
+ pass
+ """ % locals())
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "NOTE: Incompatible Exception Representation*",
+ "*ZeroDivisionError*",
+ "*1 failed*",
+ ])
+
+
+def test_testcase_totally_incompatible_exception_info(testdir):
+ item, = testdir.getitems("""
+ from unittest import TestCase
+ class MyTestCase(TestCase):
+ def test_hello(self):
+ pass
+ """)
+ item.addError(None, 42)
+ excinfo = item._excinfo.pop(0)
+ assert 'ERROR: Unknown Incompatible' in str(excinfo.getrepr())
+
+
+def test_module_level_pytestmark(testdir):
+ testpath = testdir.makepyfile("""
+ import unittest
+ import pytest
+ pytestmark = pytest.mark.xfail
+ class MyTestCase(unittest.TestCase):
+ def test_func1(self):
+ assert 0
+ """)
+ reprec = testdir.inline_run(testpath, "-s")
+ reprec.assertoutcome(skipped=1)
+
+
+class TestTrialUnittest(object):
+ def setup_class(cls):
+ cls.ut = pytest.importorskip("twisted.trial.unittest")
+ # on windows trial uses a socket for a reactor and apparently doesn't close it properly
+ # https://twistedmatrix.com/trac/ticket/9227
+ cls.ignore_unclosed_socket_warning = ('-W', 'always')
+
+ def test_trial_testcase_runtest_not_collected(self, testdir):
+ testdir.makepyfile("""
+ from twisted.trial.unittest import TestCase
+
+ class TC(TestCase):
+ def test_hello(self):
+ pass
+ """)
+ reprec = testdir.inline_run(*self.ignore_unclosed_socket_warning)
+ reprec.assertoutcome(passed=1)
+ testdir.makepyfile("""
+ from twisted.trial.unittest import TestCase
+
+ class TC(TestCase):
+ def runTest(self):
+ pass
+ """)
+ reprec = testdir.inline_run(*self.ignore_unclosed_socket_warning)
+ reprec.assertoutcome(passed=1)
+
+ def test_trial_exceptions_with_skips(self, testdir):
+ testdir.makepyfile("""
+ from twisted.trial import unittest
+ import pytest
+ class TC(unittest.TestCase):
+ def test_hello(self):
+ pytest.skip("skip_in_method")
+ @pytest.mark.skipif("sys.version_info != 1")
+ def test_hello2(self):
+ pass
+ @pytest.mark.xfail(reason="iwanto")
+ def test_hello3(self):
+ assert 0
+ def test_hello4(self):
+ pytest.xfail("i2wanto")
+ def test_trial_skip(self):
+ pass
+ test_trial_skip.skip = "trialselfskip"
+
+ def test_trial_todo(self):
+ assert 0
+ test_trial_todo.todo = "mytodo"
+
+ def test_trial_todo_success(self):
+ pass
+ test_trial_todo_success.todo = "mytodo"
+
+ class TC2(unittest.TestCase):
+ def setup_class(cls):
+ pytest.skip("skip_in_setup_class")
+ def test_method(self):
+ pass
+ """)
+ from _pytest.compat import _is_unittest_unexpected_success_a_failure
+ should_fail = _is_unittest_unexpected_success_a_failure()
+ result = testdir.runpytest("-rxs", *self.ignore_unclosed_socket_warning)
+ result.stdout.fnmatch_lines_random([
+ "*XFAIL*test_trial_todo*",
+ "*trialselfskip*",
+ "*skip_in_setup_class*",
+ "*iwanto*",
+ "*i2wanto*",
+ "*sys.version_info*",
+ "*skip_in_method*",
+ "*1 failed*4 skipped*3 xfailed*" if should_fail else "*4 skipped*3 xfail*1 xpass*",
+ ])
+ assert result.ret == (1 if should_fail else 0)
+
+ def test_trial_error(self, testdir):
+ testdir.makepyfile("""
+ from twisted.trial.unittest import TestCase
+ from twisted.internet.defer import Deferred
+ from twisted.internet import reactor
+
+ class TC(TestCase):
+ def test_one(self):
+ crash
+
+ def test_two(self):
+ def f(_):
+ crash
+
+ d = Deferred()
+ d.addCallback(f)
+ reactor.callLater(0.3, d.callback, None)
+ return d
+
+ def test_three(self):
+ def f():
+ pass # will never get called
+ reactor.callLater(0.3, f)
+ # will crash at teardown
+
+ def test_four(self):
+ def f(_):
+ reactor.callLater(0.3, f)
+ crash
+
+ d = Deferred()
+ d.addCallback(f)
+ reactor.callLater(0.3, d.callback, None)
+ return d
+ # will crash both at test time and at teardown
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ "*ERRORS*",
+ "*DelayedCalls*",
+ "*test_four*",
+ "*NameError*crash*",
+ "*test_one*",
+ "*NameError*crash*",
+ "*test_three*",
+ "*DelayedCalls*",
+ "*test_two*",
+ "*crash*",
+ ])
+
+ def test_trial_pdb(self, testdir):
+ p = testdir.makepyfile("""
+ from twisted.trial import unittest
+ import pytest
+ class TC(unittest.TestCase):
+ def test_hello(self):
+ assert 0, "hellopdb"
+ """)
+ child = testdir.spawn_pytest(p)
+ child.expect("hellopdb")
+ child.sendeof()
+
+ def test_trial_testcase_skip_property(self, testdir):
+ testpath = testdir.makepyfile("""
+ from twisted.trial import unittest
+ class MyTestCase(unittest.TestCase):
+ skip = 'dont run'
+ def test_func(self):
+ pass
+ """)
+ reprec = testdir.inline_run(testpath, "-s")
+ reprec.assertoutcome(skipped=1)
+
+ def test_trial_testfunction_skip_property(self, testdir):
+ testpath = testdir.makepyfile("""
+ from twisted.trial import unittest
+ class MyTestCase(unittest.TestCase):
+ def test_func(self):
+ pass
+ test_func.skip = 'dont run'
+ """)
+ reprec = testdir.inline_run(testpath, "-s")
+ reprec.assertoutcome(skipped=1)
+
+ def test_trial_testcase_todo_property(self, testdir):
+ testpath = testdir.makepyfile("""
+ from twisted.trial import unittest
+ class MyTestCase(unittest.TestCase):
+ todo = 'dont run'
+ def test_func(self):
+ assert 0
+ """)
+ reprec = testdir.inline_run(testpath, "-s")
+ reprec.assertoutcome(skipped=1)
+
+ def test_trial_testfunction_todo_property(self, testdir):
+ testpath = testdir.makepyfile("""
+ from twisted.trial import unittest
+ class MyTestCase(unittest.TestCase):
+ def test_func(self):
+ assert 0
+ test_func.todo = 'dont run'
+ """)
+ reprec = testdir.inline_run(testpath, "-s", *self.ignore_unclosed_socket_warning)
+ reprec.assertoutcome(skipped=1)
+
+
+def test_djangolike_testcase(testdir):
+ # contributed from Morten Breekevold
+ testdir.makepyfile("""
+ from unittest import TestCase, main
+
+ class DjangoLikeTestCase(TestCase):
+
+ def setUp(self):
+ print ("setUp()")
+
+ def test_presetup_has_been_run(self):
+ print ("test_thing()")
+ self.assertTrue(hasattr(self, 'was_presetup'))
+
+ def tearDown(self):
+ print ("tearDown()")
+
+ def __call__(self, result=None):
+ try:
+ self._pre_setup()
+ except (KeyboardInterrupt, SystemExit):
+ raise
+ except Exception:
+ import sys
+ result.addError(self, sys.exc_info())
+ return
+ super(DjangoLikeTestCase, self).__call__(result)
+ try:
+ self._post_teardown()
+ except (KeyboardInterrupt, SystemExit):
+ raise
+ except Exception:
+ import sys
+ result.addError(self, sys.exc_info())
+ return
+
+ def _pre_setup(self):
+ print ("_pre_setup()")
+ self.was_presetup = True
+
+ def _post_teardown(self):
+ print ("_post_teardown()")
+ """)
+ result = testdir.runpytest("-s")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ "*_pre_setup()*",
+ "*setUp()*",
+ "*test_thing()*",
+ "*tearDown()*",
+ "*_post_teardown()*",
+ ])
+
+
+def test_unittest_not_shown_in_traceback(testdir):
+ testdir.makepyfile("""
+ import unittest
+ class t(unittest.TestCase):
+ def test_hello(self):
+ x = 3
+ self.assertEqual(x, 4)
+ """)
+ res = testdir.runpytest()
+ assert "failUnlessEqual" not in res.stdout.str()
+
+
+def test_unorderable_types(testdir):
+ testdir.makepyfile("""
+ import unittest
+ class TestJoinEmpty(unittest.TestCase):
+ pass
+
+ def make_test():
+ class Test(unittest.TestCase):
+ pass
+ Test.__name__ = "TestFoo"
+ return Test
+ TestFoo = make_test()
+ """)
+ result = testdir.runpytest()
+ assert "TypeError" not in result.stdout.str()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+
+
+def test_unittest_typerror_traceback(testdir):
+ testdir.makepyfile("""
+ import unittest
+ class TestJoinEmpty(unittest.TestCase):
+ def test_hello(self, arg1):
+ pass
+ """)
+ result = testdir.runpytest()
+ assert "TypeError" in result.stdout.str()
+ assert result.ret == 1
+
+
+@pytest.mark.parametrize('runner', ['pytest', 'unittest'])
+def test_unittest_expected_failure_for_failing_test_is_xfail(testdir, runner):
+ script = testdir.makepyfile("""
+ import unittest
+ class MyTestCase(unittest.TestCase):
+ @unittest.expectedFailure
+ def test_failing_test_is_xfail(self):
+ assert False
+ if __name__ == '__main__':
+ unittest.main()
+ """)
+ if runner == 'pytest':
+ result = testdir.runpytest("-rxX")
+ result.stdout.fnmatch_lines([
+ "*XFAIL*MyTestCase*test_failing_test_is_xfail*",
+ "*1 xfailed*",
+ ])
+ else:
+ result = testdir.runpython(script)
+ result.stderr.fnmatch_lines([
+ "*1 test in*",
+ "*OK*(expected failures=1)*",
+ ])
+ assert result.ret == 0
+
+
+@pytest.mark.parametrize('runner', ['pytest', 'unittest'])
+def test_unittest_expected_failure_for_passing_test_is_fail(testdir, runner):
+ script = testdir.makepyfile("""
+ import unittest
+ class MyTestCase(unittest.TestCase):
+ @unittest.expectedFailure
+ def test_passing_test_is_fail(self):
+ assert True
+ if __name__ == '__main__':
+ unittest.main()
+ """)
+ from _pytest.compat import _is_unittest_unexpected_success_a_failure
+ should_fail = _is_unittest_unexpected_success_a_failure()
+ if runner == 'pytest':
+ result = testdir.runpytest("-rxX")
+ result.stdout.fnmatch_lines([
+ "*MyTestCase*test_passing_test_is_fail*",
+ "*1 failed*" if should_fail else "*1 xpassed*",
+ ])
+ else:
+ result = testdir.runpython(script)
+ result.stderr.fnmatch_lines([
+ "*1 test in*",
+ "*(unexpected successes=1)*",
+ ])
+
+ assert result.ret == (1 if should_fail else 0)
+
+
+@pytest.mark.parametrize('fix_type, stmt', [
+ ('fixture', 'return'),
+ ('yield_fixture', 'yield'),
+])
+def test_unittest_setup_interaction(testdir, fix_type, stmt):
+ testdir.makepyfile("""
+ import unittest
+ import pytest
+ class MyTestCase(unittest.TestCase):
+ @pytest.{fix_type}(scope="class", autouse=True)
+ def perclass(self, request):
+ request.cls.hello = "world"
+ {stmt}
+ @pytest.{fix_type}(scope="function", autouse=True)
+ def perfunction(self, request):
+ request.instance.funcname = request.function.__name__
+ {stmt}
+
+ def test_method1(self):
+ assert self.funcname == "test_method1"
+ assert self.hello == "world"
+
+ def test_method2(self):
+ assert self.funcname == "test_method2"
+
+ def test_classattr(self):
+ assert self.__class__.hello == "world"
+ """.format(fix_type=fix_type, stmt=stmt))
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("*3 passed*")
+
+
+def test_non_unittest_no_setupclass_support(testdir):
+ testpath = testdir.makepyfile("""
+ class TestFoo(object):
+ x = 0
+
+ @classmethod
+ def setUpClass(cls):
+ cls.x = 1
+
+ def test_method1(self):
+ assert self.x == 0
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.x = 1
+
+ def test_not_teareddown():
+ assert TestFoo.x == 0
+
+ """)
+ reprec = testdir.inline_run(testpath)
+ reprec.assertoutcome(passed=2)
+
+
+def test_no_teardown_if_setupclass_failed(testdir):
+ testpath = testdir.makepyfile("""
+ import unittest
+
+ class MyTestCase(unittest.TestCase):
+ x = 0
+
+ @classmethod
+ def setUpClass(cls):
+ cls.x = 1
+ assert False
+
+ def test_func1(self):
+ cls.x = 10
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.x = 100
+
+ def test_notTornDown():
+ assert MyTestCase.x == 1
+ """)
+ reprec = testdir.inline_run(testpath)
+ reprec.assertoutcome(passed=1, failed=1)
+
+
+def test_issue333_result_clearing(testdir):
+ testdir.makeconftest("""
+ import pytest
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_runtest_call(item):
+ yield
+ assert 0
+ """)
+ testdir.makepyfile("""
+ import unittest
+ class TestIt(unittest.TestCase):
+ def test_func(self):
+ 0/0
+ """)
+
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(failed=1)
+
+
+def test_unittest_raise_skip_issue748(testdir):
+ testdir.makepyfile(test_foo="""
+ import unittest
+
+ class MyTestCase(unittest.TestCase):
+ def test_one(self):
+ raise unittest.SkipTest('skipping due to reasons')
+ """)
+ result = testdir.runpytest("-v", '-rs')
+ result.stdout.fnmatch_lines("""
+ *SKIP*[1]*test_foo.py*skipping due to reasons*
+ *1 skipped*
+ """)
+
+
+def test_unittest_skip_issue1169(testdir):
+ testdir.makepyfile(test_foo="""
+ import unittest
+
+ class MyTestCase(unittest.TestCase):
+ @unittest.skip("skipping due to reasons")
+ def test_skip(self):
+ self.fail()
+ """)
+ result = testdir.runpytest("-v", '-rs')
+ result.stdout.fnmatch_lines("""
+ *SKIP*[1]*skipping due to reasons*
+ *1 skipped*
+ """)
+
+
+def test_class_method_containing_test_issue1558(testdir):
+ testdir.makepyfile(test_foo="""
+ import unittest
+
+ class MyTestCase(unittest.TestCase):
+ def test_should_run(self):
+ pass
+ def test_should_not_run(self):
+ pass
+ test_should_not_run.__test__ = False
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_warnings.py b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_warnings.py
new file mode 100644
index 00000000000..02400bd1ded
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/testing/test_warnings.py
@@ -0,0 +1,258 @@
+# -*- coding: utf8 -*-
+from __future__ import unicode_literals
+
+import sys
+
+import pytest
+
+
+WARNINGS_SUMMARY_HEADER = 'warnings summary'
+
+
+@pytest.fixture
+def pyfile_with_warnings(testdir, request):
+ """
+ Create a test file which calls a function in a module which generates warnings.
+ """
+ testdir.syspathinsert()
+ test_name = request.function.__name__
+ module_name = test_name.lstrip('test_') + '_module'
+ testdir.makepyfile(**{
+ module_name: '''
+ import warnings
+ def foo():
+ warnings.warn(UserWarning("user warning"))
+ warnings.warn(RuntimeWarning("runtime warning"))
+ return 1
+ ''',
+ test_name: '''
+ import {module_name}
+ def test_func():
+ assert {module_name}.foo() == 1
+ '''.format(module_name=module_name)
+ })
+
+
+@pytest.mark.filterwarnings('always')
+def test_normal_flow(testdir, pyfile_with_warnings):
+ """
+ Check that the warnings section is displayed, containing test node ids followed by
+ all warnings generated by that test node.
+ """
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*== %s ==*' % WARNINGS_SUMMARY_HEADER,
+
+ '*test_normal_flow.py::test_func',
+
+ '*normal_flow_module.py:3: UserWarning: user warning',
+ '* warnings.warn(UserWarning("user warning"))',
+
+ '*normal_flow_module.py:4: RuntimeWarning: runtime warning',
+ '* warnings.warn(RuntimeWarning("runtime warning"))',
+ '* 1 passed, 2 warnings*',
+ ])
+ assert result.stdout.str().count('test_normal_flow.py::test_func') == 1
+
+
+@pytest.mark.filterwarnings('always')
+def test_setup_teardown_warnings(testdir, pyfile_with_warnings):
+ testdir.makepyfile('''
+ import warnings
+ import pytest
+
+ @pytest.fixture
+ def fix():
+ warnings.warn(UserWarning("warning during setup"))
+ yield
+ warnings.warn(UserWarning("warning during teardown"))
+
+ def test_func(fix):
+ pass
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*== %s ==*' % WARNINGS_SUMMARY_HEADER,
+
+ '*test_setup_teardown_warnings.py:6: UserWarning: warning during setup',
+ '*warnings.warn(UserWarning("warning during setup"))',
+
+ '*test_setup_teardown_warnings.py:8: UserWarning: warning during teardown',
+ '*warnings.warn(UserWarning("warning during teardown"))',
+ '* 1 passed, 2 warnings*',
+ ])
+
+
+@pytest.mark.parametrize('method', ['cmdline', 'ini'])
+def test_as_errors(testdir, pyfile_with_warnings, method):
+ args = ('-W', 'error') if method == 'cmdline' else ()
+ if method == 'ini':
+ testdir.makeini('''
+ [pytest]
+ filterwarnings= error
+ ''')
+ result = testdir.runpytest(*args)
+ result.stdout.fnmatch_lines([
+ 'E UserWarning: user warning',
+ 'as_errors_module.py:3: UserWarning',
+ '* 1 failed in *',
+ ])
+
+
+@pytest.mark.parametrize('method', ['cmdline', 'ini'])
+def test_ignore(testdir, pyfile_with_warnings, method):
+ args = ('-W', 'ignore') if method == 'cmdline' else ()
+ if method == 'ini':
+ testdir.makeini('''
+ [pytest]
+ filterwarnings= ignore
+ ''')
+
+ result = testdir.runpytest(*args)
+ result.stdout.fnmatch_lines([
+ '* 1 passed in *',
+ ])
+ assert WARNINGS_SUMMARY_HEADER not in result.stdout.str()
+
+
+@pytest.mark.skipif(sys.version_info < (3, 0),
+ reason='warnings message is unicode is ok in python3')
+@pytest.mark.filterwarnings('always')
+def test_unicode(testdir, pyfile_with_warnings):
+ testdir.makepyfile('''
+ # -*- coding: utf8 -*-
+ import warnings
+ import pytest
+
+
+ @pytest.fixture
+ def fix():
+ warnings.warn(u"测试")
+ yield
+
+ def test_func(fix):
+ pass
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*== %s ==*' % WARNINGS_SUMMARY_HEADER,
+ '*test_unicode.py:8: UserWarning: \u6d4b\u8bd5*',
+ '* 1 passed, 1 warnings*',
+ ])
+
+
+@pytest.mark.skipif(sys.version_info >= (3, 0),
+ reason='warnings message is broken as it is not str instance')
+def test_py2_unicode(testdir, pyfile_with_warnings):
+ if getattr(sys, "pypy_version_info", ())[:2] == (5, 9) and sys.platform.startswith('win'):
+ pytest.xfail("fails with unicode error on PyPy2 5.9 and Windows (#2905)")
+ testdir.makepyfile('''
+ # -*- coding: utf8 -*-
+ import warnings
+ import pytest
+
+
+ @pytest.fixture
+ def fix():
+ warnings.warn(u"测试")
+ yield
+
+ @pytest.mark.filterwarnings('always')
+ def test_func(fix):
+ pass
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*== %s ==*' % WARNINGS_SUMMARY_HEADER,
+
+ '*test_py2_unicode.py:8: UserWarning: \\u6d4b\\u8bd5',
+ '*warnings.warn(u"\u6d4b\u8bd5")',
+ '*warnings.py:*: UnicodeWarning: Warning is using unicode non*',
+ '* 1 passed, 2 warnings*',
+ ])
+
+
+def test_py2_unicode_ascii(testdir):
+ """Ensure that our warning about 'unicode warnings containing non-ascii messages'
+ does not trigger with ascii-convertible messages"""
+ testdir.makeini('[pytest]')
+ testdir.makepyfile('''
+ import pytest
+ import warnings
+
+ @pytest.mark.filterwarnings('always')
+ def test_func():
+ warnings.warn(u"hello")
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*== %s ==*' % WARNINGS_SUMMARY_HEADER,
+ '*warnings.warn(u"hello")',
+ '* 1 passed, 1 warnings in*'
+ ])
+
+
+def test_works_with_filterwarnings(testdir):
+ """Ensure our warnings capture does not mess with pre-installed filters (#2430)."""
+ testdir.makepyfile('''
+ import warnings
+
+ class MyWarning(Warning):
+ pass
+
+ warnings.filterwarnings("error", category=MyWarning)
+
+ class TestWarnings(object):
+ def test_my_warning(self):
+ try:
+ warnings.warn(MyWarning("warn!"))
+ assert False
+ except MyWarning:
+ assert True
+ ''')
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines([
+ '*== 1 passed in *',
+ ])
+
+
+@pytest.mark.parametrize('default_config', ['ini', 'cmdline'])
+def test_filterwarnings_mark(testdir, default_config):
+ """
+ Test ``filterwarnings`` mark works and takes precedence over command line and ini options.
+ """
+ if default_config == 'ini':
+ testdir.makeini("""
+ [pytest]
+ filterwarnings = always
+ """)
+ testdir.makepyfile("""
+ import warnings
+ import pytest
+
+ @pytest.mark.filterwarnings('ignore::RuntimeWarning')
+ def test_ignore_runtime_warning():
+ warnings.warn(RuntimeWarning())
+
+ @pytest.mark.filterwarnings('error')
+ def test_warning_error():
+ warnings.warn(RuntimeWarning())
+
+ def test_show_warning():
+ warnings.warn(RuntimeWarning())
+ """)
+ result = testdir.runpytest('-W always' if default_config == 'cmdline' else '')
+ result.stdout.fnmatch_lines(['*= 1 failed, 2 passed, 1 warnings in *'])
+
+
+def test_non_string_warning_argument(testdir):
+ """Non-str argument passed to warning breaks pytest (#2956)"""
+ testdir.makepyfile("""
+ import warnings
+ import pytest
+
+ def test():
+ warnings.warn(UserWarning(1, u'foo'))
+ """)
+ result = testdir.runpytest('-W', 'always')
+ result.stdout.fnmatch_lines(['*= 1 passed, 1 warnings in *'])
diff --git a/tests/wpt/web-platform-tests/tools/third_party/pytest/tox.ini b/tests/wpt/web-platform-tests/tools/third_party/pytest/tox.ini
new file mode 100644
index 00000000000..900b602dc33
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/third_party/pytest/tox.ini
@@ -0,0 +1,220 @@
+[tox]
+minversion = 2.0
+distshare = {homedir}/.tox/distshare
+# make sure to update environment list in travis.yml and appveyor.yml
+envlist =
+ linting
+ py27
+ py34
+ py35
+ py36
+ py37
+ pypy
+ {py27,py36}-{pexpect,xdist,trial,numpy,pluggymaster}
+ py27-nobyte
+ doctesting
+ py35-freeze
+ docs
+
+[testenv]
+commands = pytest --lsof -ra {posargs:testing}
+passenv = USER USERNAME
+deps =
+ hypothesis>=3.5.2
+ nose
+ mock
+ requests
+
+[testenv:py27-subprocess]
+changedir = .
+deps =
+ pytest-xdist>=1.13
+ mock
+ nose
+commands =
+ pytest -n3 -ra --runpytest=subprocess {posargs:testing}
+
+
+[testenv:linting]
+skipsdist = True
+usedevelop = True
+basepython = python2.7
+deps =
+ flake8
+ # pygments required by rst-lint
+ pygments
+ restructuredtext_lint
+commands =
+ flake8 pytest.py _pytest testing setup.py pytest.py
+ {envpython} scripts/check-rst.py
+
+[testenv:py27-xdist]
+deps =
+ pytest-xdist>=1.13
+ mock
+ nose
+ hypothesis>=3.5.2
+changedir=testing
+commands =
+ pytest -n1 -ra {posargs:.}
+
+[testenv:py36-xdist]
+deps = {[testenv:py27-xdist]deps}
+commands =
+ pytest -n3 -ra {posargs:testing}
+
+[testenv:py27-pexpect]
+changedir = testing
+platform = linux|darwin
+deps = pexpect
+commands =
+ pytest -ra test_pdb.py test_terminal.py test_unittest.py
+
+[testenv:py36-pexpect]
+changedir = testing
+platform = linux|darwin
+deps = {[testenv:py27-pexpect]deps}
+commands =
+ pytest -ra test_pdb.py test_terminal.py test_unittest.py
+
+[testenv:py27-nobyte]
+deps =
+ pytest-xdist>=1.13
+ hypothesis>=3.5.2
+distribute = true
+changedir=testing
+setenv =
+ PYTHONDONTWRITEBYTECODE=1
+commands =
+ pytest -n3 -ra {posargs:.}
+
+[testenv:py27-trial]
+deps = twisted
+commands =
+ pytest -ra {posargs:testing/test_unittest.py}
+
+[testenv:py36-trial]
+deps = {[testenv:py27-trial]deps}
+commands =
+ pytest -ra {posargs:testing/test_unittest.py}
+
+[testenv:py27-numpy]
+deps=numpy
+commands=
+ pytest -ra {posargs:testing/python/approx.py}
+
+[testenv:py36-numpy]
+deps=numpy
+commands=
+ pytest -ra {posargs:testing/python/approx.py}
+
+[testenv:py27-pluggymaster]
+setenv=
+ _PYTEST_SETUP_SKIP_PLUGGY_DEP=1
+deps =
+ {[testenv]deps}
+ git+https://github.com/pytest-dev/pluggy.git@master
+
+[testenv:py35-pluggymaster]
+setenv=
+ _PYTEST_SETUP_SKIP_PLUGGY_DEP=1
+deps =
+ {[testenv:py27-pluggymaster]deps}
+ git+https://github.com/pytest-dev/pluggy.git@master
+
+[testenv:docs]
+skipsdist = True
+usedevelop = True
+basepython = python
+changedir = doc/en
+deps =
+ sphinx
+ PyYAML
+
+commands =
+ sphinx-build -W -b html . _build
+
+[testenv:doctesting]
+basepython = python
+usedevelop = True
+skipsdist = True
+# ensure the given pyargs cant mean anytrhing else
+changedir = doc/
+deps =
+ PyYAML
+commands =
+ pytest -ra en
+ pytest --doctest-modules --pyargs _pytest
+
+[testenv:regen]
+changedir = doc/en
+skipsdist = True
+basepython = python3.5
+deps =
+ sphinx
+ PyYAML
+ regendoc>=0.6.1
+whitelist_externals =
+ rm
+ make
+commands =
+ rm -rf /tmp/doc-exec*
+ make regen
+
+[testenv:fix-lint]
+skipsdist = True
+usedevelop = True
+deps =
+ autopep8
+commands =
+ autopep8 --in-place -r --max-line-length=120 --exclude=test_source_multiline_block.py _pytest testing setup.py pytest.py
+
+[testenv:jython]
+changedir = testing
+commands =
+ {envpython} {envbindir}/py.test-jython -ra {posargs}
+
+[testenv:py35-freeze]
+changedir = testing/freeze
+deps = pyinstaller
+commands =
+ {envpython} create_executable.py
+ {envpython} tox_run.py
+
+
+[testenv:coveralls]
+passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH COVERALLS_REPO_TOKEN
+usedevelop = True
+changedir = .
+deps =
+ {[testenv]deps}
+ coveralls
+commands =
+ coverage run --source=_pytest -m pytest testing
+ coverage report -m
+ coveralls
+
+[pytest]
+minversion = 2.0
+plugins = pytester
+#--pyargs --doctest-modules --ignore=.tox
+addopts = -ra -p pytester --ignore=testing/cx_freeze
+rsyncdirs = tox.ini pytest.py _pytest testing
+python_files = test_*.py *_test.py testing/*/*.py
+python_classes = Test Acceptance
+python_functions = test
+norecursedirs = .tox ja .hg cx_freeze_source
+xfail_strict=true
+filterwarnings =
+ error
+ # produced by path.local
+ ignore:bad escape.*:DeprecationWarning:re
+ # produced by path.readlines
+ ignore:.*U.*mode is deprecated:DeprecationWarning
+ # produced by pytest-xdist
+ ignore:.*type argument to addoption.*:DeprecationWarning
+ # produced by python >=3.5 on execnet (pytest-xdist)
+ ignore:.*inspect.getargspec.*deprecated, use inspect.signature.*:DeprecationWarning
+
+[flake8]
+max-line-length = 120
diff --git a/tests/wpt/web-platform-tests/tools/tox.ini b/tests/wpt/web-platform-tests/tools/tox.ini
index 3b97ae4b5f7..b154c471d6f 100644
--- a/tests/wpt/web-platform-tests/tools/tox.ini
+++ b/tests/wpt/web-platform-tests/tools/tox.ini
@@ -21,4 +21,4 @@ passenv =
[flake8]
ignore = E128,E129,E221,E226,E231,E251,E265,E302,E303,E305,E402,E901,F401,F821,F841
max-line-length = 141
-exclude = .tox,html5lib,py,pytest,pywebsocket,six,_venv,webencodings,wptserve/docs,wptserve/tests/functional/docroot/,wpt,wptrunner
+exclude = .tox,html5lib,third_party/py,third_party/pytest,third_party/funcsigs,third_party/attrs,third_party/pluggy/,pywebsocket,six,_venv,webencodings,wptserve/docs,wptserve/tests/functional/docroot/,wpt,wptrunner
diff --git a/tests/wpt/web-platform-tests/tools/webdriver/webdriver/client.py b/tests/wpt/web-platform-tests/tools/webdriver/webdriver/client.py
index 26a035da776..ec7d6deccba 100644
--- a/tests/wpt/web-platform-tests/tools/webdriver/webdriver/client.py
+++ b/tests/wpt/web-platform-tests/tools/webdriver/webdriver/client.py
@@ -42,7 +42,7 @@ class Timeouts(object):
def _set(self, key, secs):
body = {key: secs * 1000}
timeouts = self.session.send_session_command("POST", "timeouts", body)
- return timeouts[key]
+ return None
@property
def script(self):
@@ -370,8 +370,8 @@ class Session(object):
self.actions = Actions(self)
def __eq__(self, other):
- return (self.session_id is not None and isinstance(other, Session)
- and self.session_Id == other.session_id)
+ return (self.session_id is not None and isinstance(other, Session) and
+ self.session_id == other.session_id)
def __enter__(self):
self.start()
@@ -509,6 +509,11 @@ class Session(object):
@property
@command
+ def source(self):
+ return self.send_session_command("GET", "source")
+
+ @property
+ @command
def window_handle(self):
return self.send_session_command("GET", "window")
@@ -626,8 +631,8 @@ class Element(object):
self.session._element_cache[self.id] = self
def __eq__(self, other):
- return isinstance(other, Element) and self.id == other.id \
- and self.session == other.session
+ return (isinstance(other, Element) and self.id == other.id and
+ self.session == other.session)
@classmethod
def from_json(cls, json, session):
diff --git a/tests/wpt/web-platform-tests/tools/webdriver/webdriver/error.py b/tests/wpt/web-platform-tests/tools/webdriver/webdriver/error.py
index 01ab31b8af1..ecfe8910c51 100644
--- a/tests/wpt/web-platform-tests/tools/webdriver/webdriver/error.py
+++ b/tests/wpt/web-platform-tests/tools/webdriver/webdriver/error.py
@@ -8,17 +8,25 @@ class WebDriverException(Exception):
def __init__(self, message, stacktrace=None):
super(WebDriverException, self)
+ self.message = message
self.stacktrace = stacktrace
def __repr__(self):
- return "<%s http_status=%d>" % (self.__class__.__name__, self.http_status)
+ return "<%s http_status=%s>" % (self.__class__.__name__, self.http_status)
def __str__(self):
- return ("%s (%d)\n"
- "\n"
+ message = "%s (%s): %s\n" % (self.status_code, self.http_status, self.message)
+ if self.stacktrace:
+ message += ("\n"
"Remote-end stacktrace:\n"
"\n"
- "%s" % (self.status_code, self.http_status, self.stacktrace))
+ "%s" % self.stacktrace)
+ return message
+
+
+class ElementClickInterceptedException(WebDriverException):
+ http_status = 400
+ status_code = "element click intercepted"
class ElementNotSelectableException(WebDriverException):
@@ -53,7 +61,7 @@ class InvalidElementCoordinatesException(WebDriverException):
class InvalidElementStateException(WebDriverException):
http_status = 400
- status_code = "invalid cookie domain"
+ status_code = "invalid element state"
class InvalidSelectorException(WebDriverException):
@@ -107,7 +115,7 @@ class SessionNotCreatedException(WebDriverException):
class StaleElementReferenceException(WebDriverException):
- http_status = 400
+ http_status = 404
status_code = "stale element reference"
diff --git a/tests/wpt/web-platform-tests/tools/webdriver/webdriver/transport.py b/tests/wpt/web-platform-tests/tools/webdriver/webdriver/transport.py
index ef8e59a6f82..b198b194b38 100644
--- a/tests/wpt/web-platform-tests/tools/webdriver/webdriver/transport.py
+++ b/tests/wpt/web-platform-tests/tools/webdriver/webdriver/transport.py
@@ -19,6 +19,7 @@ class Response(object):
self.body = body
def __repr__(self):
+ cls_name = self.__class__.__name__
if self.error:
return "<%s status=%s error=%s>" % (cls_name, self.status, repr(self.error))
return "<% status=%s body=%s>" % (cls_name, self.status, json.dumps(self.body))
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/OWNERS b/tests/wpt/web-platform-tests/tools/wptrunner/OWNERS
deleted file mode 100644
index 4bf48ad76e5..00000000000
--- a/tests/wpt/web-platform-tests/tools/wptrunner/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-@bobholt
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/docs/conf.py b/tests/wpt/web-platform-tests/tools/wptrunner/docs/conf.py
index 39e5cc4f0d2..0c717f56536 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/docs/conf.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/docs/conf.py
@@ -264,4 +264,4 @@ texinfo_documents = [
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'python': ('http://docs.python.org/', None),
- 'mozlog': ('http://mozbase.readthedocs.org/en/latest/', None)}
+ 'mozlog': ('https://firefox-source-docs.mozilla.org/', None)}
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/firefox.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/firefox.py
index 552cd15b459..b324bccd91e 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/firefox.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -51,7 +51,10 @@ def get_timeout_multiplier(test_type, run_info_data, **kwargs):
else:
return 2
elif run_info_data["debug"] or run_info_data.get("asan"):
- return 3
+ if run_info_data.get("ccov"):
+ return 4
+ else:
+ return 3
return 1
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/ie.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/ie.py
index c981024f22b..553372f390e 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/ie.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/ie.py
@@ -31,8 +31,6 @@ def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
ieOptions = {}
ieOptions["requireWindowFocus"] = True
capabilities = {}
- capabilities["browserName"] = "internet explorer"
- capabilities["platformName"] = "windows"
capabilities["se:ieOptions"] = ieOptions
executor_kwargs = base_executor_kwargs(test_type, server_config,
cache_manager, **kwargs)
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/servo.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/servo.py
index 3738ce4bddc..17fa59834fd 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/servo.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/servo.py
@@ -54,7 +54,7 @@ def env_extras(**kwargs):
def env_options():
return {"host": "127.0.0.1",
"external_host": "web-platform.test",
- "bind_hostname": "true",
+ "bind_hostname": "false",
"testharnessreport": "testharnessreport-servo.js",
"supports_debugger": True}
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/environment.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/environment.py
index e8734405e00..874595cbff3 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/environment.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/environment.py
@@ -37,8 +37,6 @@ def do_delayed_imports(logger, test_paths):
try:
from tools.serve import serve
except ImportError:
- from wpt_tools.serve import serve
- except ImportError:
failed.append("serve")
try:
@@ -86,7 +84,6 @@ class TestEnvironment(object):
self.ssl_env = ssl_env
self.server = None
self.config = None
- self.external_config = None
self.pause_after_test = pause_after_test
self.test_server_port = options.pop("test_server_port", True)
self.debug_info = debug_info
@@ -105,9 +102,10 @@ class TestEnvironment(object):
cm.__enter__(self.options)
self.setup_server_logging()
self.config = self.load_config()
- serve.set_computed_defaults(self.config)
- self.external_config, self.servers = serve.start(self.config, self.ssl_env,
- self.get_routes())
+ ports = serve.get_ports(self.config, self.ssl_env)
+ self.config = serve.normalise_config(self.config, ports)
+ self.servers = serve.start(self.config, self.ssl_env,
+ self.get_routes())
if self.options.get("supports_debugger") and self.debug_info and self.debug_info.interactive:
self.ignore_interrupts()
return self
@@ -160,6 +158,8 @@ class TestEnvironment(object):
config["key_file"] = key_file
config["certificate"] = certificate
+ serve.set_computed_defaults(config)
+
return config
def setup_server_logging(self):
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/base.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/base.py
index b33bc428ecb..d87e4ab9924 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/base.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/base.py
@@ -113,7 +113,7 @@ class TestExecutor(object):
:param browser: ExecutorBrowser instance providing properties of the
browser that will be tested.
:param server_config: Dictionary of wptserve server configuration of the
- form stored in TestEnvironment.external_config
+ form stored in TestEnvironment.config
:param timeout_multiplier: Multiplier relative to base timeout to use
when setting test timeout.
"""
@@ -357,12 +357,10 @@ class WdspecExecutor(TestExecutor):
return (test.result_cls(*data), [])
def do_wdspec(self, session_config, path, timeout):
- harness_result = ("OK", None)
- subtest_results = pytestrunner.run(path,
- self.server_config,
- session_config,
- timeout=timeout)
- return (harness_result, subtest_results)
+ return pytestrunner.run(path,
+ self.server_config,
+ session_config,
+ timeout=timeout)
def do_delayed_imports(self):
global pytestrunner
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py
index e2163bb0707..2d0dc914d10 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py
@@ -63,30 +63,26 @@ class MarionetteProtocol(Protocol):
self.marionette = marionette.Marionette(host='localhost',
port=self.marionette_port,
socket_timeout=None,
- startup_timeout=None)
-
- # XXX Move this timeout somewhere
- self.logger.debug("Waiting for Marionette connection")
- while True:
- success = self.marionette.wait_for_port(startup_timeout)
- #When running in a debugger wait indefinitely for firefox to start
- if success or self.executor.debug_info is None:
- break
-
- session_started = False
- if success:
- try:
- self.logger.debug("Starting Marionette session")
- self.marionette.start_session(capabilities=self.capabilities)
- except Exception as e:
- self.logger.warning("Starting marionette session failed: %s" % e)
- else:
- self.logger.debug("Marionette session started")
- session_started = True
+ startup_timeout=startup_timeout)
+ try:
+ self.logger.debug("Waiting for Marionette connection")
+ while True:
+ try:
+ self.marionette.raise_for_port()
+ break
+ except IOError:
+ # When running in a debugger wait indefinitely for Firefox to start
+ if self.executor.debug_info is None:
+ raise
+
+ self.logger.debug("Starting Marionette session")
+ self.marionette.start_session()
+ self.logger.debug("Marionette session started")
- if not success or not session_started:
- self.logger.warning("Failed to connect to Marionette")
+ except Exception as e:
+ self.logger.warning("Failed to start a Marionette session: %s" % e)
self.executor.runner.send_message("init_failed")
+
else:
try:
self.after_connect()
@@ -173,9 +169,17 @@ class MarionetteProtocol(Protocol):
self.load_runner(protocol)
def wait(self):
- socket_timeout = self.marionette.client.sock.gettimeout()
+ try:
+ socket_timeout = self.marionette.client.socket_timeout
+ except AttributeError:
+ # This can happen if there was a crash
+ return
if socket_timeout:
- self.marionette.timeout.script = socket_timeout / 2
+ try:
+ self.marionette.timeout.script = socket_timeout / 2
+ except (socket.error, IOError):
+ self.logger.debug("Socket closed")
+ return
self.marionette.switch_to_window(self.runner_handle)
while True:
@@ -564,7 +568,7 @@ class InternalRefTestImplementation(object):
self.executor.protocol.marionette.set_context(self.executor.protocol.marionette.CONTEXT_CONTENT)
except Exception as e:
# Ignore errors during teardown
- self.logger.warning(traceback.traceback.format_exc(e))
+ self.logger.warning(traceback.format_exc(e))
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py
index 611a4989c39..0870b1232ad 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py
@@ -1,4 +1,5 @@
-"""Provides interface to deal with pytest.
+"""
+Provides interface to deal with pytest.
Usage::
@@ -24,7 +25,8 @@ def do_delayed_imports():
def run(path, server_config, session_config, timeout=0):
- """Run Python test at ``path`` in pytest. The provided ``session``
+ """
+ Run Python test at ``path`` in pytest. The provided ``session``
is exposed as a fixture available in the scope of the test functions.
:param path: Path to the test file.
@@ -33,36 +35,51 @@ def run(path, server_config, session_config, timeout=0):
:param timeout: Duration before interrupting potentially hanging
tests. If 0, there is no timeout.
- :returns: List of subtest results, which are tuples of (test id,
- status, message, stacktrace).
+ :returns: (<harness result>, [<subtest result>, ...]),
+ where <subtest result> is (test id, status, message, stacktrace).
"""
-
if pytest is None:
do_delayed_imports()
- recorder = SubtestResultRecorder()
-
os.environ["WD_HOST"] = session_config["host"]
os.environ["WD_PORT"] = str(session_config["port"])
os.environ["WD_CAPABILITIES"] = json.dumps(session_config["capabilities"])
os.environ["WD_SERVER_CONFIG"] = json.dumps(server_config)
- plugins = [recorder]
-
- # TODO(ato): Deal with timeouts
+ harness = HarnessResultRecorder()
+ subtests = SubtestResultRecorder()
with TemporaryDirectory() as cache:
- pytest.main(["--strict", # turn warnings into errors
- "--verbose", # show each individual subtest
- "--capture", "no", # enable stdout/stderr from tests
- "--basetemp", cache, # temporary directory
- "--showlocals", # display contents of variables in local scope
- "-p", "no:mozlog", # use the WPT result recorder
- "-p", "no:cacheprovider", # disable state preservation across invocations
- path],
- plugins=plugins)
+ try:
+ pytest.main(["--strict", # turn warnings into errors
+ "--verbose", # show each individual subtest
+ "--capture", "no", # enable stdout/stderr from tests
+ "--basetemp", cache, # temporary directory
+ "--showlocals", # display contents of variables in local scope
+ "-p", "no:mozlog", # use the WPT result recorder
+ "-p", "no:cacheprovider", # disable state preservation across invocations
+ path],
+ plugins=[harness, subtests])
+ except Exception as e:
+ harness.outcome = ("ERROR", str(e))
+
+ return (harness.outcome, subtests.results)
+
+
+class HarnessResultRecorder(object):
+ outcomes = {
+ "failed": "ERROR",
+ "passed": "OK",
+ "skipped": "SKIP",
+ }
+
+ def __init__(self):
+ # we are ok unless told otherwise
+ self.outcome = ("OK", None)
- return recorder.results
+ def pytest_collectreport(self, report):
+ harness_result = self.outcomes[report.outcome]
+ self.outcome = (harness_result, None)
class SubtestResultRecorder(object):
@@ -100,7 +117,7 @@ class SubtestResultRecorder(object):
def record(self, test, status, message=None, stack=None):
if stack is not None:
stack = str(stack)
- new_result = (test, status, message, stack)
+ new_result = (test.split("::")[-1], status, message, stack)
self.results.append(new_result)
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/stability.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/stability.py
index dc1a1d0fed0..6eb060485c2 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/stability.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/stability.py
@@ -1,14 +1,14 @@
import copy
import functools
import imp
+import io
import os
-import sys
from collections import OrderedDict, defaultdict
from datetime import datetime
from mozlog import reader
-from mozlog.formatters import JSONFormatter, TbplFormatter
-from mozlog.handlers import BaseHandler, LogLevelFilter, StreamHandler
+from mozlog.formatters import JSONFormatter
+from mozlog.handlers import BaseHandler, StreamHandler, LogLevelFilter
here = os.path.dirname(__file__)
localpaths = imp.load_source("localpaths", os.path.abspath(os.path.join(here, os.pardir, os.pardir, "localpaths.py")))
@@ -86,6 +86,8 @@ class LogHandler(reader.LogHandler):
def is_inconsistent(results_dict, iterations):
"""Return whether or not a single test is inconsistent."""
+ if 'SKIP' in results_dict:
+ return False
return len(results_dict) > 1 or sum(results_dict.values()) != iterations
@@ -178,31 +180,29 @@ def run_step(logger, iterations, restart_after_iteration, kwargs_extras, **kwarg
kwargs["pause_after_test"] = False
kwargs.update(kwargs_extras)
- handler = LogActionFilter(
- LogLevelFilter(
- StreamHandler(
- sys.stdout,
- TbplFormatter()
- ),
- "WARNING"),
- ["log", "process_output"])
+ def wrap_handler(x):
+ x = LogLevelFilter(x, "WARNING")
+ if not kwargs["verify_log_full"]:
+ x = LogActionFilter(x, ["log", "process_output"])
+ return x
- # There is a public API for this in the next mozlog
initial_handlers = logger._state.handlers
- logger._state.handlers = []
+ logger._state.handlers = [wrap_handler(handler)
+ for handler in initial_handlers]
- with open("raw.log", "wb") as log:
- # Setup logging for wptrunner that keeps process output and
- # warning+ level logs only
- logger.add_handler(handler)
- logger.add_handler(StreamHandler(log, JSONFormatter()))
+ log = io.BytesIO()
+ # Setup logging for wptrunner that keeps process output and
+ # warning+ level logs only
+ logger.add_handler(StreamHandler(log, JSONFormatter()))
- wptrunner.run_tests(**kwargs)
+ wptrunner.run_tests(**kwargs)
logger._state.handlers = initial_handlers
+ logger._state.running_tests = set()
+ logger._state.suite_started = False
- with open("raw.log", "rb") as log:
- results, inconsistent = process_results(log, iterations)
+ log.seek(0)
+ results, inconsistent = process_results(log, iterations)
return results, inconsistent, iterations
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testdriver-vendor.js b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testdriver-vendor.js
new file mode 100644
index 00000000000..3e884036363
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testdriver-vendor.js
@@ -0,0 +1 @@
+// This file intentionally left blank
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testloader.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testloader.py
index 71c55232580..0eb78bb884f 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testloader.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testloader.py
@@ -460,7 +460,8 @@ class TestLoader(object):
chunk_type="none",
total_chunks=1,
chunk_number=1,
- include_https=True):
+ include_https=True,
+ skip_timeout=False):
self.test_types = test_types
self.run_info = run_info
@@ -472,6 +473,7 @@ class TestLoader(object):
self.tests = None
self.disabled_tests = None
self.include_https = include_https
+ self.skip_timeout = skip_timeout
self.chunk_type = chunk_type
self.total_chunks = total_chunks
@@ -557,6 +559,8 @@ class TestLoader(object):
enabled = not test.disabled()
if not self.include_https and test.environment["protocol"] == "https":
enabled = False
+ if self.skip_timeout and test.expected() == "TIMEOUT":
+ enabled = False
key = "enabled" if enabled else "disabled"
tests[key][test_type].append(test)
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testrunner.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testrunner.py
index 64418285e66..75f4825f952 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testrunner.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/testrunner.py
@@ -120,6 +120,13 @@ def start_runner(runner_command_queue, runner_result_queue,
executor_browser_cls, executor_browser_kwargs,
stop_flag):
"""Launch a TestRunner in a new process"""
+ def log(level, msg):
+ runner_result_queue.put(("log", (level, {"message": msg})))
+
+ def handle_error(e):
+ log("critical", traceback.format_exc())
+ stop_flag.set()
+
try:
browser = executor_browser_cls(**executor_browser_kwargs)
executor = executor_cls(browser, **executor_kwargs)
@@ -128,10 +135,10 @@ def start_runner(runner_command_queue, runner_result_queue,
runner.run()
except KeyboardInterrupt:
stop_flag.set()
- except Exception:
- runner_result_queue.put(("log", ("critical", {"message": traceback.format_exc()})))
- print >> sys.stderr, traceback.format_exc()
- stop_flag.set()
+ except Exception as e:
+ handle_error(e)
+ except Exception as e:
+ handle_error(e)
finally:
runner_command_queue = None
runner_result_queue = None
@@ -389,6 +396,7 @@ class TestRunnerManager(threading.Thread):
}
try:
command, data = self.command_queue.get(True, 1)
+ self.logger.debug("Got command: %r" % command)
except IOError:
self.logger.error("Got IOError from poll")
return RunnerManagerState.restarting(0)
@@ -676,7 +684,18 @@ class TestRunnerManager(threading.Thread):
self.browser.cleanup()
while True:
try:
- self.logger.warning(" ".join(map(repr, self.command_queue.get_nowait())))
+ cmd, data = self.command_queue.get_nowait()
+ except Empty:
+ break
+ else:
+ if cmd == "log":
+ self.log(*data)
+ else:
+ self.logger.warning("%r: %r" % (cmd, data))
+ while True:
+ try:
+ cmd, data = self.remote_queue.get_nowait()
+ self.logger.warning("%r: %r" % (cmd, data))
except Empty:
break
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/update/sync.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/update/sync.py
index 40bd1d7ebb7..c1bff854349 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/update/sync.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/update/sync.py
@@ -81,7 +81,6 @@ def copy_wpt_tree(tree, dest, excludes=None, includes=None):
shutil.copy2(source_path, dest_path)
for source, destination in [("testharness_runner.html", ""),
- ("testharnessreport.js", "resources/"),
("testdriver-vendor.js", "resources/")]:
source_path = os.path.join(here, os.pardir, source)
dest_path = os.path.join(dest, destination, os.path.split(source)[1])
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/update/update.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/update/update.py
index d9ef40fcda8..c985d3513ac 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/update/update.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/update/update.py
@@ -11,10 +11,7 @@ from state import State
def setup_paths(sync_path):
sys.path.insert(0, os.path.abspath(sync_path))
- try:
- from tools import localpaths
- except ImportError:
- from wpt_tools import localpaths
+ from tools import localpaths
class LoadConfig(Step):
"""Step for loading configuration from the ini file and kwargs."""
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py
index b9f7a4c34a5..b232462d915 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py
@@ -79,6 +79,9 @@ scheme host and port.""")
mode_group.add_argument("--verify", action="store_true",
default=False,
help="Run a stability check on the selected tests")
+ mode_group.add_argument("--verify-log-full", action="store_true",
+ default=False,
+ help="Output per-iteration test results when running verify")
test_selection_group = parser.add_argument_group("Test Selection")
test_selection_group.add_argument("--test-types", action="store",
@@ -91,6 +94,8 @@ scheme host and port.""")
help="URL prefix to exclude")
test_selection_group.add_argument("--include-manifest", type=abs_path,
help="Path to manifest listing tests to include")
+ test_selection_group.add_argument("--skip-timeout", action="store_true",
+ help="Skip tests that are expected to time out")
test_selection_group.add_argument("--tag", action="append", dest="tags",
help="Labels applied to tests to include in the run. Labels starting dir: are equivalent to top-level directories.")
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptlogging.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptlogging.py
index 4d320617859..26d174c066b 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptlogging.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptlogging.py
@@ -1,13 +1,20 @@
import logging
import sys
import threading
+from Queue import Empty
from StringIO import StringIO
from multiprocessing import Queue
-from mozlog import commandline, stdadapter
+from mozlog import commandline, stdadapter, set_default_logger
+from mozlog.structuredlog import StructuredLogger
def setup(args, defaults):
- logger = commandline.setup_logging("web-platform-tests", args, defaults)
+ logger = args.pop('log', None)
+ if logger:
+ set_default_logger(logger)
+ StructuredLogger._logger_states["web-platform-tests"] = logger._state
+ else:
+ logger = commandline.setup_logging("web-platform-tests", args, defaults)
setup_stdlib_logger()
for name in args.keys():
@@ -45,7 +52,6 @@ class LogLevelRewriter(object):
return self.inner(data)
-
class LogThread(threading.Thread):
def __init__(self, queue, logger, level):
self.queue = queue
@@ -120,5 +126,10 @@ class CaptureIO(object):
self.logging_queue.put(None)
if self.logging_thread is not None:
self.logging_thread.join(10)
+ while not self.logging_queue.empty():
+ try:
+ self.logger.warning("Dropping log message: %r", self.logging_queue.get())
+ except Exception:
+ pass
self.logging_queue.close()
self.logger.info("queue closed")
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptrunner.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptrunner.py
index f34698a6380..637cbe57b6f 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptrunner.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptrunner.py
@@ -38,6 +38,7 @@ def setup_logging(*args, **kwargs):
global logger
logger = wptlogging.setup(*args, **kwargs)
+
def get_loader(test_paths, product, ssl_env, debug=None, run_info_extras=None, **kwargs):
if run_info_extras is None:
run_info_extras = {}
@@ -67,9 +68,11 @@ def get_loader(test_paths, product, ssl_env, debug=None, run_info_extras=None, *
chunk_type=kwargs["chunk_type"],
total_chunks=kwargs["total_chunks"],
chunk_number=kwargs["this_chunk"],
- include_https=ssl_env.ssl_enabled)
+ include_https=ssl_env.ssl_enabled,
+ skip_timeout=kwargs["skip_timeout"])
return run_info, test_loader
+
def list_test_groups(test_paths, product, **kwargs):
env.do_delayed_imports(logger, test_paths)
@@ -198,7 +201,7 @@ def run_tests(config, test_paths, product, **kwargs):
logger.info("Repetition %i / %i" % (repeat_count, repeat))
unexpected_count = 0
- logger.suite_start(test_loader.test_ids, run_info)
+ logger.suite_start(test_loader.test_ids, name='web-platform-test', run_info=run_info)
for test_type in kwargs["test_types"]:
logger.info("Running %s tests" % test_type)
@@ -217,10 +220,9 @@ def run_tests(config, test_paths, product, **kwargs):
ssl_env=ssl_env,
**kwargs)
-
executor_cls = executor_classes.get(test_type)
executor_kwargs = get_executor_kwargs(test_type,
- test_environment.external_config,
+ test_environment.config,
test_environment.cache_manager,
run_info,
**kwargs)
diff --git a/tests/wpt/web-platform-tests/tools/wptserve/docs/pipes.rst b/tests/wpt/web-platform-tests/tools/wptserve/docs/pipes.rst
index c606140d474..39e98ab4f5e 100644
--- a/tests/wpt/web-platform-tests/tools/wptserve/docs/pipes.rst
+++ b/tests/wpt/web-platform-tests/tools/wptserve/docs/pipes.rst
@@ -11,6 +11,10 @@ functions are applied to the response from left to right. For example::
This would serve bytes 1 to 199, inclusive, of foo.txt with the HTTP status
code 404.
+.. note::
+ Pipes are only applied to static files, and will not work if applied to
+ other types of handlers, such as Python File Handlers.
+
There are several built-in pipe functions, and it is possible to add
more using the `@pipe` decorator on a function, if required.
diff --git a/tests/wpt/web-platform-tests/tools/wptserve/tests/functional/test_pipes.py b/tests/wpt/web-platform-tests/tools/wptserve/tests/functional/test_pipes.py
index 95da70faea9..bd38f2ef18f 100644
--- a/tests/wpt/web-platform-tests/tools/wptserve/tests/functional/test_pipes.py
+++ b/tests/wpt/web-platform-tests/tools/wptserve/tests/functional/test_pipes.py
@@ -76,5 +76,11 @@ class TestTrickle(TestUsingServer):
self.assertEqual(resp.read(), expected)
self.assertGreater(6, t1-t0)
+ def test_headers(self):
+ resp = self.request("/document.txt", query="pipe=trickle(d0.01)")
+ self.assertEqual(resp.info()["Cache-Control"], "no-cache, no-store, must-revalidate")
+ self.assertEqual(resp.info()["Pragma"], "no-cache")
+ self.assertEqual(resp.info()["Expires"], "0")
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/wpt/web-platform-tests/tools/wptserve/wptserve/pipes.py b/tests/wpt/web-platform-tests/tools/wptserve/wptserve/pipes.py
index 95108ba7e93..7203815b70c 100644
--- a/tests/wpt/web-platform-tests/tools/wptserve/wptserve/pipes.py
+++ b/tests/wpt/web-platform-tests/tools/wptserve/wptserve/pipes.py
@@ -231,6 +231,13 @@ def trickle(request, response, delays):
content = resolve_content(response)
offset = [0]
+ if not ("Cache-Control" in response.headers or
+ "Pragma" in response.headers or
+ "Expires" in response.headers):
+ response.headers.set("Cache-Control", "no-cache, no-store, must-revalidate")
+ response.headers.set("Pragma", "no-cache")
+ response.headers.set("Expires", "0")
+
def add_content(delays, repeat=False):
for i, (item_type, value) in enumerate(delays):
if item_type == "bytes":
diff --git a/tests/wpt/web-platform-tests/tools/wptserve/wptserve/server.py b/tests/wpt/web-platform-tests/tools/wptserve/wptserve/server.py
index 0ab512cb2a5..2a83bd8097e 100644
--- a/tests/wpt/web-platform-tests/tools/wptserve/wptserve/server.py
+++ b/tests/wpt/web-platform-tests/tools/wptserve/wptserve/server.py
@@ -418,7 +418,8 @@ class WebTestHttpd(object):
_host, self.port = self.httpd.socket.getsockname()
except Exception:
- self.logger.error('Init failed! You may need to modify your hosts file. Refer to README.md.')
+ self.logger.error("Failed to start HTTP server. "
+ "You may need to edit /etc/hosts or similar, see README.md.")
raise
def start(self, block=False):