Files
pytest2/contrib/pytest_twisted/__init__.py
holger krekel 3a9d2873b5 more or less fixes #4 along with changeset e07b15140498
note that i don't understand why the twisted plugin went
through the "wrapper" function.  seems to work just
fine for me without it.  also added the "True"
return value which indicates we handled the call.
however, the plugin hook will need some refinments.
see XXXs.

--HG--
branch : trunk
2009-04-17 19:33:38 +02:00

202 lines
6.6 KiB
Python

"""
Notes: twisted's asynchrone behavior may have influence on the order of test-functions
TODO:
+ credits to Ralf Schmitt See: http://twistedmatrix.com/pipermail/twisted-python/2007-February/014872.html
+ get test to work
"""
import os
import sys
import py
try:
from twisted.internet.defer import Deferred
except ImportError:
print "To use the twisted option you have to install twisted."
sys.exit(0)
try:
from greenlet import greenlet
except ImportError:
print "Since pylib 1.0 greenlet are removed and separately packaged: " \
"http://pypi.python.org/pypi/greenlet"
sys.exit(0)
def _start_twisted_logging():
"""Enables twisted internal logging"""
class Logger(object):
"""late-bound sys.stdout"""
def write(self, msg):
sys.stdout.write(msg)
def flush(self):
sys.stdout.flush()
# sys.stdout will be changed by py.test later.
import twisted.python.log
twisted.python.log.startLogging(Logger(), setStdout=0)
def _run_twisted(logging=False):
"""Start twisted mainloop and initialize recursive calling of doit()."""
from twisted.internet import reactor, defer
from twisted.python import log, failure
# make twisted copy traceback...
failure.Failure.cleanFailure = lambda *args: None
if logging:
_start_twisted_logging()
# recursively called for each test-function/method due done()
def doit(val): # val always None
# switch context to wait that wrapper() passes back to test-method
res = gr_tests.switch(val)
if res is None:
reactor.stop()
return
def done(res):
reactor.callLater(0.0, doit, None) # recursive call of doit()
def err(res):
reactor.callLater(0.0, doit, res)
# the test-function *may* return a deferred
# here the test-function will actually been called
# done() is finalizing a test-process by assureing recursive envoking
# of doit()
defer.maybeDeferred(res).addCallback(done).addErrback(err)
# initialy preparing the calling of doit() and starting the reactor
reactor.callLater(0.0, doit, None)
reactor.run()
class TwistedPlugin:
"""Allows to test twisted applications with pytest."""
def pytest_addoption(self, parser):
#parser.addoption("--twisted", dest="twisted",
# help="Allows to test twisted applications with pytest.")
group = parser.addgroup('twisted options')
group.addoption('-T', action='store_true', default=False,
dest = 'twisted',
help="Allows to test twisted applications.")
group.addoption('--twisted-logging', action='store', default=False,
dest='twisted_logging',
help="switch on twisted internal logging")
self.twisted = False
def pytest_configure(self, config):
twisted = config.getvalue("twisted")
twisted_logging = config.getvalue("twisted_logging")
if twisted:
self.twisted = True
gr_twisted.switch(twisted_logging)
def pytest_unconfigure(self, config):
if self.twisted:
gr_twisted.switch(None)
def pytest_pyfunc_call(self, pyfuncitem, *args, **kwargs):
if self.twisted:
# XXX1 kwargs?
# XXX2 we want to delegate actual call to next plugin
# (which may want to produce test coverage, etc.)
res = gr_twisted.switch(lambda: pyfuncitem.obj(*args))
if res:
res.raiseException()
return True # indicates that we performed the function call
gr_twisted = greenlet(_run_twisted)
gr_tests = greenlet.getcurrent()
# ===============================================================================
# plugin tests
# ===============================================================================
def test_generic(plugintester):
plugintester.apicheck(TwistedPlugin)
testdir = plugintester.testdir()
testdir.makepyfile('''
def test_pass():
pass
from twisted.internet import defer, reactor
from twisted.python import failure
from twisted.python import log
def test_no_deferred():
assert True is True
def test_deferred():
log.msg("test_deferred() called")
d = defer.Deferred()
def done():
log.msg("test_deferred.done() CALLBACK DONE")
d.callback(None)
reactor.callLater(2.5, done)
log.msg("test_deferred() returning deferred: %r" % (d,))
return d
def test_deferred2():
log.msg("test_deferred2() called")
d = defer.Deferred()
def done():
log.msg("test_deferred2.done() CALLBACK DONE")
d.callback(None)
reactor.callLater(2.5, done)
log.msg("test_deferred2() returning deferred: %r" % (d,))
return d
def test_deferred4():
log.msg("test_deferred4() called")
from twisted.web.client import getPage
def printContents(contents):
assert contents == ""
deferred = getPage('http://twistedmatrix.com/')
deferred.addCallback(printContents)
return deferred
def test_deferred3():
log.msg("test_deferred3() called")
d = defer.Deferred()
def done():
log.msg("test_deferred3.done() CALLBACK DONE")
d.callback(None)
reactor.callLater(2.5, done)
log.msg("test_deferred3() returning deferred: %r" % (d,))
return d
class TestTwistedSetupMethod:
def setup_method(self, method):
log.msg("TestTwistedSetupMethod.setup_method() called")
def test_deferred(self):
log.msg("TestTwistedSetupMethod.test_deferred() called")
d = defer.Deferred()
def done():
log.msg("TestTwistedSetupMethod.test_deferred() CALLBACK DONE")
d.callback(None)
reactor.callLater(2.5, done)
log.msg("TestTwistedSetupMethod.test_deferred() returning deferred: %r" % (d,))
return d
def test_defer_fail():
def fun():
log.msg("provoking NameError")
rsdfg
return defer.maybeDeferred(fun)
''')
testdir.runpytest("-T")
# XXX: what to do?
# s = testdir.tmpdir.join("event.log").read()
# assert s.find("TestrunFinish") != -1