merge trunk

This commit is contained in:
holger krekel 2010-10-31 18:17:08 +01:00
commit f73ab23003
33 changed files with 532 additions and 268 deletions

View File

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

View File

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

View File

@ -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`:

View File

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

View File

@ -1,7 +1,4 @@
Misc examples
====================================================
Detect if running from within a py.test run Detect if running from within a py.test run
-------------------------------------------------------------- --------------------------------------------------------------

View File

@ -1,6 +1,8 @@
.. highlightlang:: python .. highlightlang:: python
.. _mysetup:
mysetup pattern: application specific test fixtures mysetup pattern: application specific test fixtures
========================================================== ==========================================================

68
doc/example/nonpython.txt Normal file
View File

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

View File

@ -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. """

View File

@ -0,0 +1,7 @@
# test_simple.yml
ok:
sub1: sub1
hello:
world: world
some: other

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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'],
) )

View File

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

View File

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

View File

@ -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, ""

View File

@ -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'],

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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("""

View File

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