commit
8a085c035a
17
CHANGELOG
17
CHANGELOG
|
@ -1,12 +1,19 @@
|
||||||
Changes between 1.0.0b7 and 1.0.0b8
|
Changes between 1.0.0b7 and 1.0.0b8
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
* docs: refined funcargs doc, use the term
|
* if plugins use "py.test.importorskip" for importing
|
||||||
"factory" instead of "provider", added a new
|
a dependency only a warning will be issued instead
|
||||||
talk/tutorial doc page
|
of exiting the testing process.
|
||||||
|
|
||||||
|
* docs:
|
||||||
|
- refined funcargs doc , use the term "factory" instead
|
||||||
|
of "provider"
|
||||||
|
- added a new talk/tutorial doc page
|
||||||
|
- better plugin docstrings
|
||||||
|
|
||||||
* fixed teardown problem related to partially failing funcarg setups
|
* fixed teardown problem related to partially failing funcarg setups
|
||||||
(thanks MrTopf for reporting)
|
(thanks MrTopf for reporting), "pytest_runtest_teardown" is now
|
||||||
|
always invoked even if the "pytest_runtest_setup" failed.
|
||||||
|
|
||||||
* tweaked doctest output for docstrings in py modules,
|
* tweaked doctest output for docstrings in py modules,
|
||||||
thanks Radomir.
|
thanks Radomir.
|
||||||
|
@ -34,6 +41,8 @@ Changes between 1.0.0b3 and 1.0.0b7
|
||||||
* resolve issue #18, multiprocessing.Manager() and
|
* resolve issue #18, multiprocessing.Manager() and
|
||||||
redirection clash
|
redirection clash
|
||||||
|
|
||||||
|
* make __name__ == "__channelexec__" for remote_exec code
|
||||||
|
|
||||||
Changes between 1.0.0b1 and 1.0.0b3
|
Changes between 1.0.0b1 and 1.0.0b3
|
||||||
=============================================
|
=============================================
|
||||||
|
|
||||||
|
|
44
MANIFEST
44
MANIFEST
|
@ -1,32 +1,18 @@
|
||||||
.hgtags
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
MANIFEST
|
|
||||||
doc/announce/release-1.0.0.txt
|
|
||||||
doc/download.txt
|
|
||||||
doc/test/funcargs.txt
|
|
||||||
py/__init__.py
|
|
||||||
py/io/stdcapture.py
|
|
||||||
py/io/testing/test_stdcapture.py
|
|
||||||
py/test/collect.py
|
|
||||||
py/test/funcargs.py
|
|
||||||
py/test/plugin/pytest_runner.py
|
|
||||||
py/test/plugin/pytest_terminal.py
|
|
||||||
py/test/pycollect.py
|
|
||||||
py/test/testing/test_funcargs.py
|
|
||||||
py/test/testing/test_genitems.py
|
|
||||||
setup.py
|
|
||||||
.hgignore
|
|
||||||
LICENSE
|
LICENSE
|
||||||
|
MANIFEST
|
||||||
README.txt
|
README.txt
|
||||||
_findpy.py
|
_findpy.py
|
||||||
doc/announce/release-0.9.0.txt
|
doc/announce/release-0.9.0.txt
|
||||||
doc/announce/release-0.9.2.txt
|
doc/announce/release-0.9.2.txt
|
||||||
|
doc/announce/release-1.0.0.txt
|
||||||
doc/announce/releases.txt
|
doc/announce/releases.txt
|
||||||
doc/bin.txt
|
doc/bin.txt
|
||||||
doc/code.txt
|
doc/code.txt
|
||||||
doc/confrest.py
|
doc/confrest.py
|
||||||
doc/conftest.py
|
doc/conftest.py
|
||||||
doc/contact.txt
|
doc/contact.txt
|
||||||
|
doc/download.txt
|
||||||
doc/execnet.txt
|
doc/execnet.txt
|
||||||
doc/img/pylib.png
|
doc/img/pylib.png
|
||||||
doc/index.txt
|
doc/index.txt
|
||||||
|
@ -41,11 +27,19 @@ doc/test/dist.txt
|
||||||
doc/test/examples.txt
|
doc/test/examples.txt
|
||||||
doc/test/extend.txt
|
doc/test/extend.txt
|
||||||
doc/test/features.txt
|
doc/test/features.txt
|
||||||
|
doc/test/funcargs.txt
|
||||||
doc/test/quickstart.txt
|
doc/test/quickstart.txt
|
||||||
|
doc/test/talks.txt
|
||||||
doc/test/test.txt
|
doc/test/test.txt
|
||||||
doc/test/xunit_setup.txt
|
doc/test/xunit_setup.txt
|
||||||
doc/xml.txt
|
doc/xml.txt
|
||||||
|
example/assertion/failure_demo.py
|
||||||
|
example/assertion/test_failures.py
|
||||||
|
example/assertion/test_setup_flow_example.py
|
||||||
example/execnet/popen_read_multiple.py
|
example/execnet/popen_read_multiple.py
|
||||||
|
example/execnet/svn-sync-repo.py
|
||||||
|
example/execnet/sysinfo.py
|
||||||
|
example/funcarg/conftest.py
|
||||||
example/funcarg/costlysetup/conftest.py
|
example/funcarg/costlysetup/conftest.py
|
||||||
example/funcarg/costlysetup/sub1/test_quick.py
|
example/funcarg/costlysetup/sub1/test_quick.py
|
||||||
example/funcarg/costlysetup/sub2/test_two.py
|
example/funcarg/costlysetup/sub2/test_two.py
|
||||||
|
@ -65,10 +59,8 @@ example/funcarg/test_simpleprovider.py
|
||||||
example/genhtml.py
|
example/genhtml.py
|
||||||
example/genhtmlcss.py
|
example/genhtmlcss.py
|
||||||
example/genxml.py
|
example/genxml.py
|
||||||
example/pytest/failure_demo.py
|
|
||||||
example/pytest/test_failures.py
|
|
||||||
example/pytest/test_setup_flow_example.py
|
|
||||||
py/LICENSE
|
py/LICENSE
|
||||||
|
py/__init__.py
|
||||||
py/_com.py
|
py/_com.py
|
||||||
py/bin/_findpy.py
|
py/bin/_findpy.py
|
||||||
py/bin/_genscripts.py
|
py/bin/_genscripts.py
|
||||||
|
@ -174,10 +166,12 @@ py/initpkg.py
|
||||||
py/io/__init__.py
|
py/io/__init__.py
|
||||||
py/io/dupfile.py
|
py/io/dupfile.py
|
||||||
py/io/fdcapture.py
|
py/io/fdcapture.py
|
||||||
|
py/io/stdcapture.py
|
||||||
py/io/terminalwriter.py
|
py/io/terminalwriter.py
|
||||||
py/io/testing/__init__.py
|
py/io/testing/__init__.py
|
||||||
py/io/testing/test_dupfile.py
|
py/io/testing/test_dupfile.py
|
||||||
py/io/testing/test_fdcapture.py
|
py/io/testing/test_fdcapture.py
|
||||||
|
py/io/testing/test_stdcapture.py
|
||||||
py/io/testing/test_terminalwriter.py
|
py/io/testing/test_terminalwriter.py
|
||||||
py/log/__init__.py
|
py/log/__init__.py
|
||||||
py/log/consumer.py
|
py/log/consumer.py
|
||||||
|
@ -292,6 +286,7 @@ py/rest/testing/test_transform.py
|
||||||
py/rest/transform.py
|
py/rest/transform.py
|
||||||
py/test/__init__.py
|
py/test/__init__.py
|
||||||
py/test/cmdline.py
|
py/test/cmdline.py
|
||||||
|
py/test/collect.py
|
||||||
py/test/compat.py
|
py/test/compat.py
|
||||||
py/test/config.py
|
py/test/config.py
|
||||||
py/test/conftesthandle.py
|
py/test/conftesthandle.py
|
||||||
|
@ -306,6 +301,7 @@ py/test/dist/testing/test_mypickle.py
|
||||||
py/test/dist/testing/test_nodemanage.py
|
py/test/dist/testing/test_nodemanage.py
|
||||||
py/test/dist/testing/test_txnode.py
|
py/test/dist/testing/test_txnode.py
|
||||||
py/test/dist/txnode.py
|
py/test/dist/txnode.py
|
||||||
|
py/test/funcargs.py
|
||||||
py/test/looponfail/__init__.py
|
py/test/looponfail/__init__.py
|
||||||
py/test/looponfail/remote.py
|
py/test/looponfail/remote.py
|
||||||
py/test/looponfail/testing/__init__.py
|
py/test/looponfail/testing/__init__.py
|
||||||
|
@ -334,11 +330,15 @@ py/test/plugin/pytest_recwarn.py
|
||||||
py/test/plugin/pytest_restdoc.py
|
py/test/plugin/pytest_restdoc.py
|
||||||
py/test/plugin/pytest_resultdb.py
|
py/test/plugin/pytest_resultdb.py
|
||||||
py/test/plugin/pytest_resultlog.py
|
py/test/plugin/pytest_resultlog.py
|
||||||
|
py/test/plugin/pytest_runner.py
|
||||||
|
py/test/plugin/pytest_terminal.py
|
||||||
py/test/plugin/pytest_tmpdir.py
|
py/test/plugin/pytest_tmpdir.py
|
||||||
py/test/plugin/pytest_unittest.py
|
py/test/plugin/pytest_unittest.py
|
||||||
py/test/plugin/pytest_xfail.py
|
py/test/plugin/pytest_xfail.py
|
||||||
py/test/plugin/test_pytest_runner.py
|
py/test/plugin/test_pytest_runner.py
|
||||||
|
py/test/plugin/test_pytest_runner_xunit.py
|
||||||
py/test/pluginmanager.py
|
py/test/pluginmanager.py
|
||||||
|
py/test/pycollect.py
|
||||||
py/test/session.py
|
py/test/session.py
|
||||||
py/test/testing/__init__.py
|
py/test/testing/__init__.py
|
||||||
py/test/testing/acceptance_test.py
|
py/test/testing/acceptance_test.py
|
||||||
|
@ -353,6 +353,8 @@ py/test/testing/test_compat.py
|
||||||
py/test/testing/test_config.py
|
py/test/testing/test_config.py
|
||||||
py/test/testing/test_conftesthandle.py
|
py/test/testing/test_conftesthandle.py
|
||||||
py/test/testing/test_deprecated_api.py
|
py/test/testing/test_deprecated_api.py
|
||||||
|
py/test/testing/test_funcargs.py
|
||||||
|
py/test/testing/test_genitems.py
|
||||||
py/test/testing/test_outcome.py
|
py/test/testing/test_outcome.py
|
||||||
py/test/testing/test_parseopt.py
|
py/test/testing/test_parseopt.py
|
||||||
py/test/testing/test_pickling.py
|
py/test/testing/test_pickling.py
|
||||||
|
@ -360,7 +362,6 @@ py/test/testing/test_pluginmanager.py
|
||||||
py/test/testing/test_pycollect.py
|
py/test/testing/test_pycollect.py
|
||||||
py/test/testing/test_recording.py
|
py/test/testing/test_recording.py
|
||||||
py/test/testing/test_session.py
|
py/test/testing/test_session.py
|
||||||
py/test/testing/test_setup_functional.py
|
|
||||||
py/test/testing/test_traceback.py
|
py/test/testing/test_traceback.py
|
||||||
py/test/web/__init__.py
|
py/test/web/__init__.py
|
||||||
py/test/web/exception.py
|
py/test/web/exception.py
|
||||||
|
@ -384,3 +385,4 @@ py/xmlobj/testing/test_html.py
|
||||||
py/xmlobj/testing/test_xml.py
|
py/xmlobj/testing/test_xml.py
|
||||||
py/xmlobj/visit.py
|
py/xmlobj/visit.py
|
||||||
py/xmlobj/xml.py
|
py/xmlobj/xml.py
|
||||||
|
setup.py
|
|
@ -1,34 +1,49 @@
|
||||||
py.test / py lib 1.0.0: distributed testing and dynamic code deployment
|
py.test / py lib 1.0.0: new test plugins, funcargs and cleanups
|
||||||
============================================================================
|
============================================================================
|
||||||
|
|
||||||
Welcome to the 1.0 release bringing new flexibility and
|
Welcome to the 1.0 release bringing new flexibility and
|
||||||
power to testing with Python! Main news:
|
power to testing with Python. Main news:
|
||||||
|
|
||||||
* new py.test plugin architecture, some examples:
|
* improved test architecture, featuring super-simple project
|
||||||
|
specific or cross-project single-file plugins, e.g:
|
||||||
|
|
||||||
pytest_xfail.py: mark tests as "expected to fail"
|
* pytest_unittest.py: run traditional unittest.py tests
|
||||||
pytest_pocoo.py: automatically send tracebacks to pocoo paste service
|
* pytest_xfail.py: mark tests as "expected to fail"
|
||||||
pytest_monkeypatch.py: safely patch parts of your environment in a test function
|
* pytest_pocoo.py: automatically send tracebacks to pocoo paste service
|
||||||
pytest_figleaf.py: generate html coverage reports
|
* pytest_monkeypatch.py: safely patch parts of your environment in a test function
|
||||||
pytest_resultlog.py: generate buildbot-friendly output
|
* pytest_figleaf.py: generate html coverage reports
|
||||||
|
* pytest_resultlog.py: generate buildbot-friendly output
|
||||||
|
|
||||||
and much more!
|
and many more!
|
||||||
|
|
||||||
* funcargs - the new flexible mechanism for managing all your test setup/fixture needs!
|
* funcargs - bringing new flexibilty and zero-boilerplate to Python testing:
|
||||||
|
|
||||||
|
- cleanly separated test code and test configuration and test value setup
|
||||||
|
- ideal for integration and functional tests
|
||||||
|
- new generative tests -> deprecation of yield-generated tests
|
||||||
|
|
||||||
|
* distributed testing and distributed execution (py.execnet):
|
||||||
|
|
||||||
|
- new unified "TX" URL scheme for specifying remote resources
|
||||||
|
- new sync/async ways to handle multiple remote processes
|
||||||
|
- much improved documentation
|
||||||
|
|
||||||
* flexibly distribute tests to multiple computers from the command line
|
|
||||||
|
|
||||||
See the py.test documentation for more info:
|
See the py.test documentation for more info:
|
||||||
|
|
||||||
http://pytest.org
|
http://pytest.org
|
||||||
|
|
||||||
The py lib contains the py.test tool and offers its well-tested code
|
The py lib also got smaller and focuses on offering much of the
|
||||||
independently from the testing tool, mainly:
|
well-tested py.test code in independent namespaces:
|
||||||
|
|
||||||
* py.execnet: ad-hoc code distribution to SSH, Socket and local sub processes
|
* py.execnet: ad-hoc code distribution to SSH, Socket and local sub processes
|
||||||
* py.code: support for dynamically running and debugging python code
|
* py.code: higher-level introspection and dynamic generation of python code
|
||||||
* py.path: path abstractions over local and subversion files
|
* py.path: path abstractions over local and subversion files
|
||||||
|
|
||||||
|
Some non-strictly-test related code, notably greenlets/co-routines
|
||||||
|
and apigen now live on their own and have been removed, also simplifying
|
||||||
|
the installation procedures.
|
||||||
|
|
||||||
The whole package works well with Linux, OSX and Win32, on
|
The whole package works well with Linux, OSX and Win32, on
|
||||||
Python 2.3, 2.4, 2.5 and 2.6. (Expect Python3 compatibility soon!)
|
Python 2.3, 2.4, 2.5 and 2.6. (Expect Python3 compatibility soon!)
|
||||||
|
|
||||||
|
|
|
@ -238,3 +238,19 @@ socketserver::
|
||||||
socketgw = py.execnet.SocketGateway.new_remote(popengw, ("127.0.0.1", 0))
|
socketgw = py.execnet.SocketGateway.new_remote(popengw, ("127.0.0.1", 0))
|
||||||
|
|
||||||
print socketgw._rinfo() # print some info about the remote environment
|
print socketgw._rinfo() # print some info about the remote environment
|
||||||
|
|
||||||
|
|
||||||
|
Sending a module / checking if run through remote_exec
|
||||||
|
--------------------------------------------------------------
|
||||||
|
|
||||||
|
You can pass a module object to ``remote_exec`` in which case
|
||||||
|
its source code will be sent. No dependencies will be transferred
|
||||||
|
so the module must be self-contained or only use modules that are
|
||||||
|
installed on the "other" side. Module code can detect if it is
|
||||||
|
running in a remote_exec situation by checking for the special
|
||||||
|
``__name__`` attribute like this::
|
||||||
|
|
||||||
|
if __name__ == '__channelexec__':
|
||||||
|
# ... call module functions ...
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,6 @@ def test_failure_demo_fails_properly(testdir):
|
||||||
passed, skipped, failed = reprec.countoutcomes()
|
passed, skipped, failed = reprec.countoutcomes()
|
||||||
assert passed == 0
|
assert passed == 0
|
||||||
assert failed == 20, failed
|
assert failed == 20, failed
|
||||||
colreports = reprec.getnamed("collectionreport")
|
colreports = reprec.getreports("pytest_collectreport")
|
||||||
failed = len([x.failed for x in colreports])
|
failed = len([x.failed for x in colreports])
|
||||||
assert failed == 5
|
assert failed == 5
|
|
@ -0,0 +1,3 @@
|
||||||
|
import py
|
||||||
|
|
||||||
|
collect_ignore = 'mysetup', 'mysetup2', 'test_simpleprovider.py', 'parametrize'
|
|
@ -230,7 +230,7 @@ class Gateway(object):
|
||||||
from sys import exc_info
|
from sys import exc_info
|
||||||
channel, (source, outid, errid) = item
|
channel, (source, outid, errid) = item
|
||||||
try:
|
try:
|
||||||
loc = { 'channel' : channel }
|
loc = { 'channel' : channel, '__name__': '__channelexec__'}
|
||||||
self._trace("execution starts:", repr(source)[:50])
|
self._trace("execution starts:", repr(source)[:50])
|
||||||
close = self._local_redirect_thread_output(outid, errid)
|
close = self._local_redirect_thread_output(outid, errid)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -92,6 +92,11 @@ class BasicRemoteExecution:
|
||||||
def test_repr_doesnt_crash(self):
|
def test_repr_doesnt_crash(self):
|
||||||
assert isinstance(repr(self), str)
|
assert isinstance(repr(self), str)
|
||||||
|
|
||||||
|
def test_attribute__name__(self):
|
||||||
|
channel = self.gw.remote_exec("channel.send(__name__)")
|
||||||
|
name = channel.receive()
|
||||||
|
assert name == "__channelexec__"
|
||||||
|
|
||||||
def test_correct_setup_no_py(self):
|
def test_correct_setup_no_py(self):
|
||||||
channel = self.gw.remote_exec("""
|
channel = self.gw.remote_exec("""
|
||||||
import sys
|
import sys
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
""" Plugin implementing defaults and general options. """
|
""" default hooks and general py.test options. """
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ def pytest_addoption(parser):
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
fixoptions(config)
|
fixoptions(config)
|
||||||
setsession(config)
|
setsession(config)
|
||||||
loadplugins(config)
|
#xxxloadplugins(config)
|
||||||
|
|
||||||
def fixoptions(config):
|
def fixoptions(config):
|
||||||
if config.option.numprocesses:
|
if config.option.numprocesses:
|
||||||
|
@ -124,7 +124,7 @@ def fixoptions(config):
|
||||||
if config.option.distload:
|
if config.option.distload:
|
||||||
config.option.dist = "load"
|
config.option.dist = "load"
|
||||||
|
|
||||||
def loadplugins(config):
|
def xxxloadplugins(config):
|
||||||
for name in config.getvalue("plugin"):
|
for name in config.getvalue("plugin"):
|
||||||
print "importing", name
|
print "importing", name
|
||||||
config.pluginmanager.import_plugin(name)
|
config.pluginmanager.import_plugin(name)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
automatically collect and execute doctests.
|
collect and execute doctests from modules and test files.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
cleanup gateways that were instantiated during a test function run.
|
cleanup execnet gateways during test function runs.
|
||||||
"""
|
"""
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,7 @@ write and report coverage data using the 'figleaf' module.
|
||||||
"""
|
"""
|
||||||
import py
|
import py
|
||||||
|
|
||||||
figleaf = py.test.importorskip("figleaf")
|
figleaf = py.test.importorskip("figleaf.annotate_html")
|
||||||
import figleaf.annotate_html
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
group = parser.addgroup('figleaf options')
|
group = parser.addgroup('figleaf options')
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
""" log calling of plugin hooks to a file. """
|
""" log invocations of extension hooks to a file. """
|
||||||
import py
|
import py
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""
|
"""
|
||||||
'capsys' and 'capfd' funcargs for capturing stdout/stderror either
|
'capsys' and 'capfd' funcargs for capturing stdout/stderror.
|
||||||
by intercepting sys.stdout/stderr or File Descriptors 1/2.
|
|
||||||
|
|
||||||
Calling the reset() method of the capture funcargs gives
|
Calling the reset() method of the capture funcargs gives
|
||||||
a out/err tuple of strings representing the captured streams.
|
a out/err tuple of strings representing the captured streams.
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""
|
"""
|
||||||
py.test.mark / keyword plugin
|
py.test.mark / keyword plugin
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""
|
"""
|
||||||
"monkeypatch" funcarg for safely patching objects,
|
safely patch object attributes, dicts and environment variables.
|
||||||
dictionaries and environment variables during the execution of
|
|
||||||
a test. "monkeypatch" has three helper functions:
|
the "monkeypatch" funcarg has three helper functions:
|
||||||
|
|
||||||
monkeypatch.setattr(obj, name, value)
|
monkeypatch.setattr(obj, name, value)
|
||||||
monkeypatch.setitem(obj, name, value)
|
monkeypatch.setitem(obj, name, value)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""
|
"""
|
||||||
interactive debugging with a PDB prompt.
|
interactive debugging with the Python Debugger.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import py
|
import py
|
||||||
import pdb, sys, linecache
|
import pdb, sys, linecache
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
py.test plugin for sending testing failure information to paste.pocoo.org
|
submit failure information to paste.pocoo.org
|
||||||
"""
|
"""
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
funcargs and support code for testing py.test functionality.
|
funcargs and support code for testing py.test's own functionality.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
help performing checks for deprecation and other warnings. Provides:
|
helpers for asserting deprecation and other warnings.
|
||||||
|
|
||||||
recwarn: function argument where one can call recwarn.pop() to get
|
recwarn: function argument where one can call recwarn.pop() to get
|
||||||
the last warning that would have been shown.
|
the last warning that would have been shown.
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""
|
"""
|
||||||
perform ReST specific tests on .txt files, including
|
perform ReST syntax, local and remote reference tests on .rst/.txt files.
|
||||||
linkchecks and remote URL checks.
|
|
||||||
"""
|
"""
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
@ -17,7 +16,7 @@ def pytest_addoption(parser):
|
||||||
help="force generation of html files.")
|
help="force generation of html files.")
|
||||||
|
|
||||||
def pytest_collect_file(path, parent):
|
def pytest_collect_file(path, parent):
|
||||||
if path.ext == ".txt":
|
if path.ext in (".txt", ".rst"):
|
||||||
project = getproject(path)
|
project = getproject(path)
|
||||||
if project is not None:
|
if project is not None:
|
||||||
return ReSTFile(path, parent=parent, project=project)
|
return ReSTFile(path, parent=parent, project=project)
|
||||||
|
@ -362,6 +361,7 @@ def test_deindent():
|
||||||
|
|
||||||
class TestApigenLinkRole:
|
class TestApigenLinkRole:
|
||||||
disabled = True
|
disabled = True
|
||||||
|
|
||||||
# these tests are moved here from the former py/doc/conftest.py
|
# these tests are moved here from the former py/doc/conftest.py
|
||||||
def test_resolve_linkrole(self):
|
def test_resolve_linkrole(self):
|
||||||
from py.__.doc.conftest import get_apigen_relpath
|
from py.__.doc.conftest import get_apigen_relpath
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
"""
|
"""
|
||||||
collect and run test items.
|
collect and run test items and create reports.
|
||||||
|
|
||||||
* executing test items
|
|
||||||
* running collectors
|
|
||||||
* and generating report events about it
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
@ -225,11 +221,14 @@ class SetupState(object):
|
||||||
colitem = self.stack.pop()
|
colitem = self.stack.pop()
|
||||||
self._teardown_with_finalization(colitem)
|
self._teardown_with_finalization(colitem)
|
||||||
|
|
||||||
def _teardown_with_finalization(self, colitem):
|
def _callfinalizers(self, colitem):
|
||||||
finalizers = self._finalizers.pop(colitem, None)
|
finalizers = self._finalizers.pop(colitem, None)
|
||||||
while finalizers:
|
while finalizers:
|
||||||
fin = finalizers.pop()
|
fin = finalizers.pop()
|
||||||
fin()
|
fin()
|
||||||
|
|
||||||
|
def _teardown_with_finalization(self, colitem):
|
||||||
|
self._callfinalizers(colitem)
|
||||||
if colitem:
|
if colitem:
|
||||||
colitem.teardown()
|
colitem.teardown()
|
||||||
for colitem in self._finalizers:
|
for colitem in self._finalizers:
|
||||||
|
@ -242,17 +241,19 @@ class SetupState(object):
|
||||||
assert not self._finalizers
|
assert not self._finalizers
|
||||||
|
|
||||||
def teardown_exact(self, item):
|
def teardown_exact(self, item):
|
||||||
assert self.stack and self.stack[-1] == item
|
if item == self.stack[-1]:
|
||||||
self._pop_and_teardown()
|
self._pop_and_teardown()
|
||||||
|
else:
|
||||||
|
self._callfinalizers(item)
|
||||||
|
|
||||||
def prepare(self, colitem):
|
def prepare(self, colitem):
|
||||||
""" setup objects along the collector chain to the test-method
|
""" setup objects along the collector chain to the test-method
|
||||||
Teardown any unneccessary previously setup objects."""
|
and teardown previously setup objects."""
|
||||||
needed_collectors = colitem.listchain()
|
needed_collectors = colitem.listchain()
|
||||||
while self.stack:
|
while self.stack:
|
||||||
if self.stack == needed_collectors[:len(self.stack)]:
|
if self.stack == needed_collectors[:len(self.stack)]:
|
||||||
break
|
break
|
||||||
self._pop_and_teardown()
|
self._pop_and_teardown()
|
||||||
for col in needed_collectors[len(self.stack):]:
|
for col in needed_collectors[len(self.stack):]:
|
||||||
self.stack.append(col)
|
|
||||||
col.setup()
|
col.setup()
|
||||||
|
self.stack.append(col)
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
"""
|
||||||
|
terminal reporting of the full testing process.
|
||||||
|
"""
|
||||||
import py
|
import py
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,8 @@ class BaseFunctionalTests:
|
||||||
#assert rep.skipped.reason == "hello"
|
#assert rep.skipped.reason == "hello"
|
||||||
#assert rep.skipped.location.lineno == 3
|
#assert rep.skipped.location.lineno == 3
|
||||||
#assert rep.skipped.location.lineno == 3
|
#assert rep.skipped.location.lineno == 3
|
||||||
assert len(reports) == 1
|
assert len(reports) == 2
|
||||||
|
assert reports[1].passed # teardown
|
||||||
|
|
||||||
def test_failure_in_setup_function(self, testdir):
|
def test_failure_in_setup_function(self, testdir):
|
||||||
reports = testdir.runitem("""
|
reports = testdir.runitem("""
|
||||||
|
@ -101,7 +102,7 @@ class BaseFunctionalTests:
|
||||||
assert not rep.passed
|
assert not rep.passed
|
||||||
assert rep.failed
|
assert rep.failed
|
||||||
assert rep.when == "setup"
|
assert rep.when == "setup"
|
||||||
assert len(reports) == 1
|
assert len(reports) == 2
|
||||||
|
|
||||||
def test_failure_in_teardown_function(self, testdir):
|
def test_failure_in_teardown_function(self, testdir):
|
||||||
reports = testdir.runitem("""
|
reports = testdir.runitem("""
|
||||||
|
@ -156,7 +157,7 @@ class BaseFunctionalTests:
|
||||||
def test_func():
|
def test_func():
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
assert len(reports) == 1
|
assert len(reports) == 2
|
||||||
rep = reports[0]
|
rep = reports[0]
|
||||||
print rep
|
print rep
|
||||||
assert not rep.skipped
|
assert not rep.skipped
|
||||||
|
|
|
@ -55,6 +55,7 @@ def test_class_setup(testdir):
|
||||||
""")
|
""")
|
||||||
reprec.assertoutcome(passed=1+2+1)
|
reprec.assertoutcome(passed=1+2+1)
|
||||||
|
|
||||||
|
|
||||||
def test_method_setup(testdir):
|
def test_method_setup(testdir):
|
||||||
reprec = testdir.inline_runsource("""
|
reprec = testdir.inline_runsource("""
|
||||||
class TestSetupMethod:
|
class TestSetupMethod:
|
|
@ -3,6 +3,7 @@ managing loading and interacting with pytest plugins.
|
||||||
"""
|
"""
|
||||||
import py
|
import py
|
||||||
from py.__.test.plugin import hookspec
|
from py.__.test.plugin import hookspec
|
||||||
|
from py.__.test.outcome import Skipped
|
||||||
|
|
||||||
def check_old_use(mod, modname):
|
def check_old_use(mod, modname):
|
||||||
clsname = modname[len('pytest_'):].capitalize() + "Plugin"
|
clsname = modname[len('pytest_'):].capitalize() + "Plugin"
|
||||||
|
@ -96,11 +97,21 @@ class PluginManager(object):
|
||||||
modname = canonical_importname(spec)
|
modname = canonical_importname(spec)
|
||||||
if modname in self.impname2plugin:
|
if modname in self.impname2plugin:
|
||||||
return
|
return
|
||||||
|
try:
|
||||||
mod = importplugin(modname)
|
mod = importplugin(modname)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except Skipped, e:
|
||||||
|
self._warn("could not import plugin %r, reason: %r" %(
|
||||||
|
(modname, e.msg)))
|
||||||
|
else:
|
||||||
check_old_use(mod, modname)
|
check_old_use(mod, modname)
|
||||||
self.register(mod)
|
self.register(mod)
|
||||||
self.consider_module(mod)
|
self.consider_module(mod)
|
||||||
|
|
||||||
|
def _warn(self, msg):
|
||||||
|
print "===WARNING=== %s" % (msg,)
|
||||||
|
|
||||||
def _checkplugin(self, plugin):
|
def _checkplugin(self, plugin):
|
||||||
# =====================================================
|
# =====================================================
|
||||||
# check plugin hooks
|
# check plugin hooks
|
||||||
|
@ -243,3 +254,36 @@ def formatdef(func):
|
||||||
py.std.inspect.formatargspec(*py.std.inspect.getargspec(func))
|
py.std.inspect.formatargspec(*py.std.inspect.getargspec(func))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import py.__.test.plugin
|
||||||
|
basedir = py.path.local(py.__.test.plugin.__file__).dirpath()
|
||||||
|
name2text = {}
|
||||||
|
for p in basedir.listdir("pytest_*"):
|
||||||
|
if p.ext == ".py" or (
|
||||||
|
p.check(dir=1) and p.join("__init__.py").check()):
|
||||||
|
impname = p.purebasename
|
||||||
|
if impname.find("__") != -1:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
plugin = importplugin(impname)
|
||||||
|
except (ImportError, py.__.test.outcome.Skipped):
|
||||||
|
name2text[impname] = "IMPORT ERROR"
|
||||||
|
else:
|
||||||
|
doc = plugin.__doc__ or ""
|
||||||
|
doc = doc.strip()
|
||||||
|
name2text[impname] = doc
|
||||||
|
|
||||||
|
for name in sorted(name2text.keys()):
|
||||||
|
text = name2text[name]
|
||||||
|
if name[0] == "_":
|
||||||
|
continue
|
||||||
|
print "%-20s %s" % (name, text.split("\n")[0])
|
||||||
|
|
||||||
|
#text = py.std.textwrap.wrap(name2text[name],
|
||||||
|
# width = 80,
|
||||||
|
# initial_indent="%s: " % name,
|
||||||
|
# replace_whitespace = False)
|
||||||
|
#for line in text:
|
||||||
|
# print line
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,27 @@ class TestBootstrapping:
|
||||||
monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'nonexistingmodule')
|
monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'nonexistingmodule')
|
||||||
py.test.raises(ImportError, "pluginmanager.consider_env()")
|
py.test.raises(ImportError, "pluginmanager.consider_env()")
|
||||||
|
|
||||||
def test_preparse_args(self, monkeypatch):
|
def test_preparse_args(self):
|
||||||
pluginmanager = PluginManager()
|
pluginmanager = PluginManager()
|
||||||
py.test.raises(ImportError, """
|
py.test.raises(ImportError, """
|
||||||
pluginmanager.consider_preparse(["xyz", "-p", "hello123"])
|
pluginmanager.consider_preparse(["xyz", "-p", "hello123"])
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
def test_plugin_skip(self, testdir, monkeypatch):
|
||||||
|
testdir.makepyfile(pytest_skipping1="""
|
||||||
|
import py
|
||||||
|
py.test.skip("hello")
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest("-p", "skipping1")
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*WARNING*could not import plugin*skipping1*hello*"
|
||||||
|
])
|
||||||
|
monkeypatch.setenv("PYTEST_PLUGINS", "skipping1")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*WARNING*could not import plugin*skipping1*hello*"
|
||||||
|
])
|
||||||
|
|
||||||
def test_consider_env_plugin_instantiation(self, testdir, monkeypatch):
|
def test_consider_env_plugin_instantiation(self, testdir, monkeypatch):
|
||||||
pluginmanager = PluginManager()
|
pluginmanager = PluginManager()
|
||||||
testdir.syspathinsert()
|
testdir.syspathinsert()
|
||||||
|
@ -221,30 +236,6 @@ class TestPytestPluginInteractions:
|
||||||
assert not pluginmanager.listattr("hello")
|
assert not pluginmanager.listattr("hello")
|
||||||
assert pluginmanager.listattr("x") == [42]
|
assert pluginmanager.listattr("x") == [42]
|
||||||
|
|
||||||
@py.test.mark.xfail # setup call methods
|
|
||||||
def test_call_setup_participants(self, testdir):
|
|
||||||
testdir.makepyfile(
|
|
||||||
conftest="""
|
|
||||||
import py
|
|
||||||
def pytest_method(self, x):
|
|
||||||
return x+1
|
|
||||||
pytest_plugin = "pytest_someplugin",
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
testdir.makepyfile(pytest_someplugin="""
|
|
||||||
def pytest_method(self, x):
|
|
||||||
return x+1
|
|
||||||
""")
|
|
||||||
modcol = testdir.getmodulecol("""
|
|
||||||
def pytest_method(x):
|
|
||||||
return x+0
|
|
||||||
""")
|
|
||||||
l = []
|
|
||||||
call = modcol.config.pluginmanager.setupcall(modcol, "pytest_method", 1)
|
|
||||||
assert len(call.methods) == 3
|
|
||||||
results = call.execute()
|
|
||||||
assert results == [1,2,2]
|
|
||||||
|
|
||||||
def test_collectattr():
|
def test_collectattr():
|
||||||
class A:
|
class A:
|
||||||
def pytest_hello(self):
|
def pytest_hello(self):
|
||||||
|
|
|
@ -39,6 +39,7 @@ class TestModule:
|
||||||
modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',")
|
modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',")
|
||||||
py.test.raises(ImportError, "modcol.obj")
|
py.test.raises(ImportError, "modcol.obj")
|
||||||
|
|
||||||
|
class TestDisabled:
|
||||||
def test_disabled_module(self, testdir):
|
def test_disabled_module(self, testdir):
|
||||||
modcol = testdir.getmodulecol("""
|
modcol = testdir.getmodulecol("""
|
||||||
disabled = True
|
disabled = True
|
||||||
|
@ -51,7 +52,6 @@ class TestModule:
|
||||||
assert len(l) == 1
|
assert len(l) == 1
|
||||||
py.test.raises(Skipped, "modcol.setup()")
|
py.test.raises(Skipped, "modcol.setup()")
|
||||||
|
|
||||||
class TestClass:
|
|
||||||
def test_disabled_class(self, testdir):
|
def test_disabled_class(self, testdir):
|
||||||
modcol = testdir.getmodulecol("""
|
modcol = testdir.getmodulecol("""
|
||||||
class TestClass:
|
class TestClass:
|
||||||
|
@ -67,6 +67,15 @@ class TestClass:
|
||||||
assert len(l) == 1
|
assert len(l) == 1
|
||||||
py.test.raises(Skipped, "modcol.setup()")
|
py.test.raises(Skipped, "modcol.setup()")
|
||||||
|
|
||||||
|
def test_disabled_class_functional(self, testdir):
|
||||||
|
reprec = testdir.inline_runsource("""
|
||||||
|
class TestSimpleClassSetup:
|
||||||
|
disabled = True
|
||||||
|
def test_classlevel(self): pass
|
||||||
|
def test_classlevel2(self): pass
|
||||||
|
""")
|
||||||
|
reprec.assertoutcome(skipped=2)
|
||||||
|
|
||||||
class TestGenerator:
|
class TestGenerator:
|
||||||
def test_generative_functions(self, testdir):
|
def test_generative_functions(self, testdir):
|
||||||
modcol = testdir.getmodulecol("""
|
modcol = testdir.getmodulecol("""
|
||||||
|
|
Loading…
Reference in New Issue