Merge remote-tracking branch 'upstream/master' into merge-master-into-features

This commit is contained in:
Bruno Oliveira 2016-08-06 17:58:17 -03:00
commit 802755ceed
22 changed files with 183 additions and 186 deletions

View File

@ -25,7 +25,7 @@ env:
- TESTENV=py35-trial
- TESTENV=py27-nobyte
- TESTENV=doctesting
- TESTENV=py27-cxfreeze
- TESTENV=freeze
script: tox --recreate -e $TESTENV

View File

@ -254,6 +254,15 @@ time or change existing behaviors in order to make them less surprising/more use
* ``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`_.
Thanks to `@blueyed`_ and `@davehunt`_ 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.
* Renamed the pytest ``pdb`` module (plugin) into ``debugging``.
* Better message in case of not using parametrized variable (see `#1539`_).
@ -322,11 +331,13 @@ time or change existing behaviors in order to make them less surprising/more use
.. _#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
@ -341,6 +352,7 @@ time or change existing behaviors in order to make them less surprising/more use
.. _#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
@ -367,6 +379,7 @@ time or change existing behaviors in order to make them less surprising/more use
.. _#717: https://github.com/pytest-dev/pytest/issues/717
.. _#925: https://github.com/pytest-dev/pytest/issues/925
.. _@anntzer: https://github.com/anntzer
.. _@bagerard: https://github.com/bagerard
.. _@BeyondEvil: https://github.com/BeyondEvil
@ -397,8 +410,8 @@ time or change existing behaviors in order to make them less surprising/more use
.. _@RedBeardCode: https://github.com/RedBeardCode
.. _@sallner: https://github.com/sallner
.. _@sober7: https://github.com/sober7
.. _@suzaku: https://github.com/suzaku
.. _@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

View File

@ -1,5 +1,5 @@
.. image:: http://pytest.org/latest/_static/pytest1.png
:target: http://pytest.org
.. image:: http://docs.pytest.org/en/latest/_static/pytest1.png
:target: http://docs.pytest.org
:align: center
:alt: pytest
@ -51,33 +51,34 @@ To execute it::
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 `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples.
Due to ``py.test``'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://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
- Detailed info on failing `assert statements <http://docs.pytest.org/en/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
- `Auto-discovery
<http://pytest.org/latest/goodpractices.html#python-test-discovery>`_
<http://docs.pytest.org/en/latest/goodpractices.html#python-test-discovery>`_
of test modules and functions;
- `Modular fixtures <http://pytest.org/latest/fixture.html>`_ for
- `Modular fixtures <http://docs.pytest.org/en/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;
- 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;
- Python2.6+, Python3.3+, 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;
- Rich plugin architecture, with over 150+ `external plugins <http://docs.pytest.org/en/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.
For full documentation, including installation, tutorials and PDF documents, please see http://docs.pytest.org.
Bugs/Requests
@ -89,7 +90,7 @@ Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issue
Changelog
---------
Consult the `Changelog <http://pytest.org/latest/changelog.html>`_ page for fixes and enhancements of each version.
Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`_ page for fixes and enhancements of each version.
License

View File

@ -1181,6 +1181,8 @@ def get_common_ancestor(args):
if str(arg)[0] == "-":
continue
p = py.path.local(arg)
if not p.exists():
continue
if common_ancestor is None:
common_ancestor = p
else:
@ -1194,21 +1196,28 @@ def get_common_ancestor(args):
common_ancestor = shared
if common_ancestor is None:
common_ancestor = py.path.local()
elif not common_ancestor.isdir():
elif common_ancestor.isfile():
common_ancestor = common_ancestor.dirpath()
return common_ancestor
def get_dirs_from_args(args):
return [d for d in (py.path.local(x) for x in args
if not str(x).startswith("-"))
if d.exists()]
def determine_setup(inifile, args):
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(args)
rootdir = get_common_ancestor(dirs)
else:
ancestor = get_common_ancestor(args)
ancestor = get_common_ancestor(dirs)
rootdir, inifile, inicfg = getcfg(
[ancestor], ["pytest.ini", "tox.ini", "setup.cfg"])
if rootdir is None:
@ -1216,7 +1225,13 @@ def determine_setup(inifile, args):
if rootdir.join("setup.py").exists():
break
else:
rootdir = ancestor
rootdir, inifile, inicfg = getcfg(
dirs, ["pytest.ini", "tox.ini", "setup.cfg"])
if rootdir is None:
rootdir = get_common_ancestor([py.path.local(), ancestor])
is_fs_root = os.path.splitdrive(str(rootdir))[1] == os.sep
if is_fs_root:
rootdir = ancestor
return rootdir, inifile, inicfg or {}

View File

@ -10,7 +10,7 @@ environment:
# builds timing out in AppVeyor
- TOXENV: "linting,py26,py27,py33,py34,py35,pypy"
- TOXENV: "py27-pexpect,py27-xdist,py27-trial,py35-pexpect,py35-xdist,py35-trial"
- TOXENV: "py27-nobyte,doctesting,py27-cxfreeze"
- TOXENV: "py27-nobyte,doctesting,freeze"
install:
- echo Installed Pythons

View File

@ -3,7 +3,7 @@
Full pytest documentation
===========================
`Download latest version as PDF <pytest.pdf>`_
`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>`_

View File

@ -29,25 +29,29 @@ project/testrun-specific information.
Here is the algorithm which finds the rootdir from ``args``:
- determine the common ancestor directory for the specified ``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. 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.
- 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 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.
- 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.
Note that options from multiple ini-files candidates are never merged,
the first one wins (``pytest.ini`` always wins even if it does not
- if no ini-file 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 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. 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:

View File

@ -720,40 +720,29 @@ and run it::
You'll see that the fixture finalizers could use the precise reporting
information.
Integrating pytest runner and cx_freeze
-----------------------------------------------------------
Freezing pytest
---------------
If you freeze your application using a tool like
`cx_freeze <https://cx-freeze.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.
`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.
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.
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.
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()``::
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.
# 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::
.. code-block:: python
# contents of app_main.py
import sys
@ -766,7 +755,8 @@ over to ``pytest`` instead. For example::
# by your argument-parsing library of choice as usual
...
This makes it convenient to execute your tests from within your frozen
application, using standard ``pytest`` command-line options::
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/

View File

@ -11,7 +11,7 @@ Installation and Getting Started
`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>`_
**documentation as PDF**: `download latest <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
.. _`getstarted`:
.. _installation:

