diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index a8b2c9e75..bc62e8a3f 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ 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 +- [ ] 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/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 00673625f..d09edce43 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,7 +2,7 @@ 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) +- [ ] 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/AUTHORS b/AUTHORS index 42bdf81b1..5ed55dc94 100644 --- a/AUTHORS +++ b/AUTHORS @@ -10,6 +10,7 @@ Andy Freeland Anthon van der Neut Armin Rigo Aron Curzon +Aviv Palivoda Benjamin Peterson Bob Ippolito Brian Dorsey @@ -23,6 +24,7 @@ Christian Theunert Christian Tismer Christopher Gilling Daniel Grana +Daniel Hahler Daniel Nuri Dave Hunt David Mohr @@ -60,6 +62,7 @@ Marc Schlaich Mark Abramowitz Markus Unterwaditzer Martijn Faassen +Matt Bachmann Matt Williams Michael Aquilina Michael Birtwell diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c1f573f18..0a492c6b4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -40,7 +40,7 @@ .. _#1454: https://github.com/pytest-dev/pytest/pull/1454 -2.9.1.dev1 +2.9.2.dev1 ========== **Bug Fixes** @@ -51,6 +51,43 @@ * +* + + +2.9.1 +===== + +**Bug Fixes** + +* Improve error message when a plugin fails to load. + Thanks `@nicoddemus`_ for the PR. + +* Fix (`#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 `_): 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 ===== diff --git a/_pytest/__init__.py b/_pytest/__init__.py index 79942e374..fcbd2cde7 100644 --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,3 @@ # + __version__ = '2.10.0.dev1' diff --git a/_pytest/_code/_py2traceback.py b/_pytest/_code/_py2traceback.py index d65e27cb7..a830d9899 100644 --- a/_pytest/_code/_py2traceback.py +++ b/_pytest/_code/_py2traceback.py @@ -47,7 +47,9 @@ def format_exception_only(etype, value): filename = filename or "" lines.append(' File "%s", line %d\n' % (filename, lineno)) if badline is not None: - lines.append(' %s\n' % badline.strip()) + 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 diff --git a/_pytest/cacheprovider.py b/_pytest/cacheprovider.py index e5c11a878..0657001f2 100755 --- a/_pytest/cacheprovider.py +++ b/_pytest/cacheprovider.py @@ -149,7 +149,9 @@ class LFPlugin: config = self.config if config.getvalue("cacheshow") or hasattr(config, "slaveinput"): return - config.cache.set("cache/lastfailed", self.lastfailed) + 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): diff --git a/_pytest/config.py b/_pytest/config.py index 70e19b1d1..fb7b1774f 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -383,8 +383,13 @@ class PytestPluginManager(PluginManager): importspec = modname try: __import__(importspec) - except ImportError: - raise + 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): diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index 224b7971c..60e9b47d2 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -28,17 +28,14 @@ def pytest_plugin_registered(plugin, manager): @hookspec(historic=True) def pytest_addoption(parser): - """register argparse-style options and ini-style config values. + """register argparse-style options and ini-style config values, + called once at the beginning of a test run. - .. warning:: + .. note:: - This function must be implemented in a :ref:`plugin ` - and is called once at the beginning of a test run. - - Implementing this hook from ``conftest.py`` files is **strongly** - discouraged because ``conftest.py`` files are lazily loaded and - may give strange *unknown option* errors depending on the directory - ``py.test`` is invoked from. + 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 `. :arg parser: To add command line options, call :py:func:`parser.addoption(...) <_pytest.config.Parser.addoption>`. @@ -84,7 +81,7 @@ 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(args, early_config, parser): +def pytest_load_initial_conftests(early_config, parser, args): """ implements the loading of initial conftest files ahead of command line option parsing. """ diff --git a/_pytest/junitxml.py b/_pytest/junitxml.py index 168faaf13..56400892a 100644 --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -46,6 +46,8 @@ 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): @@ -89,7 +91,7 @@ class _NodeReporter(object): def record_testreport(self, testreport): assert not self.testcase - names = mangle_testnames(testreport.nodeid.split("::")) + names = mangle_test_address(testreport.nodeid) classnames = names[:-1] if self.xml.prefix: classnames.insert(0, self.xml.prefix) @@ -235,9 +237,18 @@ def pytest_unconfigure(config): config.pluginmanager.unregister(xml) -def mangle_testnames(names): - names = [x.replace(".py", "") for x in names if x != '()'] +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 diff --git a/_pytest/main.py b/_pytest/main.py index 70d6896cb..8654d7af6 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -718,7 +718,8 @@ class Session(FSCollector): if rep.passed: has_matched = False for x in rep.result: - if x.name == name: + # 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 diff --git a/_pytest/python.py b/_pytest/python.py index 2e6c8d87e..68a13224b 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -751,7 +751,7 @@ class FunctionMixin(PyobjMixin): def _repr_failure_py(self, excinfo, style="long"): if excinfo.errisinstance(pytest.fail.Exception): if not excinfo.value.pytrace: - return str(excinfo.value) + return py._builtin._totext(excinfo.value) return super(FunctionMixin, self)._repr_failure_py(excinfo, style=style) @@ -1126,7 +1126,7 @@ def _idval(val, argname, idx, idfn): elif isinstance(val, (float, int, str, bool, NoneType)): return str(val) elif isinstance(val, REGEX_TYPE): - return val.pattern + 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__'): diff --git a/_pytest/runner.py b/_pytest/runner.py index a50c2d738..cde94c8c8 100644 --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -435,7 +435,10 @@ class OutcomeException(Exception): def __repr__(self): if self.msg: - return str(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__ diff --git a/doc/en/announce/release-2.9.1.rst b/doc/en/announce/release-2.9.1.rst new file mode 100644 index 000000000..05a448430 --- /dev/null +++ b/doc/en/announce/release-2.9.1.rst @@ -0,0 +1,65 @@ +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 `_): + ``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 `_): 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/doc/en/assert.rst b/doc/en/assert.rst index d83383da6..e7f14e8bd 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -26,7 +26,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -143,7 +143,7 @@ if you run this module:: $ py.test test_assert2.py ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/cache.rst b/doc/en/cache.rst index 33e4c700d..52abb52a0 100644 --- a/doc/en/cache.rst +++ b/doc/en/cache.rst @@ -80,7 +80,7 @@ If you then run it with ``--lf``:: $ py.test --lf ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -121,7 +121,7 @@ of ``FF`` and dots):: $ py.test --ff ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -226,7 +226,7 @@ You can always peek at the content of the cache using the $ py.test --cache-clear ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/capture.rst b/doc/en/capture.rst index 8e696a8d7..8892f5be7 100644 --- a/doc/en/capture.rst +++ b/doc/en/capture.rst @@ -64,7 +64,7 @@ of the failing function and hide the other one:: $ py.test ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/doctest.rst b/doc/en/doctest.rst index 64b5621d4..9569b1dbc 100644 --- a/doc/en/doctest.rst +++ b/doc/en/doctest.rst @@ -49,7 +49,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index 85a57b9b8..6bdc60347 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -31,7 +31,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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 @@ -45,7 +45,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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 @@ -66,7 +66,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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 @@ -79,7 +79,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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 @@ -92,7 +92,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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 @@ -130,7 +130,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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 @@ -144,7 +144,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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 @@ -160,7 +160,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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 @@ -350,7 +350,7 @@ the test needs:: $ py.test -E stage2 ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -362,7 +362,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -481,7 +481,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -495,7 +495,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -547,7 +547,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -569,7 +569,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/example/nonpython.rst b/doc/en/example/nonpython.rst index 01eca5ec7..6437e3984 100644 --- a/doc/en/example/nonpython.rst +++ b/doc/en/example/nonpython.rst @@ -27,7 +27,7 @@ now execute the test specification:: nonpython $ py.test test_simple.yml ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -59,7 +59,7 @@ consulted when reporting in ``verbose`` mode:: nonpython $ py.test -v ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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 @@ -81,7 +81,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 12062e650..5d637ffcb 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -130,7 +130,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -181,7 +181,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -194,7 +194,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia $ py.test --collect-only test_scenarios.py ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -259,7 +259,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -320,7 +320,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -399,8 +399,8 @@ Running it results in some skips if we don't have all the python interpreters in . $ py.test -rs -q multipython.py ssssssssssss...ssssssssssss ======= short test summary info ======== - SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:23: 'python2.6' not found 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 @@ -448,7 +448,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst index 6305aae31..5faf4c6c8 100644 --- a/doc/en/example/pythoncollection.rst +++ b/doc/en/example/pythoncollection.rst @@ -82,7 +82,7 @@ then the test collection looks like this:: $ py.test --collect-only ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -128,7 +128,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -182,7 +182,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index db35c2a73..28624aa07 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -13,7 +13,7 @@ get on the terminal - we are working on that): assertion $ py.test failure_demo.py ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 8bddb1348..be12d2afe 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -108,7 +108,7 @@ directory with the above conftest.py:: $ py.test ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -156,7 +156,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -170,7 +170,7 @@ Or run it including the ``slow`` marked test:: $ py.test --runslow ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -262,7 +262,7 @@ which will add the string to the test header accordingly:: $ py.test ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -286,7 +286,7 @@ which will add info only when run with "--v":: $ py.test -v ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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? @@ -299,7 +299,7 @@ and nothing when run plainly:: $ py.test ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -332,7 +332,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -394,7 +394,7 @@ If we run this:: $ py.test -rx ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -465,7 +465,7 @@ We can run this:: $ py.test ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -479,7 +479,7 @@ We can run this:: 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: cache, tmpdir_factory, capsys, pytestconfig, capfd, record_xml_property, recwarn, tmpdir, monkeypatch + 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 @@ -569,7 +569,7 @@ and run them:: $ py.test test_module.py ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -660,7 +660,7 @@ and run it:: $ py.test -s test_module.py ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 6f7794eb8..f48607ae2 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -75,7 +75,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -193,7 +193,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -480,7 +480,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -531,7 +531,7 @@ Here we declare an ``app`` fixture which receives the previously defined $ py.test -v test_appsetup.py ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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 @@ -597,7 +597,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4 + 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 diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 6ab8638b9..4a5b75aea 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -27,7 +27,7 @@ Installation options:: To check your installation has installed the correct version:: $ py.test --version - This is pytest version 2.9.0, imported from $PYTHON_PREFIX/lib/python3.4/site-packages/pytest.py + 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`. @@ -49,7 +49,7 @@ That's it. You can execute the test function now:: $ py.test ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/parametrize.rst b/doc/en/parametrize.rst index 66449fbdd..919ac93d2 100644 --- a/doc/en/parametrize.rst +++ b/doc/en/parametrize.rst @@ -55,7 +55,7 @@ them in turn:: $ py.test ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 @@ -103,7 +103,7 @@ Let's run this:: $ py.test ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/skipping.rst b/doc/en/skipping.rst index cd8f5d8a4..4282afb77 100644 --- a/doc/en/skipping.rst +++ b/doc/en/skipping.rst @@ -224,7 +224,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/talks.rst b/doc/en/talks.rst index 8ae5d74a1..7a5221845 100644 --- a/doc/en/talks.rst +++ b/doc/en/talks.rst @@ -4,7 +4,7 @@ Talks and Tutorials .. sidebar:: Next Open Trainings - `professional testing with pytest and tox `_, 24-26th November 2014, Freiburg, Germany + `professional testing with pytest and tox `_, 27-29th June 2016, Freiburg, Germany .. _`funcargs`: funcargs.html @@ -14,6 +14,9 @@ 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 + `_. + - `Improve your testing with Pytest and Mock, Gabe Hollombe, PyCon SG 2015 `_. diff --git a/doc/en/tmpdir.rst b/doc/en/tmpdir.rst index 21497d8ea..f8935b8ce 100644 --- a/doc/en/tmpdir.rst +++ b/doc/en/tmpdir.rst @@ -29,7 +29,7 @@ Running this would result in a passed test except for the last $ py.test test_tmpdir.py ======= test session starts ======== - platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/doc/en/unittest.rst b/doc/en/unittest.rst index f1ef1b2d9..ce99bd118 100644 --- a/doc/en/unittest.rst +++ b/doc/en/unittest.rst @@ -88,7 +88,7 @@ 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.0, py-1.4.31, pluggy-0.3.1 + 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 diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 74db4425d..9bc3a191a 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -392,6 +392,20 @@ class TestGeneralUsage: 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): diff --git a/testing/code/test_code.py b/testing/code/test_code.py index e9c7f7ab1..0db4ad2ab 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -93,6 +93,17 @@ def test_unicode_handling(): 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 diff --git a/testing/python/collect.py b/testing/python/collect.py index 7ba2574f2..22433da77 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import sys from textwrap import dedent @@ -1181,3 +1182,19 @@ def test_class_injection_does_not_break_collection(testdir): 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/testing/python/metafunc.py b/testing/python/metafunc.py index 21869cff9..faa687f40 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -170,6 +170,11 @@ class TestMetafunc: 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 diff --git a/testing/test_cache.py b/testing/test_cache.py index 75557af38..98053f869 100755 --- a/testing/test_cache.py +++ b/testing/test_cache.py @@ -46,12 +46,12 @@ class TestNewAPI: def test_cache_failure_warns(self, testdir): testdir.tmpdir.ensure_dir('.cache').chmod(0) testdir.makepyfile(""" - def test_pass(): - pass + def test_error(): + raise Exception """) result = testdir.runpytest('-rw') - assert result.ret == 0 + assert result.ret == 1 result.stdout.fnmatch_lines([ "*could not create cache path*", "*1 pytest-warnings*", @@ -266,7 +266,7 @@ class TestLastFailed: """) config = testdir.parseconfigure() lastfailed = config.cache.get("cache/lastfailed", -1) - assert not lastfailed + assert lastfailed == -1 def test_non_serializable_parametrize(self, testdir): """Test that failed parametrized tests with unmarshable parameters @@ -305,7 +305,7 @@ class TestLastFailed: return lastfailed lastfailed = rlf(fail_import=0, fail_run=0) - assert not lastfailed + assert lastfailed == -1 lastfailed = rlf(fail_import=1, fail_run=0) assert list(lastfailed) == ['test_maybe.py'] @@ -347,7 +347,7 @@ class TestLastFailed: return result, lastfailed result, lastfailed = rlf(fail_import=0, fail_run=0) - assert not lastfailed + assert lastfailed == -1 result.stdout.fnmatch_lines([ '*3 passed*', ]) @@ -370,3 +370,17 @@ class TestLastFailed: 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/testing/test_junitxml.py b/testing/test_junitxml.py index 8ff1028df..e84734dfa 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -449,11 +449,12 @@ class TestPython: assert "hello-stderr" in systemout.toxml() -def test_mangle_testnames(): - from _pytest.junitxml import mangle_testnames - names = ["a/pything.py", "Class", "()", "method"] - newnames = mangle_testnames(names) - assert newnames == ["a.pything", "Class", "method"] +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): @@ -619,6 +620,36 @@ def test_escaped_parametrized_names_xml(testdir): 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) diff --git a/testing/test_mark.py b/testing/test_mark.py index 1795928f0..aa1be6f7c 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -269,6 +269,22 @@ def test_keyword_option_parametrize(spec, testdir): 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): diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 97daf2eae..36847638d 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -178,13 +178,17 @@ def test_default_markers(testdir): "*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") - assert "qwe" not in str(excinfo.value) - assert "aaaa" in str(excinfo.value) + expected = '.*Error importing plugin "qwe": No module named \'?aaaa\'?' + assert py.std.re.match(expected, str(excinfo.value)) class TestPytestPluginManager: diff --git a/testing/test_runner.py b/testing/test_runner.py index c3c415e0f..4421c5d0d 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from __future__ import with_statement import _pytest._code @@ -439,6 +440,27 @@ def test_pytest_fail_notrace(testdir): 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*')