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,10 +331,26 @@ 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: |     :members: | ||||||
| 
 | 
 | ||||||
| .. autoclass:: pytest.plugin.session.Node | .. 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: |         :members: | ||||||
| 
 | 
 | ||||||
| .. autoclass:: pytest.plugin.runner.CallInfo | .. autoclass:: pytest.plugin.runner.CallInfo | ||||||
|  | @ -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,11 +140,10 @@ 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 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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