View File

@ -10,7 +10,7 @@ pytest: helps you write better programs
- 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>`_
- :ref:`comprehensive online <toc>` and `PDF documentation <https://media.readthedocs.org/pdf/pytest/latest/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>`

View File

@ -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)

View File

@ -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="example of how embedding pytest into an executable using cx_freeze",
executables=[Executable("runtests_script.py")],
options={"build_exe": {'includes': pytest.freeze_includes()}},
)

View File

@ -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))

3
testing/freeze/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build/
dist/
*.spec

View File

@ -0,0 +1,13 @@
"""
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)

View File

@ -1,9 +1,9 @@
"""
This is the script that is actually frozen into an executable: simply executes
pytest main().
"""
if __name__ == '__main__':
import sys
import pytest
"""
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())

View File

@ -1,6 +1,6 @@
def test_upper():
assert 'foo'.upper() == 'FOO'
def test_lower():
def test_upper():
assert 'foo'.upper() == 'FOO'
def test_lower():
assert 'FOO'.lower() == 'foo'

12
testing/freeze/tox_run.py Normal file
View File

@ -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))

View File

@ -493,7 +493,8 @@ class TestSession:
class Test_getinitialnodes:
def test_global_file(self, testdir, tmpdir):
x = tmpdir.ensure("x.py")
config = testdir.parseconfigure(x)
with tmpdir.as_cwd():
config = testdir.parseconfigure(x)
col = testdir.getnode(config, x)
assert isinstance(col, pytest.Module)
assert col.name == 'x.py'
@ -507,7 +508,8 @@ class Test_getinitialnodes:
subdir = tmpdir.join("subdir")
x = subdir.ensure("x.py")
subdir.ensure("__init__.py")
config = testdir.parseconfigure(x)
with subdir.as_cwd():
config = testdir.parseconfigure(x)
col = testdir.getnode(config, x)
assert isinstance(col, pytest.Module)
assert col.name == 'x.py'

View File

@ -483,7 +483,8 @@ def test_consider_args_after_options_for_rootdir_and_inifile(testdir, args):
args[i] = d1
elif arg == 'dir2':
args[i] = d2
result = testdir.runpytest(*args)
with root.as_cwd():
result = testdir.runpytest(*args)
result.stdout.fnmatch_lines(['*rootdir: *myroot, inifile: '])
@ -566,10 +567,14 @@ class TestWarning:
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
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):
@ -604,7 +609,8 @@ class TestRootdir:
assert inifile is None
assert inicfg == {}
def test_nothing(self, tmpdir):
def test_nothing(self, tmpdir, monkeypatch):
monkeypatch.chdir(str(tmpdir))
rootdir, inifile, inicfg = determine_setup(None, [tmpdir])
assert rootdir == tmpdir
assert inifile is None
@ -685,3 +691,36 @@ class TestOverrideIniArgs:
"ini2:url=/tmp/user2?a=b&d=e",
"ini3:True",
"ini4:False"])
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

11
tox.ini
View File

@ -5,7 +5,7 @@ distshare={homedir}/.tox/distshare
envlist=
linting,py26,py27,py33,py34,py35,pypy,
{py27,py35}-{pexpect,xdist,trial},
py27-nobyte,doctesting,py27-cxfreeze
py27-nobyte,doctesting,freeze
[testenv]
commands= pytest --lsof -rfsxX {posargs:testing}
@ -128,12 +128,11 @@ changedir=testing
commands=
{envpython} {envbindir}/py.test-jython -rfsxX {posargs}
[testenv:py27-cxfreeze]
changedir=testing/cx_freeze
platform=linux|darwin
[testenv:freeze]
changedir=testing/freeze
deps=pyinstaller
commands=
{envpython} install_cx_freeze.py
{envpython} runtests_setup.py build --build-exe build
{envpython} create_executable.py
{envpython} tox_run.py