merge trunk
This commit is contained in:
commit
f73ab23003
|
@ -1,7 +1,11 @@
|
||||||
Changes between 1.3.4 and 2.0.0dev0
|
Changes between 1.3.4 and 2.0.0dev0
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
- pytest-2.0 is now its own package and depends on pylib
|
- pytest-2.0 is now its own package and depends on pylib-2.0
|
||||||
|
- 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).
|
||||||
- fix issue126 - introduce py.test.set_trace() to trace execution via
|
- fix issue126 - introduce py.test.set_trace() to trace execution via
|
||||||
PDB during the running of tests even if capturing is ongoing.
|
PDB during the running of tests even if capturing is ongoing.
|
||||||
- fix issue123 - new "python -m py.test" invocation for py.test
|
- fix issue123 - new "python -m py.test" invocation for py.test
|
||||||
|
|
|
@ -258,3 +258,9 @@ epub_copyright = u'2010, holger krekel et aliter'
|
||||||
|
|
||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
intersphinx_mapping = {'http://docs.python.org/': None}
|
intersphinx_mapping = {'http://docs.python.org/': 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')
|
||||||
|
|
|
@ -5,33 +5,57 @@ Customizing and Extending py.test
|
||||||
basic test configuration
|
basic test configuration
|
||||||
===================================
|
===================================
|
||||||
|
|
||||||
Command line options
|
Command line options and configuration file settings
|
||||||
---------------------------------
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
You can see command line options by running::
|
You can get help on options and configuration options by running::
|
||||||
|
|
||||||
py.test -h
|
py.test -h # prints options _and_ config file settings
|
||||||
|
|
||||||
This will display all available command line options
|
This will display command line and configuration file settings
|
||||||
in your specific environment.
|
which were registered by installed plugins.
|
||||||
|
|
||||||
|
how test configuration is read from setup/tox ini-files
|
||||||
|
--------------------------------------------------------
|
||||||
|
|
||||||
setting persistent option defaults
|
py.test looks for the first ``[pytest]`` section in either the first ``setup.cfg`` or the first ``tox.ini`` file found upwards from the arguments. Example::
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
py.test will lookup option values in this order:
|
py.test path/to/testdir
|
||||||
|
|
||||||
* command line
|
will look in the following dirs for a config file::
|
||||||
* conftest.py files
|
|
||||||
* environment variables
|
|
||||||
|
|
||||||
To get an overview on existing names and settings type::
|
path/to/testdir/setup.cfg
|
||||||
|
path/to/setup.cfg
|
||||||
|
path/setup.cfg
|
||||||
|
setup.cfg
|
||||||
|
... # up until root of filesystem
|
||||||
|
path/to/testdir/tox.ini
|
||||||
|
path/to/tox.ini
|
||||||
|
path/tox.ini
|
||||||
|
... # up until root of filesystem
|
||||||
|
|
||||||
py.test --help-config
|
If no path was provided at all the current working directory is used for the lookup.
|
||||||
|
|
||||||
This will print information about all available options
|
builtin configuration file options
|
||||||
in your environment, including your local plugins and
|
----------------------------------------------
|
||||||
command line options.
|
|
||||||
|
.. confval:: minversion = VERSTRING
|
||||||
|
|
||||||
|
specifies the minimal pytest version that is needed for this test suite.
|
||||||
|
|
||||||
|
minversion = 2.1 # will fail if we run with pytest-2.0
|
||||||
|
|
||||||
|
.. confval:: addargs = OPTS
|
||||||
|
|
||||||
|
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::
|
||||||
|
|
||||||
|
[pytest]
|
||||||
|
addargs = --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
|
||||||
|
|
||||||
.. _`function arguments`: funcargs.html
|
.. _`function arguments`: funcargs.html
|
||||||
.. _`extensions`:
|
.. _`extensions`:
|
||||||
|
@ -49,10 +73,8 @@ extensions and customizations close to test code.
|
||||||
local conftest.py plugins
|
local conftest.py plugins
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
local `conftest.py` plugins are usually automatically loaded and
|
local ``conftest.py`` plugins contain directory-specific hook implemenations. Its contained runtest- and collection- related hooks are called when collecting or running tests in files or directories next to or below the ``conftest.py``
|
||||||
registered but its contained hooks are only called when collecting or
|
file. Example: Assume the following layout and content of files::
|
||||||
running tests in files or directories next to or below the ``conftest.py``
|
|
||||||
file. Assume the following layout and content of files::
|
|
||||||
|
|
||||||
a/conftest.py:
|
a/conftest.py:
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
|
@ -72,20 +94,22 @@ Here is how you might run it::
|
||||||
py.test a/test_sub.py # will show "setting up"
|
py.test a/test_sub.py # will show "setting up"
|
||||||
|
|
||||||
``py.test`` loads all ``conftest.py`` files upwards from the command
|
``py.test`` loads all ``conftest.py`` files upwards from the command
|
||||||
line file arguments. It usually looks up configuration values or hooks
|
line file arguments. It usually performs look up right-to-left, i.e.
|
||||||
right-to-left, i.e. the closer conftest files are checked before
|
the hooks in "closer" conftest files will be called earlier than further
|
||||||
the further away ones. This means you can have a ``conftest.py``
|
away ones. This means you can even have a ``conftest.py`` file in your home
|
||||||
in your home directory to provide global configuration values.
|
directory to customize test functionality globally for all of your projects.
|
||||||
|
|
||||||
.. Note::
|
.. Note::
|
||||||
if you have ``conftest.py`` files which do not reside in a
|
If you have ``conftest.py`` files which do not reside in a
|
||||||
python package directory (i.e. one containing an ``__init__.py``) then
|
python package directory (i.e. one containing an ``__init__.py``) then
|
||||||
"import conftest" will be ambigous and should be avoided. If you
|
"import conftest" can be ambigous because there might be other
|
||||||
ever want to import anything from a ``conftest.py`` file
|
``conftest.py`` files as well on your PYTHONPATH or ``sys.path``.
|
||||||
put it inside a package. You avoid trouble this way.
|
It is good practise for projects to put ``conftest.py`` within a package
|
||||||
|
scope or to never import anything from the conftest.py file.
|
||||||
|
|
||||||
.. _`named plugins`: plugin/index.html
|
.. _`named plugins`: plugin/index.html
|
||||||
|
|
||||||
|
|
||||||
Plugin discovery at tool startup
|
Plugin discovery at tool startup
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
|
@ -93,9 +117,6 @@ py.test loads plugin modules at tool startup in the following way:
|
||||||
|
|
||||||
* by loading all plugins registered through `setuptools entry points`_.
|
* by loading all plugins registered through `setuptools entry points`_.
|
||||||
|
|
||||||
* by reading the ``PYTEST_PLUGINS`` environment variable
|
|
||||||
and importing the comma-separated list of named plugins.
|
|
||||||
|
|
||||||
* by pre-scanning the command line for the ``-p name`` option
|
* by pre-scanning the command line for the ``-p name`` option
|
||||||
and loading the specified plugin before actual command line parsing.
|
and loading the specified plugin before actual command line parsing.
|
||||||
|
|
||||||
|
@ -105,39 +126,17 @@ py.test loads plugin modules at tool startup in the following way:
|
||||||
not loaded at tool startup.
|
not loaded at tool startup.
|
||||||
|
|
||||||
* by recursively loading all plugins specified by the
|
* by recursively loading all plugins specified by the
|
||||||
``pytest_plugins`` variable in a ``conftest.py`` file
|
``pytest_plugins`` variable in ``conftest.py`` files
|
||||||
|
|
||||||
Requiring/Loading plugins in a test module or plugin
|
Requiring/Loading plugins in a test module or plugin
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
|
|
||||||
You can specify plugins in a test module or a plugin like this::
|
You can require plugins in a test module or a plugin like this::
|
||||||
|
|
||||||
pytest_plugins = "name1", "name2",
|
pytest_plugins = "name1", "name2",
|
||||||
|
|
||||||
When the test module or plugin is loaded the specified plugins
|
When the test module or plugin is loaded the specified plugins
|
||||||
will be loaded. If you specify plugins without the ``pytest_``
|
will be loaded.
|
||||||
prefix it will be automatically added. All plugin names
|
|
||||||
must be lowercase.
|
|
||||||
|
|
||||||
.. _`conftest.py plugin`:
|
|
||||||
.. _`conftestplugin`:
|
|
||||||
|
|
||||||
Writing per-project plugins (conftest.py)
|
|
||||||
------------------------------------------------------
|
|
||||||
|
|
||||||
The purpose of :file:`conftest.py` files is to allow project-specific
|
|
||||||
test customization. They thus make for a good place to implement
|
|
||||||
project-specific test related features through hooks. For example you may
|
|
||||||
set the ``collect_ignore`` variable depending on a command line option
|
|
||||||
by defining the following hook in a ``conftest.py`` file::
|
|
||||||
|
|
||||||
# ./conftest.py in your root or package dir
|
|
||||||
collect_ignore = ['hello', 'test_world.py']
|
|
||||||
def pytest_addoption(parser):
|
|
||||||
parser.addoption("--runall", action="store_true", default=False)
|
|
||||||
def pytest_configure(config):
|
|
||||||
if config.getvalue("runall"):
|
|
||||||
collect_ignore[:] = []
|
|
||||||
|
|
||||||
.. _`setuptools entry points`:
|
.. _`setuptools entry points`:
|
||||||
.. _registered:
|
.. _registered:
|
||||||
|
@ -332,12 +331,28 @@ Reference of important objects involved in hooks
|
||||||
.. autoclass:: pytest.plugin.config.Config
|
.. autoclass:: pytest.plugin.config.Config
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.session.Item
|
.. autoclass:: pytest.plugin.config.Parser
|
||||||
:inherited-members:
|
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.session.Node
|
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.session.Node(name, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
..
|
||||||
|
.. autoclass:: pytest.plugin.session.File(fspath, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.session.Item(name, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.python.Module(name, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.python.Class(name, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.python.Function(name, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.runner.CallInfo
|
.. autoclass:: pytest.plugin.runner.CallInfo
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
@ -345,29 +360,3 @@ Reference of important objects involved in hooks
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
||||||
conftest.py configuration files
|
|
||||||
=================================================
|
|
||||||
|
|
||||||
conftest.py reference docs
|
|
||||||
|
|
||||||
A unique feature of py.test are its ``conftest.py`` files which allow
|
|
||||||
project and directory specific customizations to testing.
|
|
||||||
|
|
||||||
* `set option defaults`_
|
|
||||||
|
|
||||||
or set particular variables to influence the testing process:
|
|
||||||
|
|
||||||
* ``pytest_plugins``: list of named plugins to load
|
|
||||||
|
|
||||||
* ``collect_ignore``: list of paths to ignore during test collection, relative to the containing ``conftest.py`` file
|
|
||||||
|
|
||||||
* ``rsyncdirs``: list of to-be-rsynced directories for distributed
|
|
||||||
testing, relative to the containing ``conftest.py`` file.
|
|
||||||
|
|
||||||
You may put a conftest.py files in your project root directory or into
|
|
||||||
your package directory if you want to add project-specific test options.
|
|
||||||
|
|
||||||
|
|
||||||
.. _`specify funcargs`: funcargs.html#application-setup-tutorial-example
|
|
||||||
|
|
||||||
.. _`set option defaults`:
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
|
|
||||||
Customizing test function through marks and hooks
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
.. _`retrieved by hooks as item keywords`:
|
.. _`retrieved by hooks as item keywords`:
|
||||||
|
|
||||||
control skipping of tests according to command line option
|
control skipping of tests according to command line option
|
|
@ -1,7 +1,4 @@
|
||||||
|
|
||||||
Misc examples
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
Detect if running from within a py.test run
|
Detect if running from within a py.test run
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
|
||||||
.. highlightlang:: python
|
.. highlightlang:: python
|
||||||
|
|
||||||
|
.. _mysetup:
|
||||||
|
|
||||||
mysetup pattern: application specific test fixtures
|
mysetup pattern: application specific test fixtures
|
||||||
==========================================================
|
==========================================================
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
|
||||||
|
.. _`non-python tests`:
|
||||||
|
|
||||||
|
Working with non-python tests
|
||||||
|
====================================================
|
||||||
|
|
||||||
|
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 session starts ============================
|
||||||
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev10
|
||||||
|
test path 1: /home/hpk/p/pytest/doc/example/nonpython
|
||||||
|
|
||||||
|
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.06 seconds ====================
|
||||||
|
|
||||||
|
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. Note that ``reportinfo()``
|
||||||
|
is used for representing the test location and is also consulted for
|
||||||
|
reporting in ``verbose`` mode::
|
||||||
|
|
||||||
|
nonpython $ py.test -v
|
||||||
|
=========================== test session starts ============================
|
||||||
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev10 -- /home/hpk/venv/0/bin/python
|
||||||
|
test path 1: /home/hpk/p/pytest/doc/example/nonpython
|
||||||
|
|
||||||
|
test_simple.yml:1: usecase: ok PASSED
|
||||||
|
test_simple.yml:1: usecase: hello FAILED
|
||||||
|
|
||||||
|
================================= FAILURES =================================
|
||||||
|
______________________________ usecase: hello ______________________________
|
||||||
|
usecase execution failed
|
||||||
|
spec failed: 'some': 'other'
|
||||||
|
no further details known at this point.
|
||||||
|
==================== 1 failed, 1 passed in 0.06 seconds ====================
|
||||||
|
|
||||||
|
While developing your custom test collection and execution it's also
|
||||||
|
interesting to just look at the collection tree::
|
||||||
|
|
||||||
|
nonpython $ py.test --collectonly
|
||||||
|
<Directory 'nonpython'>
|
||||||
|
<YamlFile 'test_simple.yml'>
|
||||||
|
<UsecaseItem 'ok'>
|
||||||
|
<UsecaseItem 'hello'>
|
|
@ -0,0 +1,40 @@
|
||||||
|
# content of conftest.py
|
||||||
|
|
||||||
|
import py
|
||||||
|
|
||||||
|
def pytest_collect_file(path, parent):
|
||||||
|
if path.ext == ".yml" and path.basename.startswith("test"):
|
||||||
|
return YamlFile(path, parent)
|
||||||
|
|
||||||
|
class YamlFile(py.test.collect.File):
|
||||||
|
def collect(self):
|
||||||
|
import yaml # we need a yaml parser, e.g. PyYAML
|
||||||
|
raw = yaml.load(self.fspath.open())
|
||||||
|
for name, spec in raw.items():
|
||||||
|
yield UsecaseItem(name, self, spec)
|
||||||
|
|
||||||
|
class UsecaseItem(py.test.collect.Item):
|
||||||
|
def __init__(self, name, parent, spec):
|
||||||
|
super(UsecaseItem, 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 UsecaseException(self, name, value)
|
||||||
|
|
||||||
|
def repr_failure(self, excinfo):
|
||||||
|
""" called when self.runtest() raises an exception. """
|
||||||
|
if excinfo.errisinstance(UsecaseException):
|
||||||
|
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 UsecaseException(Exception):
|
||||||
|
""" custom exception for error reporting. """
|
|
@ -0,0 +1,7 @@
|
||||||
|
# test_simple.yml
|
||||||
|
ok:
|
||||||
|
sub1: sub1
|
||||||
|
|
||||||
|
hello:
|
||||||
|
world: world
|
||||||
|
some: other
|
|
@ -7,6 +7,7 @@ Usages and Examples
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
example/marking.txt
|
example/controlskip.txt
|
||||||
example/mysetup.txt
|
example/mysetup.txt
|
||||||
example/misc.txt
|
example/detectpytest.txt
|
||||||
|
example/nonpython.txt
|
||||||
|
|
|
@ -45,7 +45,8 @@ supports several testing practises and methods
|
||||||
|
|
||||||
- supports extended `xUnit style setup`_
|
- supports extended `xUnit style setup`_
|
||||||
- can integrate nose_, `unittest.py` and `doctest.py`_ style tests
|
- can integrate nose_, `unittest.py` and `doctest.py`_ style tests
|
||||||
- supports generating testing coverage
|
- supports generating testing coverage reports
|
||||||
|
- supports :ref:`non-python tests`
|
||||||
- `Javasript unit- and functional testing`_
|
- `Javasript unit- and functional testing`_
|
||||||
|
|
||||||
.. _`Javasript unit- and functional testing`: plugin/oejskit.html
|
.. _`Javasript unit- and functional testing`: plugin/oejskit.html
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
.. highlightlang:: python
|
||||||
.. _`good practises`:
|
.. _`good practises`:
|
||||||
|
|
||||||
Good Practises
|
Good Practises
|
||||||
|
@ -28,8 +29,7 @@ py.test supports common test layouts.
|
||||||
|
|
||||||
XXX
|
XXX
|
||||||
|
|
||||||
|
.. _`genscript method`:
|
||||||
|
|
||||||
|
|
||||||
Generating a py.test standalone Script
|
Generating a py.test standalone Script
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
@ -38,12 +38,12 @@ If you are a maintainer or application developer and want users
|
||||||
to run tests you can use a facility to generate a standalone
|
to run tests you can use a facility to generate a standalone
|
||||||
"py.test" script that you can tell users to run::
|
"py.test" script that you can tell users to run::
|
||||||
|
|
||||||
py.test --genscript=mytest
|
py.test --genscript=runtests.py
|
||||||
|
|
||||||
will generate a ``mytest`` script that is, in fact, a ``py.test`` under
|
will generate a ``mytest`` script that is, in fact, a ``py.test`` under
|
||||||
disguise. You can tell people to download and then e.g. run it like this::
|
disguise. You can tell people to download and then e.g. run it like this::
|
||||||
|
|
||||||
python mytest --pastebin=all
|
python runtests.py --pastebin=all
|
||||||
|
|
||||||
and ask them to send you the resulting URL. The resulting script has
|
and ask them to send you the resulting URL. The resulting script has
|
||||||
all core features and runs unchanged under Python2 and Python3 interpreters.
|
all core features and runs unchanged under Python2 and Python3 interpreters.
|
||||||
|
@ -51,4 +51,46 @@ all core features and runs unchanged under Python2 and Python3 interpreters.
|
||||||
.. _`Distribute for installation`: http://pypi.python.org/pypi/distribute#installation-instructions
|
.. _`Distribute for installation`: http://pypi.python.org/pypi/distribute#installation-instructions
|
||||||
.. _`distribute installation`: http://pypi.python.org/pypi/distribute
|
.. _`distribute installation`: http://pypi.python.org/pypi/distribute
|
||||||
|
|
||||||
|
|
||||||
|
Integrating with distutils / ``python setup.py test``
|
||||||
|
--------------------------------------------------------
|
||||||
|
|
||||||
|
You can easily integrate test runs into your distutils or
|
||||||
|
setuptools based project. Use the `genscript method`_
|
||||||
|
to generate a standalone py.test script::
|
||||||
|
|
||||||
|
py.test --genscript=runtests.py
|
||||||
|
|
||||||
|
and make this script part of your distribution and then add
|
||||||
|
this to your ``setup.py`` file::
|
||||||
|
|
||||||
|
from distutils.core import setup, Command
|
||||||
|
# you can also import from setuptools
|
||||||
|
|
||||||
|
class PyTest(Command):
|
||||||
|
user_options = []
|
||||||
|
def initialize_options(self):
|
||||||
|
pass
|
||||||
|
def finalize_options(self):
|
||||||
|
pass
|
||||||
|
def run(self):
|
||||||
|
import sys,subprocess
|
||||||
|
errno = subprocess.call([sys.executable, 'runtest.py'])
|
||||||
|
raise SystemExit(errno)
|
||||||
|
setup(
|
||||||
|
#...,
|
||||||
|
cmdclass = {'test': PyTest},
|
||||||
|
#...,
|
||||||
|
)
|
||||||
|
|
||||||
|
If you now type::
|
||||||
|
|
||||||
|
python setup.py test
|
||||||
|
|
||||||
|
this will execute your tests using ``runtest.py``. As this is a
|
||||||
|
standalone version of ``py.test`` no prior installation whatsoever is
|
||||||
|
required for calling the test command. You can also pass additional
|
||||||
|
arguments to the subprocess-calls like your test directory or other
|
||||||
|
options.
|
||||||
|
|
||||||
.. include:: links.inc
|
.. include:: links.inc
|
||||||
|
|
|
@ -16,8 +16,8 @@ basic usage and funcargs:
|
||||||
|
|
||||||
function arguments:
|
function arguments:
|
||||||
|
|
||||||
|
- :ref:`mysetup`
|
||||||
- `application setup in test functions with funcargs`_
|
- `application setup in test functions with funcargs`_
|
||||||
- `making funcargs dependendent on command line options`_
|
|
||||||
- `monkey patching done right`_ (blog post, consult `monkeypatch
|
- `monkey patching done right`_ (blog post, consult `monkeypatch
|
||||||
plugin`_ for actual 1.0 API)
|
plugin`_ for actual 1.0 API)
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@ plugin specific examples:
|
||||||
- `many examples in the docs for plugins`_
|
- `many examples in the docs for plugins`_
|
||||||
|
|
||||||
.. _`skipping slow tests by default in py.test`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
|
.. _`skipping slow tests by default in py.test`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
|
||||||
.. _`making funcargs dependendent on command line options`: funcargs.html#tut-cmdlineoption
|
|
||||||
.. _`many examples in the docs for plugins`: plugin/index.html
|
.. _`many examples in the docs for plugins`: plugin/index.html
|
||||||
.. _`monkeypatch plugin`: plugin/monkeypatch.html
|
.. _`monkeypatch plugin`: plugin/monkeypatch.html
|
||||||
.. _`application setup in test functions with funcargs`: funcargs.html#appsetup
|
.. _`application setup in test functions with funcargs`: funcargs.html#appsetup
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
import py
|
import py
|
||||||
failure_demo = py.path.local(__file__).dirpath('failure_demo.py')
|
failure_demo = py.path.local(__file__).dirpath('failure_demo.py')
|
||||||
|
|
||||||
pytest_plugins = "pytest_pytester"
|
|
||||||
|
|
||||||
def test_failure_demo_fails_properly(testdir):
|
def test_failure_demo_fails_properly(testdir):
|
||||||
target = testdir.tmpdir.join(failure_demo.basename)
|
target = testdir.tmpdir.join(failure_demo.basename)
|
||||||
failure_demo.copy(target)
|
failure_demo.copy(target)
|
||||||
|
|
|
@ -5,11 +5,12 @@ see http://pytest.org for documentation and details
|
||||||
|
|
||||||
(c) Holger Krekel and others, 2004-2010
|
(c) Holger Krekel and others, 2004-2010
|
||||||
"""
|
"""
|
||||||
__version__ = '2.0.0.dev10'
|
__version__ = '2.0.0.dev11'
|
||||||
|
|
||||||
__all__ = ['config', 'cmdline']
|
__all__ = ['config', 'cmdline']
|
||||||
|
|
||||||
from pytest import _core as cmdline
|
from pytest import _core as cmdline
|
||||||
|
UsageError = cmdline.UsageError
|
||||||
|
|
||||||
def __main__():
|
def __main__():
|
||||||
raise SystemExit(cmdline.main())
|
raise SystemExit(cmdline.main())
|
|
@ -345,13 +345,16 @@ def main(args=None):
|
||||||
if args is None:
|
if args is None:
|
||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
hook = pluginmanager.hook
|
hook = pluginmanager.hook
|
||||||
config = hook.pytest_cmdline_parse(pluginmanager=pluginmanager, args=args)
|
|
||||||
try:
|
try:
|
||||||
|
config = hook.pytest_cmdline_parse(
|
||||||
|
pluginmanager=pluginmanager, args=args)
|
||||||
exitstatus = hook.pytest_cmdline_main(config=config)
|
exitstatus = hook.pytest_cmdline_main(config=config)
|
||||||
except config.Error:
|
except UsageError:
|
||||||
e = sys.exc_info()[1]
|
e = sys.exc_info()[1]
|
||||||
sys.stderr.write("ERROR: %s\n" %(e.args[0],))
|
sys.stderr.write("ERROR: %s\n" %(e.args[0],))
|
||||||
exitstatus = 3
|
exitstatus = 3
|
||||||
pluginmanager = PluginManager(load=True)
|
pluginmanager = PluginManager(load=True)
|
||||||
return exitstatus
|
return exitstatus
|
||||||
|
|
||||||
|
class UsageError(Exception):
|
||||||
|
""" error in py.test usage or invocation"""
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import py
|
import py
|
||||||
import sys, os
|
import sys, os
|
||||||
from pytest._core import PluginManager
|
from pytest._core import PluginManager
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def pytest_cmdline_parse(pluginmanager, args):
|
def pytest_cmdline_parse(pluginmanager, args):
|
||||||
|
@ -9,6 +10,10 @@ def pytest_cmdline_parse(pluginmanager, args):
|
||||||
config.parse(args)
|
config.parse(args)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addini('addargs', 'default command line arguments')
|
||||||
|
parser.addini('minversion', 'minimally required pytest version')
|
||||||
|
|
||||||
class Parser:
|
class Parser:
|
||||||
""" Parser for command line arguments. """
|
""" Parser for command line arguments. """
|
||||||
|
|
||||||
|
@ -17,6 +22,7 @@ class Parser:
|
||||||
self._groups = []
|
self._groups = []
|
||||||
self._processopt = processopt
|
self._processopt = processopt
|
||||||
self._usage = usage
|
self._usage = usage
|
||||||
|
self._inidict = {}
|
||||||
self.hints = []
|
self.hints = []
|
||||||
|
|
||||||
def processoption(self, option):
|
def processoption(self, option):
|
||||||
|
@ -28,6 +34,12 @@ class Parser:
|
||||||
self._notes.append(note)
|
self._notes.append(note)
|
||||||
|
|
||||||
def getgroup(self, name, description="", after=None):
|
def getgroup(self, name, description="", after=None):
|
||||||
|
""" get (or create) a named option Group.
|
||||||
|
|
||||||
|
:name: unique name of the option group.
|
||||||
|
:description: long description for --help output.
|
||||||
|
:after: name of other group, used for ordering --help output.
|
||||||
|
"""
|
||||||
for group in self._groups:
|
for group in self._groups:
|
||||||
if group.name == name:
|
if group.name == name:
|
||||||
return group
|
return group
|
||||||
|
@ -44,7 +56,7 @@ class Parser:
|
||||||
self._anonymous.addoption(*opts, **attrs)
|
self._anonymous.addoption(*opts, **attrs)
|
||||||
|
|
||||||
def parse(self, args):
|
def parse(self, args):
|
||||||
optparser = MyOptionParser(self)
|
self.optparser = optparser = MyOptionParser(self)
|
||||||
groups = self._groups + [self._anonymous]
|
groups = self._groups + [self._anonymous]
|
||||||
for group in groups:
|
for group in groups:
|
||||||
if group.options:
|
if group.options:
|
||||||
|
@ -52,7 +64,7 @@ class Parser:
|
||||||
optgroup = py.std.optparse.OptionGroup(optparser, desc)
|
optgroup = py.std.optparse.OptionGroup(optparser, desc)
|
||||||
optgroup.add_options(group.options)
|
optgroup.add_options(group.options)
|
||||||
optparser.add_option_group(optgroup)
|
optparser.add_option_group(optgroup)
|
||||||
return optparser.parse_args([str(x) for x in args])
|
return self.optparser.parse_args([str(x) for x in args])
|
||||||
|
|
||||||
def parse_setoption(self, args, option):
|
def parse_setoption(self, args, option):
|
||||||
parsedoption, args = self.parse(args)
|
parsedoption, args = self.parse(args)
|
||||||
|
@ -60,6 +72,9 @@ class Parser:
|
||||||
setattr(option, name, value)
|
setattr(option, name, value)
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
def addini(self, name, description, type=None):
|
||||||
|
""" add an ini-file option with the given name and description. """
|
||||||
|
self._inidict[name] = (description, type)
|
||||||
|
|
||||||
class OptionGroup:
|
class OptionGroup:
|
||||||
def __init__(self, name, description="", parser=None):
|
def __init__(self, name, description="", parser=None):
|
||||||
|
@ -90,7 +105,8 @@ class OptionGroup:
|
||||||
class MyOptionParser(py.std.optparse.OptionParser):
|
class MyOptionParser(py.std.optparse.OptionParser):
|
||||||
def __init__(self, parser):
|
def __init__(self, parser):
|
||||||
self._parser = parser
|
self._parser = parser
|
||||||
py.std.optparse.OptionParser.__init__(self, usage=parser._usage)
|
py.std.optparse.OptionParser.__init__(self, usage=parser._usage,
|
||||||
|
add_help_option=False)
|
||||||
def format_epilog(self, formatter):
|
def format_epilog(self, formatter):
|
||||||
hints = self._parser.hints
|
hints = self._parser.hints
|
||||||
if hints:
|
if hints:
|
||||||
|
@ -226,13 +242,8 @@ class CmdOptions(object):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<CmdOptions %r>" %(self.__dict__,)
|
return "<CmdOptions %r>" %(self.__dict__,)
|
||||||
|
|
||||||
class Error(Exception):
|
|
||||||
""" Test Configuration Error. """
|
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
""" access to configuration values, pluginmanager and plugin hooks. """
|
""" access to configuration values, pluginmanager and plugin hooks. """
|
||||||
Option = py.std.optparse.Option
|
|
||||||
Error = Error
|
|
||||||
basetemp = None
|
basetemp = None
|
||||||
|
|
||||||
def __init__(self, pluginmanager=None):
|
def __init__(self, pluginmanager=None):
|
||||||
|
@ -251,6 +262,11 @@ class Config(object):
|
||||||
self.trace("loaded conftestmodule %r" %(conftestmodule,))
|
self.trace("loaded conftestmodule %r" %(conftestmodule,))
|
||||||
self.pluginmanager.consider_conftest(conftestmodule)
|
self.pluginmanager.consider_conftest(conftestmodule)
|
||||||
|
|
||||||
|
def _processopt(self, opt):
|
||||||
|
if hasattr(opt, 'default') and opt.dest:
|
||||||
|
if not hasattr(self.option, opt.dest):
|
||||||
|
setattr(self.option, opt.dest, opt.default)
|
||||||
|
|
||||||
def _getmatchingplugins(self, fspath):
|
def _getmatchingplugins(self, fspath):
|
||||||
allconftests = self._conftest._conftestpath2mod.values()
|
allconftests = self._conftest._conftestpath2mod.values()
|
||||||
plugins = [x for x in self.pluginmanager.getplugins()
|
plugins = [x for x in self.pluginmanager.getplugins()
|
||||||
|
@ -262,28 +278,6 @@ class Config(object):
|
||||||
if getattr(self.option, 'traceconfig', None):
|
if getattr(self.option, 'traceconfig', None):
|
||||||
self.hook.pytest_trace(category="config", msg=msg)
|
self.hook.pytest_trace(category="config", msg=msg)
|
||||||
|
|
||||||
def _processopt(self, opt):
|
|
||||||
if hasattr(opt, 'default') and opt.dest:
|
|
||||||
val = os.environ.get("PYTEST_OPTION_" + opt.dest.upper(), None)
|
|
||||||
if val is not None:
|
|
||||||
if opt.type == "int":
|
|
||||||
val = int(val)
|
|
||||||
elif opt.type == "long":
|
|
||||||
val = long(val)
|
|
||||||
elif opt.type == "float":
|
|
||||||
val = float(val)
|
|
||||||
elif not opt.type and opt.action in ("store_true", "store_false"):
|
|
||||||
val = eval(val)
|
|
||||||
opt.default = val
|
|
||||||
else:
|
|
||||||
name = "option_" + opt.dest
|
|
||||||
try:
|
|
||||||
opt.default = self._conftest.rget(name)
|
|
||||||
except (ValueError, KeyError):
|
|
||||||
pass
|
|
||||||
if not hasattr(self.option, opt.dest):
|
|
||||||
setattr(self.option, opt.dest, opt.default)
|
|
||||||
|
|
||||||
def _setinitialconftest(self, args):
|
def _setinitialconftest(self, args):
|
||||||
# capture output during conftest init (#issue93)
|
# capture output during conftest init (#issue93)
|
||||||
name = hasattr(os, 'dup') and 'StdCaptureFD' or 'StdCapture'
|
name = hasattr(os, 'dup') and 'StdCaptureFD' or 'StdCapture'
|
||||||
|
@ -299,14 +293,31 @@ class Config(object):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _preparse(self, args):
|
def _preparse(self, args):
|
||||||
|
self.inicfg = getcfg(args, ["setup.cfg", "tox.ini",])
|
||||||
|
if self.inicfg:
|
||||||
|
newargs = self.inicfg.get("addargs", None)
|
||||||
|
if newargs:
|
||||||
|
args[:] = args + py.std.shlex.split(newargs)
|
||||||
|
self._checkversion()
|
||||||
self.pluginmanager.consider_setuptools_entrypoints()
|
self.pluginmanager.consider_setuptools_entrypoints()
|
||||||
self.pluginmanager.consider_env()
|
self.pluginmanager.consider_env()
|
||||||
self.pluginmanager.consider_preparse(args)
|
self.pluginmanager.consider_preparse(args)
|
||||||
self._setinitialconftest(args)
|
self._setinitialconftest(args)
|
||||||
self.pluginmanager.do_addoption(self._parser)
|
self.pluginmanager.do_addoption(self._parser)
|
||||||
|
|
||||||
|
def _checkversion(self):
|
||||||
|
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):
|
def parse(self, args):
|
||||||
# cmdline arguments into this config object.
|
# parse given cmdline arguments into this config object.
|
||||||
# Note that this can only be called once per testing process.
|
# Note that this can only be called once per testing process.
|
||||||
assert not hasattr(self, 'args'), (
|
assert not hasattr(self, 'args'), (
|
||||||
"can only parse cmdline args at most once per Config object")
|
"can only parse cmdline args at most once per Config object")
|
||||||
|
@ -340,12 +351,28 @@ class Config(object):
|
||||||
return py.path.local.make_numbered_dir(prefix=basename,
|
return py.path.local.make_numbered_dir(prefix=basename,
|
||||||
keep=0, rootdir=basetemp, lock_timeout=None)
|
keep=0, rootdir=basetemp, lock_timeout=None)
|
||||||
|
|
||||||
def getconftest_pathlist(self, name, path=None):
|
def getini(self, name):
|
||||||
""" return a matching value, which needs to be sequence
|
""" return configuration value from an ini file. If the
|
||||||
of filenames that will be returned as a list of Path
|
specified name hasn't been registered through a prior ``parse.addini``
|
||||||
objects (they can be relative to the location
|
call (usually from a plugin), a ValueError is raised. """
|
||||||
where they were found).
|
try:
|
||||||
"""
|
description, type = self._parser._inidict[name]
|
||||||
|
except KeyError:
|
||||||
|
raise ValueError("unknown configuration value: %r" %(name,))
|
||||||
|
try:
|
||||||
|
value = self.inicfg[name]
|
||||||
|
except KeyError:
|
||||||
|
return # None indicates nothing found
|
||||||
|
if type == "pathlist":
|
||||||
|
dp = py.path.local(self.inicfg.config.path).dirpath()
|
||||||
|
l = []
|
||||||
|
for relpath in py.std.shlex.split(value):
|
||||||
|
l.append(dp.join(relpath, abs=True))
|
||||||
|
return l
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
def _getconftest_pathlist(self, name, path=None):
|
||||||
try:
|
try:
|
||||||
mod, relroots = self._conftest.rget_with_confmod(name, path)
|
mod, relroots = self._conftest.rget_with_confmod(name, path)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -359,6 +386,22 @@ class Config(object):
|
||||||
l.append(relroot)
|
l.append(relroot)
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
def _getconftest(self, name, path=None, check=False):
|
||||||
|
if check:
|
||||||
|
self._checkconftest(name)
|
||||||
|
return self._conftest.rget(name, path)
|
||||||
|
|
||||||
|
def getvalue(self, name, path=None):
|
||||||
|
""" return ``name`` value looked set from command line options.
|
||||||
|
|
||||||
|
(deprecated) if we can't find the option also lookup
|
||||||
|
the name in a matching conftest file.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return getattr(self.option, name)
|
||||||
|
except AttributeError:
|
||||||
|
return self._getconftest(name, path, check=False)
|
||||||
|
|
||||||
def getvalueorskip(self, name, path=None):
|
def getvalueorskip(self, name, path=None):
|
||||||
""" return getvalue(name) or call py.test.skip if no value exists. """
|
""" return getvalue(name) or call py.test.skip if no value exists. """
|
||||||
try:
|
try:
|
||||||
|
@ -369,15 +412,27 @@ class Config(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
py.test.skip("no %r value found" %(name,))
|
py.test.skip("no %r value found" %(name,))
|
||||||
|
|
||||||
def getvalue(self, name, path=None):
|
|
||||||
""" return 'name' value looked up from the 'options'
|
def getcfg(args, inibasenames):
|
||||||
and then from the first conftest file found up
|
if not args:
|
||||||
the path (including the path itself).
|
args = [py.path.local()]
|
||||||
if path is None, lookup the value in the initial
|
for inibasename in inibasenames:
|
||||||
conftest modules found during command line parsing.
|
for p in args:
|
||||||
"""
|
x = findupwards(p, inibasename)
|
||||||
try:
|
if x is not None:
|
||||||
return getattr(self.option, name)
|
iniconfig = py.iniconfig.IniConfig(x)
|
||||||
except AttributeError:
|
if 'pytest' in iniconfig.sections:
|
||||||
return self._conftest.rget(name, path)
|
return iniconfig['pytest']
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def findupwards(current, basename):
|
||||||
|
current = py.path.local(current)
|
||||||
|
while 1:
|
||||||
|
p = current.join(basename)
|
||||||
|
if p.check():
|
||||||
|
return p
|
||||||
|
p = current.dirpath()
|
||||||
|
if p == current:
|
||||||
|
return
|
||||||
|
current = p
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ def pytest_cmdline_main(config):
|
||||||
genscript = config.getvalue("genscript")
|
genscript = config.getvalue("genscript")
|
||||||
if genscript:
|
if genscript:
|
||||||
script = generate_script(
|
script = generate_script(
|
||||||
'import py; py.test.cmdline.main()',
|
'import py; raise SystemExit(py.test.cmdline.main())',
|
||||||
['py', 'pytest'],
|
['py', 'pytest'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
""" provide version info, conftest/environment config names.
|
""" provide version info, conftest/environment config names.
|
||||||
"""
|
"""
|
||||||
import py
|
import py
|
||||||
|
import pytest
|
||||||
import inspect, sys
|
import inspect, sys
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
group = parser.getgroup('debugconfig')
|
group = parser.getgroup('debugconfig')
|
||||||
group.addoption('--version', action="store_true",
|
group.addoption('--version', action="store_true",
|
||||||
help="display py lib version and import information.")
|
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 = [],
|
group._addoption('-p', action="append", dest="plugins", default = [],
|
||||||
metavar="name",
|
metavar="name",
|
||||||
help="early-load given plugin (multi-allowed).")
|
help="early-load given plugin (multi-allowed).")
|
||||||
|
@ -19,67 +22,55 @@ def pytest_addoption(parser):
|
||||||
group.addoption('--debug',
|
group.addoption('--debug',
|
||||||
action="store_true", dest="debug", default=False,
|
action="store_true", dest="debug", default=False,
|
||||||
help="generate and show internal debugging information.")
|
help="generate and show internal debugging information.")
|
||||||
group.addoption("--help-config", action="store_true", dest="helpconfig",
|
|
||||||
help="show available conftest.py and ENV-variable names.")
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_cmdline_main(config):
|
def pytest_cmdline_main(config):
|
||||||
if config.option.version:
|
if config.option.version:
|
||||||
p = py.path.local(py.__file__).dirpath()
|
p = py.path.local(pytest.__file__).dirpath()
|
||||||
sys.stderr.write("This is py.test version %s, imported from %s\n" %
|
sys.stderr.write("This is py.test version %s, imported from %s\n" %
|
||||||
(py.__version__, p))
|
(py.__version__, p))
|
||||||
return 0
|
return 0
|
||||||
elif config.option.helpconfig:
|
elif config.option.help:
|
||||||
config.pluginmanager.do_configure(config)
|
config.pluginmanager.do_configure(config)
|
||||||
showpluginhelp(config)
|
showhelp(config)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def showpluginhelp(config):
|
def showhelp(config):
|
||||||
options = []
|
|
||||||
for group in config._parser._groups:
|
|
||||||
options.extend(group.options)
|
|
||||||
widths = [0] * 10
|
|
||||||
tw = py.io.TerminalWriter()
|
tw = py.io.TerminalWriter()
|
||||||
tw.sep("-")
|
tw.write(config._parser.optparser.format_help())
|
||||||
tw.line("%-13s | %-18s | %-25s | %s" %(
|
tw.line()
|
||||||
"cmdline name", "conftest.py name", "ENV-variable name", "help"))
|
tw.line()
|
||||||
tw.sep("-")
|
#tw.sep( "=", "config file settings")
|
||||||
|
tw.line("setup.cfg or tox.ini options to be put into [pytest] section:")
|
||||||
|
tw.line()
|
||||||
|
|
||||||
options = [opt for opt in options if opt._long_opts]
|
for name, help in sorted(config._parser._inidict.items()):
|
||||||
options.sort(key=lambda x: x._long_opts)
|
line = " %-15s %s" %(name, help)
|
||||||
for opt in options:
|
|
||||||
if not opt._long_opts or not opt.dest:
|
|
||||||
continue
|
|
||||||
optstrings = list(opt._long_opts) # + list(opt._short_opts)
|
|
||||||
optstrings = filter(None, optstrings)
|
|
||||||
optstring = "|".join(optstrings)
|
|
||||||
line = "%-13s | %-18s | %-25s | %s" %(
|
|
||||||
optstring,
|
|
||||||
"option_%s" % opt.dest,
|
|
||||||
"PYTEST_OPTION_%s" % opt.dest.upper(),
|
|
||||||
opt.help and opt.help or "",
|
|
||||||
)
|
|
||||||
tw.line(line[:tw.fullwidth])
|
tw.line(line[:tw.fullwidth])
|
||||||
for name, help in conftest_options:
|
|
||||||
line = "%-13s | %-18s | %-25s | %s" %(
|
|
||||||
"",
|
|
||||||
name,
|
|
||||||
"",
|
|
||||||
help,
|
|
||||||
)
|
|
||||||
tw.line(line[:tw.fullwidth])
|
|
||||||
tw.sep("-")
|
|
||||||
|
|
||||||
conftest_options = (
|
tw.line() ; tw.line()
|
||||||
|
#tw.sep("=")
|
||||||
|
return
|
||||||
|
|
||||||
|
tw.line("conftest.py options:")
|
||||||
|
tw.line()
|
||||||
|
conftestitems = sorted(config._parser._conftestdict.items())
|
||||||
|
for name, help in conftest_options + conftestitems:
|
||||||
|
line = " %-15s %s" %(name, help)
|
||||||
|
tw.line(line[:tw.fullwidth])
|
||||||
|
tw.line()
|
||||||
|
#tw.sep( "=")
|
||||||
|
|
||||||
|
conftest_options = [
|
||||||
('pytest_plugins', 'list of plugin names to load'),
|
('pytest_plugins', 'list of plugin names to load'),
|
||||||
('collect_ignore', '(relative) paths ignored during collection'),
|
]
|
||||||
('rsyncdirs', 'to-be-rsynced directories for dist-testing'),
|
|
||||||
)
|
|
||||||
|
|
||||||
def pytest_report_header(config):
|
def pytest_report_header(config):
|
||||||
lines = []
|
lines = []
|
||||||
if config.option.debug or config.option.traceconfig:
|
if config.option.debug or config.option.traceconfig:
|
||||||
lines.append("using py lib: %s" % (py.path.local(py.__file__).dirpath()))
|
lines.append("using: pytest-%s pylib-%s" %
|
||||||
|
(pytest.__version__,py.__version__))
|
||||||
|
|
||||||
if config.option.traceconfig:
|
if config.option.traceconfig:
|
||||||
lines.append("active plugins:")
|
lines.append("active plugins:")
|
||||||
plugins = []
|
plugins = []
|
||||||
|
@ -149,12 +140,11 @@ def getargs(func):
|
||||||
startindex = inspect.ismethod(func) and 1 or 0
|
startindex = inspect.ismethod(func) and 1 or 0
|
||||||
return args[startindex:]
|
return args[startindex:]
|
||||||
|
|
||||||
def collectattr(obj, prefixes=("pytest_",)):
|
def collectattr(obj):
|
||||||
methods = {}
|
methods = {}
|
||||||
for apiname in dir(obj):
|
for apiname in dir(obj):
|
||||||
for prefix in prefixes:
|
if apiname.startswith("pytest_"):
|
||||||
if apiname.startswith(prefix):
|
methods[apiname] = getattr(obj, apiname)
|
||||||
methods[apiname] = getattr(obj, apiname)
|
|
||||||
return methods
|
return methods
|
||||||
|
|
||||||
def formatdef(func):
|
def formatdef(func):
|
||||||
|
|
|
@ -242,9 +242,19 @@ class TmpTestdir:
|
||||||
def makefile(self, ext, *args, **kwargs):
|
def makefile(self, ext, *args, **kwargs):
|
||||||
return self._makefile(ext, args, kwargs)
|
return self._makefile(ext, args, kwargs)
|
||||||
|
|
||||||
|
def makeini(self, source):
|
||||||
|
return self.makefile('cfg', setup=source)
|
||||||
|
|
||||||
def makeconftest(self, source):
|
def makeconftest(self, source):
|
||||||
return self.makepyfile(conftest=source)
|
return self.makepyfile(conftest=source)
|
||||||
|
|
||||||
|
def makeini(self, source):
|
||||||
|
return self.makefile('.ini', tox=source)
|
||||||
|
|
||||||
|
def getinicfg(self, source):
|
||||||
|
p = self.makeini(source)
|
||||||
|
return py.iniconfig.IniConfig(p)['pytest']
|
||||||
|
|
||||||
def makepyfile(self, *args, **kwargs):
|
def makepyfile(self, *args, **kwargs):
|
||||||
return self._makefile('.py', args, kwargs)
|
return self._makefile('.py', args, kwargs)
|
||||||
|
|
||||||
|
@ -375,6 +385,11 @@ class TmpTestdir:
|
||||||
#config.pluginmanager.do_unconfigure(config)
|
#config.pluginmanager.do_unconfigure(config)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
def collect_by_name(self, modcol, name):
|
||||||
|
for colitem in modcol._memocollect():
|
||||||
|
if colitem.name == name:
|
||||||
|
return colitem
|
||||||
|
|
||||||
def popen(self, cmdargs, stdout, stderr, **kw):
|
def popen(self, cmdargs, stdout, stderr, **kw):
|
||||||
if not hasattr(py.std, 'subprocess'):
|
if not hasattr(py.std, 'subprocess'):
|
||||||
py.test.skip("no subprocess module")
|
py.test.skip("no subprocess module")
|
||||||
|
|
|
@ -9,6 +9,7 @@ import pytest
|
||||||
import os, sys
|
import os, sys
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
|
|
||||||
group = parser.getgroup("general", "running and selection options")
|
group = parser.getgroup("general", "running and selection options")
|
||||||
group._addoption('-x', '--exitfirst', action="store_true", default=False,
|
group._addoption('-x', '--exitfirst', action="store_true", default=False,
|
||||||
dest="exitfirst",
|
dest="exitfirst",
|
||||||
|
@ -32,6 +33,7 @@ def pytest_addoption(parser):
|
||||||
group.addoption('--basetemp', dest="basetemp", default=None, metavar="dir",
|
group.addoption('--basetemp', dest="basetemp", default=None, metavar="dir",
|
||||||
help="base temporary directory for this test run.")
|
help="base temporary directory for this test run.")
|
||||||
|
|
||||||
|
|
||||||
def pytest_namespace():
|
def pytest_namespace():
|
||||||
return dict(collect=dict(Item=Item, Collector=Collector,
|
return dict(collect=dict(Item=Item, Collector=Collector,
|
||||||
File=File, Directory=Directory))
|
File=File, Directory=Directory))
|
||||||
|
@ -64,7 +66,7 @@ def pytest_runtest_mainloop(session):
|
||||||
|
|
||||||
def pytest_ignore_collect(path, config):
|
def pytest_ignore_collect(path, config):
|
||||||
p = path.dirpath()
|
p = path.dirpath()
|
||||||
ignore_paths = config.getconftest_pathlist("collect_ignore", path=p)
|
ignore_paths = config._getconftest_pathlist("collect_ignore", path=p)
|
||||||
ignore_paths = ignore_paths or []
|
ignore_paths = ignore_paths or []
|
||||||
excludeopt = config.getvalue("ignore")
|
excludeopt = config.getvalue("ignore")
|
||||||
if excludeopt:
|
if excludeopt:
|
||||||
|
@ -128,7 +130,7 @@ class Session(object):
|
||||||
config.hook.pytest_sessionstart(session=self)
|
config.hook.pytest_sessionstart(session=self)
|
||||||
config.hook.pytest_perform_collection(session=self)
|
config.hook.pytest_perform_collection(session=self)
|
||||||
config.hook.pytest_runtest_mainloop(session=self)
|
config.hook.pytest_runtest_mainloop(session=self)
|
||||||
except self.config.Error:
|
except pytest.UsageError:
|
||||||
raise
|
raise
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
excinfo = py.code.ExceptionInfo()
|
excinfo = py.code.ExceptionInfo()
|
||||||
|
@ -173,10 +175,10 @@ class Collection:
|
||||||
parts = str(arg).split("::")
|
parts = str(arg).split("::")
|
||||||
path = base.join(parts[0], abs=True)
|
path = base.join(parts[0], abs=True)
|
||||||
if not path.check():
|
if not path.check():
|
||||||
raise self.config.Error("file not found: %s" %(path,))
|
raise pytest.UsageError("file not found: %s" %(path,))
|
||||||
topdir = self.topdir
|
topdir = self.topdir
|
||||||
if path != topdir and not path.relto(topdir):
|
if path != topdir and not path.relto(topdir):
|
||||||
raise self.config.Error("path %r is not relative to %r" %
|
raise pytest.UsageError("path %r is not relative to %r" %
|
||||||
(str(path), str(topdir)))
|
(str(path), str(topdir)))
|
||||||
topparts = path.relto(topdir).split(path.sep)
|
topparts = path.relto(topdir).split(path.sep)
|
||||||
return topparts + parts[1:]
|
return topparts + parts[1:]
|
||||||
|
@ -213,7 +215,7 @@ class Collection:
|
||||||
for node in self.matchnodes([self._topcollector], names):
|
for node in self.matchnodes([self._topcollector], names):
|
||||||
items.extend(self.genitems(node))
|
items.extend(self.genitems(node))
|
||||||
except NoMatch:
|
except NoMatch:
|
||||||
raise self.config.Error("can't collect: %s" % (arg,))
|
raise pytest.UsageError("can't collect: %s" % (arg,))
|
||||||
return items
|
return items
|
||||||
|
|
||||||
def matchnodes(self, matching, names):
|
def matchnodes(self, matching, names):
|
||||||
|
@ -444,14 +446,8 @@ class Collector(Node):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("abstract")
|
raise NotImplementedError("abstract")
|
||||||
|
|
||||||
def collect_by_name(self, name):
|
|
||||||
""" return a child matching the given name, else None. """
|
|
||||||
for colitem in self._memocollect():
|
|
||||||
if colitem.name == name:
|
|
||||||
return colitem
|
|
||||||
|
|
||||||
def repr_failure(self, excinfo):
|
def repr_failure(self, excinfo):
|
||||||
""" represent a failure. """
|
""" represent a collection failure. """
|
||||||
if excinfo.errisinstance(self.CollectError):
|
if excinfo.errisinstance(self.CollectError):
|
||||||
exc = excinfo.value
|
exc = excinfo.value
|
||||||
return str(exc.args[0])
|
return str(exc.args[0])
|
||||||
|
@ -524,8 +520,7 @@ class Directory(FSCollector):
|
||||||
|
|
||||||
class Item(Node):
|
class Item(Node):
|
||||||
""" a basic test invocation item. Note that for a single function
|
""" a basic test invocation item. Note that for a single function
|
||||||
there might be multiple test invocation items. Attributes:
|
there might be multiple test invocation items.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def reportinfo(self):
|
def reportinfo(self):
|
||||||
return self.fspath, None, ""
|
return self.fspath, None, ""
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -22,7 +22,7 @@ def main():
|
||||||
name='pytest',
|
name='pytest',
|
||||||
description='py.test: simple powerful testing with Python',
|
description='py.test: simple powerful testing with Python',
|
||||||
long_description = long_description,
|
long_description = long_description,
|
||||||
version='2.0.0.dev10',
|
version='2.0.0.dev11',
|
||||||
url='http://pytest.org',
|
url='http://pytest.org',
|
||||||
license='MIT license',
|
license='MIT license',
|
||||||
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
||||||
|
|
|
@ -4,7 +4,8 @@ class TestGeneralUsage:
|
||||||
def test_config_error(self, testdir):
|
def test_config_error(self, testdir):
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
raise config.Error("hello")
|
import pytest
|
||||||
|
raise pytest.UsageError("hello")
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest(testdir.tmpdir)
|
result = testdir.runpytest(testdir.tmpdir)
|
||||||
assert result.ret != 0
|
assert result.ret != 0
|
||||||
|
|
|
@ -2,9 +2,6 @@ import py
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
pytest_plugins = "pytester",
|
pytest_plugins = "pytester",
|
||||||
collect_ignore = ['../build', '../doc/_build']
|
|
||||||
|
|
||||||
rsyncdirs = ['conftest.py', '../pytest', '../doc', '.']
|
|
||||||
|
|
||||||
import os, py
|
import os, py
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
|
@ -41,6 +38,12 @@ def pytest_unconfigure(config, __multicall__):
|
||||||
assert len2 < config._numfiles + 7, out2
|
assert len2 < config._numfiles + 7, out2
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_runtest_setup(item):
|
||||||
|
item._oldir = py.path.local()
|
||||||
|
|
||||||
|
def pytest_runtest_teardown(item):
|
||||||
|
item._oldir.chdir()
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
multi = getattr(metafunc.function, 'multi', None)
|
multi = getattr(metafunc.function, 'multi', None)
|
||||||
if multi is not None:
|
if multi is not None:
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import py
|
import py
|
||||||
|
|
||||||
pytest_plugins = "pytester"
|
|
||||||
import pytest.plugin
|
import pytest.plugin
|
||||||
plugindir = py.path.local(pytest.plugin.__file__).dirpath()
|
plugindir = py.path.local(pytest.plugin.__file__).dirpath()
|
||||||
from pytest._core import default_plugins
|
from pytest._core import default_plugins
|
||||||
|
|
|
@ -19,13 +19,14 @@ class Standalone:
|
||||||
return testdir._run(anypython, self.script, *args)
|
return testdir._run(anypython, self.script, *args)
|
||||||
|
|
||||||
def test_gen(testdir, anypython, standalone):
|
def test_gen(testdir, anypython, standalone):
|
||||||
result = standalone.run(anypython, testdir, '-h')
|
|
||||||
assert result.ret == 0
|
|
||||||
result = standalone.run(anypython, testdir, '--version')
|
result = standalone.run(anypython, testdir, '--version')
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stderr.fnmatch_lines([
|
result.stderr.fnmatch_lines([
|
||||||
"*imported from*mypytest"
|
"*imported from*mypytest"
|
||||||
])
|
])
|
||||||
|
p = testdir.makepyfile("def test_func(): assert 0")
|
||||||
|
result = standalone.run(anypython, testdir, p)
|
||||||
|
assert result.ret != 0
|
||||||
|
|
||||||
@py.test.mark.xfail(reason="fix-dist", run=False)
|
@py.test.mark.xfail(reason="fix-dist", run=False)
|
||||||
def test_rundist(testdir, pytestconfig, standalone):
|
def test_rundist(testdir, pytestconfig, standalone):
|
||||||
|
|
|
@ -10,11 +10,13 @@ def test_version(testdir):
|
||||||
'*py.test*%s*imported from*' % (py.version, )
|
'*py.test*%s*imported from*' % (py.version, )
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_helpconfig(testdir):
|
def test_help(testdir):
|
||||||
result = testdir.runpytest("--help-config")
|
result = testdir.runpytest("--help")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*cmdline*conftest*ENV*",
|
"*-v*verbose*",
|
||||||
|
"*setup.cfg*",
|
||||||
|
"*minversion*",
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_collectattr():
|
def test_collectattr():
|
||||||
|
|
|
@ -268,9 +268,9 @@ class TestSorting:
|
||||||
def test_pass(): pass
|
def test_pass(): pass
|
||||||
def test_fail(): assert 0
|
def test_fail(): assert 0
|
||||||
""")
|
""")
|
||||||
fn1 = modcol.collect_by_name("test_pass")
|
fn1 = testdir.collect_by_name(modcol, "test_pass")
|
||||||
assert isinstance(fn1, py.test.collect.Function)
|
assert isinstance(fn1, py.test.collect.Function)
|
||||||
fn2 = modcol.collect_by_name("test_pass")
|
fn2 = testdir.collect_by_name(modcol, "test_pass")
|
||||||
assert isinstance(fn2, py.test.collect.Function)
|
assert isinstance(fn2, py.test.collect.Function)
|
||||||
|
|
||||||
assert fn1 == fn2
|
assert fn1 == fn2
|
||||||
|
@ -279,7 +279,7 @@ class TestSorting:
|
||||||
assert cmp(fn1, fn2) == 0
|
assert cmp(fn1, fn2) == 0
|
||||||
assert hash(fn1) == hash(fn2)
|
assert hash(fn1) == hash(fn2)
|
||||||
|
|
||||||
fn3 = modcol.collect_by_name("test_fail")
|
fn3 = testdir.collect_by_name(modcol, "test_fail")
|
||||||
assert isinstance(fn3, py.test.collect.Function)
|
assert isinstance(fn3, py.test.collect.Function)
|
||||||
assert not (fn1 == fn3)
|
assert not (fn1 == fn3)
|
||||||
assert fn1 != fn3
|
assert fn1 != fn3
|
||||||
|
@ -1092,7 +1092,7 @@ class TestReportInfo:
|
||||||
class TestClass:
|
class TestClass:
|
||||||
def test_hello(self): pass
|
def test_hello(self): pass
|
||||||
""")
|
""")
|
||||||
classcol = modcol.collect_by_name("TestClass")
|
classcol = testdir.collect_by_name(modcol, "TestClass")
|
||||||
fspath, lineno, msg = classcol.reportinfo()
|
fspath, lineno, msg = classcol.reportinfo()
|
||||||
assert fspath == modcol.fspath
|
assert fspath == modcol.fspath
|
||||||
assert lineno == 1
|
assert lineno == 1
|
||||||
|
@ -1106,7 +1106,7 @@ class TestReportInfo:
|
||||||
assert x
|
assert x
|
||||||
yield check, 3
|
yield check, 3
|
||||||
""")
|
""")
|
||||||
gencol = modcol.collect_by_name("test_gen")
|
gencol = testdir.collect_by_name(modcol, "test_gen")
|
||||||
fspath, lineno, modpath = gencol.reportinfo()
|
fspath, lineno, modpath = gencol.reportinfo()
|
||||||
assert fspath == modcol.fspath
|
assert fspath == modcol.fspath
|
||||||
assert lineno == 1
|
assert lineno == 1
|
||||||
|
|
|
@ -466,8 +466,8 @@ def test_getreportopt():
|
||||||
testdict.update(dict(reportchars="sfx"))
|
testdict.update(dict(reportchars="sfx"))
|
||||||
assert getreportopt(config) == "sfx"
|
assert getreportopt(config) == "sfx"
|
||||||
|
|
||||||
def test_terminalreporter_reportopt_conftestsetting(testdir):
|
def test_terminalreporter_reportopt_addargs(testdir):
|
||||||
testdir.makeconftest("option_report = 'skipped'")
|
testdir.makeini("[pytest]\naddargs=-rs")
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
def pytest_funcarg__tr(request):
|
def pytest_funcarg__tr(request):
|
||||||
tr = request.config.pluginmanager.getplugin("terminalreporter")
|
tr = request.config.pluginmanager.getplugin("terminalreporter")
|
||||||
|
|
|
@ -28,9 +28,9 @@ class TestCollector:
|
||||||
def test_pass(): pass
|
def test_pass(): pass
|
||||||
def test_fail(): assert 0
|
def test_fail(): assert 0
|
||||||
""")
|
""")
|
||||||
fn1 = modcol.collect_by_name("test_pass")
|
fn1 = testdir.collect_by_name(modcol, "test_pass")
|
||||||
assert isinstance(fn1, py.test.collect.Function)
|
assert isinstance(fn1, py.test.collect.Function)
|
||||||
fn2 = modcol.collect_by_name("test_pass")
|
fn2 = testdir.collect_by_name(modcol, "test_pass")
|
||||||
assert isinstance(fn2, py.test.collect.Function)
|
assert isinstance(fn2, py.test.collect.Function)
|
||||||
|
|
||||||
assert fn1 == fn2
|
assert fn1 == fn2
|
||||||
|
@ -39,7 +39,7 @@ class TestCollector:
|
||||||
assert cmp(fn1, fn2) == 0
|
assert cmp(fn1, fn2) == 0
|
||||||
assert hash(fn1) == hash(fn2)
|
assert hash(fn1) == hash(fn2)
|
||||||
|
|
||||||
fn3 = modcol.collect_by_name("test_fail")
|
fn3 = testdir.collect_by_name(modcol, "test_fail")
|
||||||
assert isinstance(fn3, py.test.collect.Function)
|
assert isinstance(fn3, py.test.collect.Function)
|
||||||
assert not (fn1 == fn3)
|
assert not (fn1 == fn3)
|
||||||
assert fn1 != fn3
|
assert fn1 != fn3
|
||||||
|
@ -57,8 +57,9 @@ class TestCollector:
|
||||||
def test_foo():
|
def test_foo():
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
cls = modcol.collect_by_name("TestClass")
|
cls = testdir.collect_by_name(modcol, "TestClass")
|
||||||
fn = cls.collect_by_name("()").collect_by_name("test_foo")
|
fn = testdir.collect_by_name(
|
||||||
|
testdir.collect_by_name(cls, "()"), "test_foo")
|
||||||
|
|
||||||
parent = fn.getparent(py.test.collect.Module)
|
parent = fn.getparent(py.test.collect.Module)
|
||||||
assert parent is modcol
|
assert parent is modcol
|
||||||
|
|
|
@ -1,32 +1,42 @@
|
||||||
import py
|
import py
|
||||||
|
|
||||||
class TestConfigCmdlineParsing:
|
from pytest.plugin.config import getcfg, Config
|
||||||
def test_parser_addoption_default_env(self, testdir, monkeypatch):
|
|
||||||
import os
|
|
||||||
config = testdir.Config()
|
|
||||||
group = config._parser.getgroup("hello")
|
|
||||||
|
|
||||||
monkeypatch.setitem(os.environ, 'PYTEST_OPTION_OPTION1', 'True')
|
class TestParseIni:
|
||||||
group.addoption("--option1", action="store_true")
|
def test_getcfg_and_config(self, tmpdir):
|
||||||
assert group.options[0].default == True
|
sub = tmpdir.mkdir("sub")
|
||||||
|
sub.chdir()
|
||||||
|
tmpdir.join("setup.cfg").write(py.code.Source("""
|
||||||
|
[pytest]
|
||||||
|
name = value
|
||||||
|
"""))
|
||||||
|
cfg = getcfg([sub], ["setup.cfg"])
|
||||||
|
assert cfg['name'] == "value"
|
||||||
|
config = Config()
|
||||||
|
config._preparse([sub])
|
||||||
|
assert config.inicfg['name'] == 'value'
|
||||||
|
|
||||||
monkeypatch.setitem(os.environ, 'PYTEST_OPTION_OPTION2', 'abc')
|
def test_append_parse_args(self, tmpdir):
|
||||||
group.addoption("--option2", action="store", default="x")
|
tmpdir.join("setup.cfg").write(py.code.Source("""
|
||||||
assert group.options[1].default == "abc"
|
[pytest]
|
||||||
|
addargs = --verbose
|
||||||
monkeypatch.setitem(os.environ, 'PYTEST_OPTION_OPTION3', '32')
|
"""))
|
||||||
group.addoption("--option3", action="store", type="int")
|
config = Config()
|
||||||
assert group.options[2].default == 32
|
config.parse([tmpdir])
|
||||||
|
|
||||||
group.addoption("--option4", action="store", type="int")
|
|
||||||
assert group.options[3].default == ("NO", "DEFAULT")
|
|
||||||
|
|
||||||
def test_parser_addoption_default_conftest(self, testdir, monkeypatch):
|
|
||||||
import os
|
|
||||||
testdir.makeconftest("option_verbose=True")
|
|
||||||
config = testdir.parseconfig()
|
|
||||||
assert config.option.verbose
|
assert config.option.verbose
|
||||||
|
|
||||||
|
def test_tox_ini_wrong_version(self, testdir):
|
||||||
|
p = 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*"
|
||||||
|
])
|
||||||
|
|
||||||
|
class TestConfigCmdlineParsing:
|
||||||
def test_parsing_again_fails(self, testdir):
|
def test_parsing_again_fails(self, testdir):
|
||||||
config = testdir.reparseconfig([testdir.tmpdir])
|
config = testdir.reparseconfig([testdir.tmpdir])
|
||||||
py.test.raises(AssertionError, "config.parse([])")
|
py.test.raises(AssertionError, "config.parse([])")
|
||||||
|
@ -97,13 +107,43 @@ class TestConfigAPI:
|
||||||
p = tmpdir.join("conftest.py")
|
p = tmpdir.join("conftest.py")
|
||||||
p.write("pathlist = ['.', %r]" % str(somepath))
|
p.write("pathlist = ['.', %r]" % str(somepath))
|
||||||
config = testdir.reparseconfig([p])
|
config = testdir.reparseconfig([p])
|
||||||
assert config.getconftest_pathlist('notexist') is None
|
assert config._getconftest_pathlist('notexist') is None
|
||||||
pl = config.getconftest_pathlist('pathlist')
|
pl = config._getconftest_pathlist('pathlist')
|
||||||
print(pl)
|
print(pl)
|
||||||
assert len(pl) == 2
|
assert len(pl) == 2
|
||||||
assert pl[0] == tmpdir
|
assert pl[0] == tmpdir
|
||||||
assert pl[1] == somepath
|
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"
|
||||||
|
py.test.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')
|
||||||
|
py.test.raises(ValueError, config.getini, 'other')
|
||||||
|
|
||||||
def test_options_on_small_file_do_not_blow_up(testdir):
|
def test_options_on_small_file_do_not_blow_up(testdir):
|
||||||
def runfiletest(opts):
|
def runfiletest(opts):
|
||||||
|
@ -140,3 +180,4 @@ def test_preparse_ordering(testdir, monkeypatch):
|
||||||
config = testdir.parseconfig()
|
config = testdir.parseconfig()
|
||||||
plugin = config.pluginmanager.getplugin("mytestplugin")
|
plugin = config.pluginmanager.getplugin("mytestplugin")
|
||||||
assert plugin.x == 42
|
assert plugin.x == 42
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import py
|
import py
|
||||||
from pytest.plugin import config as parseopt
|
from pytest.plugin import config as parseopt
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
class TestParser:
|
class TestParser:
|
||||||
def test_init(self, capsys):
|
def test_no_help_by_default(self, capsys):
|
||||||
parser = parseopt.Parser(usage="xyz")
|
parser = parseopt.Parser(usage="xyz")
|
||||||
py.test.raises(SystemExit, 'parser.parse(["-h"])')
|
py.test.raises(SystemExit, 'parser.parse(["-h"])')
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert out.find("xyz") != -1
|
assert err.find("no such option") != -1
|
||||||
|
|
||||||
def test_group_add_and_get(self):
|
def test_group_add_and_get(self):
|
||||||
parser = parseopt.Parser()
|
parser = parseopt.Parser()
|
||||||
|
@ -100,6 +101,7 @@ class TestParser:
|
||||||
assert option.hello == "world"
|
assert option.hello == "world"
|
||||||
assert option.this == 42
|
assert option.this == 42
|
||||||
|
|
||||||
|
|
||||||
@py.test.mark.skipif("sys.version_info < (2,5)")
|
@py.test.mark.skipif("sys.version_info < (2,5)")
|
||||||
def test_addoption_parser_epilog(testdir):
|
def test_addoption_parser_epilog(testdir):
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
|
|
4
tox.ini
4
tox.ini
|
@ -48,3 +48,7 @@ changedir=testing
|
||||||
commands=
|
commands=
|
||||||
{envpython} {envbindir}/py.test-jython --no-tools-on-path \
|
{envpython} {envbindir}/py.test-jython --no-tools-on-path \
|
||||||
-rfsxX --junitxml={envlogdir}/junit-{envname}2.xml [acceptance_test.py plugin]
|
-rfsxX --junitxml={envlogdir}/junit-{envname}2.xml [acceptance_test.py plugin]
|
||||||
|
|
||||||
|
[pytest]
|
||||||
|
minversion=2.0
|
||||||
|
plugins=pytester
|
||||||
|
|
Loading…
Reference in New Issue