[svn r37278] move files from branch to trunk (and thus complete

the merge of the config branch into the trunk)

--HG--
branch : trunk
This commit is contained in:
hpk 2007-01-24 17:46:46 +01:00
parent 638e4318e4
commit 7cf9824680
74 changed files with 4201 additions and 6210 deletions

View File

@ -9,8 +9,8 @@ version = "0.8.80-alpha2"
initpkg(__name__, initpkg(__name__,
description = "py.test and the py lib", description = "py.test and the py lib",
revision = int('$LastChangedRevision: 37056 $'.split(':')[1][:-1]), revision = int('$LastChangedRevision: 37278 $'.split(':')[1][:-1]),
lastchangedate = '$LastChangedDate: 2007-01-20 13:11:01 +0100 (Sat, 20 Jan 2007) $', lastchangedate = '$LastChangedDate: 2007-01-24 17:46:46 +0100 (Wed, 24 Jan 2007) $',
version = version, version = version,
url = "http://codespeak.net/py", url = "http://codespeak.net/py",
download_url = "http://codespeak.net/download/py/%s.tar.gz" %(version,), download_url = "http://codespeak.net/download/py/%s.tar.gz" %(version,),
@ -32,7 +32,7 @@ initpkg(__name__,
'test.compat.TestCase' : ('./test/compat.py', 'TestCase'), 'test.compat.TestCase' : ('./test/compat.py', 'TestCase'),
# configuration/initialization related test api # configuration/initialization related test api
'test.config' : ('./test/config.py', 'config'), 'test.config' : ('./test/config.py', 'config_per_process'),
'test.ensuretemp' : ('./test/config.py', 'ensuretemp'), 'test.ensuretemp' : ('./test/config.py', 'ensuretemp'),
'test.cmdline.main' : ('./test/cmdline.py', 'main'), 'test.cmdline.main' : ('./test/cmdline.py', 'main'),
@ -93,6 +93,9 @@ initpkg(__name__,
'execnet.PopenGateway' : ('./execnet/register.py', 'PopenGateway'), 'execnet.PopenGateway' : ('./execnet/register.py', 'PopenGateway'),
'execnet.SshGateway' : ('./execnet/register.py', 'SshGateway'), 'execnet.SshGateway' : ('./execnet/register.py', 'SshGateway'),
# execnet scripts
'execnet.RSync' : ('./execnet/rsync.py', 'RSync'),
# input-output helping # input-output helping
'io.dupfile' : ('./io/dupfile.py', 'dupfile'), 'io.dupfile' : ('./io/dupfile.py', 'dupfile'),
'io.FDCapture' : ('./io/capture.py', 'FDCapture'), 'io.FDCapture' : ('./io/capture.py', 'FDCapture'),

View File

@ -1,132 +0,0 @@
"""\
the py lib is a development support library featuring
py.test, an interactive testing tool which supports
unit-testing with practically no boilerplate.
"""
from initpkg import initpkg
version = "0.8.80-alpha2"
initpkg(__name__,
description = "py.test and the py lib",
revision = int('$LastChangedRevision: 37268 $'.split(':')[1][:-1]),
lastchangedate = '$LastChangedDate: 2007-01-24 17:39:40 +0100 (Wed, 24 Jan 2007) $',
version = version,
url = "http://codespeak.net/py",
download_url = "http://codespeak.net/download/py/%s.tar.gz" %(version,),
license = "MIT license",
platforms = ['unix', 'linux', 'cygwin'],
author = "holger krekel & others",
author_email = "py-dev@codespeak.net",
long_description = globals()['__doc__'],
exportdefs = {
'_dist.setup' : ('./misc/_dist.py', 'setup'),
# helpers for use from test functions or collectors
'test.raises' : ('./test/raises.py', 'raises'),
'test.deprecated_call' : ('./test/deprecate.py', 'deprecated_call'),
'test.skip' : ('./test/item.py', 'skip'),
'test.fail' : ('./test/item.py', 'fail'),
'test.exit' : ('./test/session.py', 'exit'),
'test.compat.TestCase' : ('./test/compat.py', 'TestCase'),
# configuration/initialization related test api
'test.config' : ('./test/config.py', 'config_per_process'),
'test.ensuretemp' : ('./test/config.py', 'ensuretemp'),
'test.cmdline.main' : ('./test/cmdline.py', 'main'),
# for customization of collecting/running tests
'test.collect.Collector' : ('./test/collect.py', 'Collector'),
'test.collect.Directory' : ('./test/collect.py', 'Directory'),
'test.collect.Module' : ('./test/collect.py', 'Module'),
'test.collect.DoctestFile' : ('./test/collect.py', 'DoctestFile'),
'test.collect.Class' : ('./test/collect.py', 'Class'),
'test.collect.Instance' : ('./test/collect.py', 'Instance'),
'test.collect.Generator' : ('./test/collect.py', 'Generator'),
'test.Item' : ('./test/item.py', 'Item'),
'test.Function' : ('./test/item.py', 'Function'),
# thread related API (still in early design phase)
'_thread.WorkerPool' : ('./thread/pool.py', 'WorkerPool'),
'_thread.NamedThreadPool' : ('./thread/pool.py', 'NamedThreadPool'),
'_thread.ThreadOut' : ('./thread/io.py', 'ThreadOut'),
# hook into the top-level standard library
'std' : ('./misc/std.py', 'std'),
'process.cmdexec' : ('./process/cmdexec.py', 'cmdexec'),
# path implementations
'path.svnwc' : ('./path/svn/wccommand.py', 'SvnWCCommandPath'),
'path.svnurl' : ('./path/svn/urlcommand.py', 'SvnCommandPath'),
'path.local' : ('./path/local/local.py', 'LocalPath'),
'path.extpy' : ('./path/extpy/extpy.py', 'Extpy'),
# some nice slightly magic APIs
'magic.greenlet' : ('./magic/greenlet.py', 'greenlet'),
'magic.invoke' : ('./magic/invoke.py', 'invoke'),
'magic.revoke' : ('./magic/invoke.py', 'revoke'),
'magic.patch' : ('./magic/patch.py', 'patch'),
'magic.revert' : ('./magic/patch.py', 'revert'),
'magic.autopath' : ('./magic/autopath.py', 'autopath'),
'magic.AssertionError' : ('./magic/assertion.py', 'AssertionError'),
# python inspection/code-generation API
'code.compile' : ('./code/source.py', 'compile_'),
'code.Source' : ('./code/source.py', 'Source'),
'code.Code' : ('./code/code.py', 'Code'),
'code.Frame' : ('./code/frame.py', 'Frame'),
'code.ExceptionInfo' : ('./code/excinfo.py', 'ExceptionInfo'),
'code.Traceback' : ('./code/traceback2.py', 'Traceback'),
# backports and additions of builtins
'builtin.enumerate' : ('./builtin/enumerate.py', 'enumerate'),
'builtin.reversed' : ('./builtin/reversed.py', 'reversed'),
'builtin.sorted' : ('./builtin/sorted.py', 'sorted'),
'builtin.BaseException' : ('./builtin/exception.py', 'BaseException'),
'builtin.set' : ('./builtin/set.py', 'set'),
'builtin.frozenset' : ('./builtin/set.py', 'frozenset'),
# gateways into remote contexts
'execnet.SocketGateway' : ('./execnet/register.py', 'SocketGateway'),
'execnet.PopenGateway' : ('./execnet/register.py', 'PopenGateway'),
'execnet.SshGateway' : ('./execnet/register.py', 'SshGateway'),
# execnet scripts
'execnet.RSync' : ('./execnet/rsync.py', 'RSync'),
# input-output helping
'io.dupfile' : ('./io/dupfile.py', 'dupfile'),
'io.FDCapture' : ('./io/capture.py', 'FDCapture'),
'io.OutErrCapture' : ('./io/capture.py', 'OutErrCapture'),
'io.callcapture' : ('./io/capture.py', 'callcapture'),
# error module, defining all errno's as Classes
'error' : ('./misc/error.py', 'error'),
# small and mean xml/html generation
'xml.html' : ('./xmlobj/html.py', 'html'),
'xml.Tag' : ('./xmlobj/xml.py', 'Tag'),
'xml.raw' : ('./xmlobj/xml.py', 'raw'),
'xml.Namespace' : ('./xmlobj/xml.py', 'Namespace'),
'xml.escape' : ('./xmlobj/misc.py', 'escape'),
# logging API ('producers' and 'consumers' connected via keywords)
'log.Producer' : ('./log/producer.py', 'Producer'),
'log.default' : ('./log/producer.py', 'default'),
'log._getstate' : ('./log/producer.py', '_getstate'),
'log._setstate' : ('./log/producer.py', '_setstate'),
'log.setconsumer' : ('./log/consumer.py', 'setconsumer'),
'log.Path' : ('./log/consumer.py', 'Path'),
'log.STDOUT' : ('./log/consumer.py', 'STDOUT'),
'log.STDERR' : ('./log/consumer.py', 'STDERR'),
'log.Syslog' : ('./log/consumer.py', 'Syslog'),
'log.get' : ('./log/logger.py', 'get'),
# compatibility modules (taken from 2.4.4)
'compat.doctest' : ('./compat/doctest.py', '*'),
'compat.optparse' : ('./compat/optparse.py', '*'),
'compat.textwrap' : ('./compat/textwrap.py', '*'),
'compat.subprocess' : ('./compat/subprocess.py', '*'),
})

View File

@ -76,7 +76,7 @@ def test_apigen_functional():
parentdir = py.magic.autopath().dirpath().dirpath() parentdir = py.magic.autopath().dirpath().dirpath()
pkgdir = fs_root.join('pkg') pkgdir = fs_root.join('pkg')
try: try:
output = py.process.cmdexec('APIGEN_TARGET="%s" py.test --session=L ' output = py.process.cmdexec('APIGEN_TARGET="%s" py.test '
'--apigen="%s/apigen.py" "%s"' % ( '--apigen="%s/apigen.py" "%s"' % (
tempdir, parentdir, pkgdir)) tempdir, parentdir, pkgdir))
except py.error.Error, e: except py.error.Error, e:

View File

@ -1,116 +0,0 @@
""" functional test for apigen.py
script to build api + source docs from py.test
"""
import py
def setup_fs_project():
temp = py.test.ensuretemp('apigen_functional')
temp.ensure("pkg/func.py").write(py.code.Source("""\
def func(arg1):
"docstring"
def func_2(arg1, arg2):
return arg1(arg2)
"""))
temp.ensure('pkg/sometestclass.py').write(py.code.Source("""\
class SomeTestClass(object):
" docstring sometestclass "
def __init__(self, somevar):
self.somevar = somevar
def get_somevar(self):
" get_somevar docstring "
return self.somevar
"""))
temp.ensure('pkg/sometestsubclass.py').write(py.code.Source("""\
from sometestclass import SomeTestClass
class SomeTestSubClass(SomeTestClass):
" docstring sometestsubclass "
def get_somevar(self):
return self.somevar + 1
"""))
temp.ensure('pkg/somenamespace.py').write(py.code.Source("""\
def foo():
return 'bar'
def baz(qux):
return qux
"""))
temp.ensure("pkg/__init__.py").write(py.code.Source("""\
from py.initpkg import initpkg
initpkg(__name__, exportdefs = {
'main.sub.func': ("./func.py", "func"),
'main.func': ("./func.py", "func_2"),
'main.SomeTestClass': ('./sometestclass.py', 'SomeTestClass'),
'main.SomeTestSubClass': ('./sometestsubclass.py',
'SomeTestSubClass'),
})
"""))
temp.ensure('pkg/test/test_pkg.py').write(py.code.Source("""\
import py
py.std.sys.path.insert(0,
py.magic.autopath().dirpath().dirpath().dirpath().strpath)
import pkg
# this mainly exists to provide some data to the tracer
def test_pkg():
s = pkg.main.SomeTestClass(10)
assert s.get_somevar() == 10
s = pkg.main.SomeTestClass('10')
assert s.get_somevar() == '10'
s = pkg.main.SomeTestSubClass(10)
assert s.get_somevar() == 11
s = pkg.main.SomeTestSubClass('10')
py.test.raises(TypeError, 's.get_somevar()')
assert pkg.main.sub.func(10) is None
assert pkg.main.sub.func(20) is None
s = pkg.main.func(pkg.main.SomeTestClass, 10)
assert isinstance(s, pkg.main.SomeTestClass)
"""))
return temp, 'pkg'
def test_apigen_functional():
fs_root, package_name = setup_fs_project()
tempdir = py.test.ensuretemp('test_apigen_functional_results')
parentdir = py.magic.autopath().dirpath().dirpath()
pkgdir = fs_root.join('pkg')
try:
output = py.process.cmdexec('APIGEN_TARGET="%s" py.test '
'--apigen="%s/apigen.py" "%s"' % (
tempdir, parentdir, pkgdir))
except py.error.Error, e:
print e.out
raise
assert output.lower().find('traceback') == -1
# just some quick content checks
apidir = tempdir.join('api')
assert apidir.check(dir=True)
sometestclass_api = apidir.join('main.SomeTestClass.html')
assert sometestclass_api.check(file=True)
html = sometestclass_api.read()
assert '<a href="main.SomeTestClass.html">SomeTestClass</a>' in html
# XXX not linking to method files anymore
#sometestclass_init_api = apidir.join('main.SomeTestClass.__init__.html')
#assert sometestclass_init_api.check(file=True)
#assert sometestclass_init_api.read().find(
# '<a href="main.SomeTestClass.__init__.html">__init__</a>') > -1
namespace_api = apidir.join('main.html')
assert namespace_api.check(file=True)
html = namespace_api.read()
assert '<a href="main.SomeTestClass.html">SomeTestClass</a>' in html
sourcedir = tempdir.join('source')
assert sourcedir.check(dir=True)
sometestclass_source = sourcedir.join('sometestclass.py.html')
assert sometestclass_source.check(file=True)
html = sometestclass_source.read()
assert '<div class="project_title">sources for sometestclass.py</div>' in html
# XXX later...
#index = sourcedir.join('index.html')
#assert index.check(file=True)
#html = index.read()
#assert '<a href="main/index.html">main</a>' in html

View File

@ -23,6 +23,10 @@ class TracebackEntry(object):
return self.frame.code.path return self.frame.code.path
path = property(path, None, None, "path to the full source code") path = property(path, None, None, "path to the full source code")
def getlocals(self):
return self.frame.f_locals
locals = property(getlocals, None, None, "locals of underlaying frame")
def reinterpret(self): def reinterpret(self):
"""Reinterpret the failing statement and returns a detailed information """Reinterpret the failing statement and returns a detailed information
about what operations are performed.""" about what operations are performed."""
@ -78,6 +82,10 @@ class TracebackEntry(object):
line = "<could not get sourceline>" line = "<could not get sourceline>"
return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line) return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line)
def name(self):
return self.frame.code.raw.co_name
name = property(name, None, None, "co_name of underlaying code")
class Traceback(list): class Traceback(list):
""" Traceback objects encapsulate and offer higher level """ Traceback objects encapsulate and offer higher level
access to Traceback entries. access to Traceback entries.

View File

@ -94,7 +94,7 @@ testing
- running "py.test --looponfailing" - running "py.test --looponfailing"
- running "py.test" distributed on some hosts - running "py.test" distributed on some hosts
* fix --box on config branch (XXX guido - find tests that depend on each other) * consider --box on trunk (XXX guido - find tests that depend on each other)
* tests should not create any tempdirectories in the source code base * tests should not create any tempdirectories in the source code base
@ -109,15 +109,6 @@ testing
distributed testing distributed testing
---------------------- ----------------------
* the main rsession methods should have docstrings
explaining the purpose
(done)
* move RSync class to py.execnet.RSync, document its usage and
features in the class and method docstrings
(done)
code quality code quality
----------------- -----------------

View File

@ -1,561 +0,0 @@
Things to do before 0.9.0
=========================
py/bin
-----------
* review py/bin scripts abit
py.test
py.rest
py.lookup
py.cleanup
py.countloc
review all py lib documentation
-------------------------------------
* (hpk, in-progress) rename py/documentation to py/doc
(check web page and pypy usage of it)
streamline exported API
-------------------------------------
* (hpk, should be done) move not-to-be-exported Gateway() methods to _ - methods.
* docstrings for all exported API
* remove:
test.compat.TestCAse
* check and likely remove already deprecated API
* remove from public namespace:
XXX consider py.magic. invoke/revoke/patch/revert
* make "_" namespace:
py.path.extpy -> py.path._extpy
py.log -> py._log (pypy!)
* review py.io and write py.io.dupfile docstring (XXX guido)
* re-consider what to do with read and write methods of py.path classes (since
there are places that check for file-ness by doing hasattr(... 'write'))
(XXX guido talk to holger)
packaging
-------------------------------------
* debian and TAR/zip packages for py lib,
particularly look into C module issues (greenlet most importantly)
* do something about c-extensions both on unix-ish
versus win32 systems
* ensure compatibility with Python 2.3 - 2.5,
see what is missing for 2.2
* optional: support setuptools (eggs?) installs, and instally
from pypi (and register pylib there)
* see if things work on Win32 (partially done)
* refine and implement `releasescheme`_
.. _releasescheme: releasescheme.html
APIGEN / source viewer
-------------------------------------
* make py.test --apigen=PATH_TO_SCRIPT
collect tracing information and call the apigen
script to produce api and source code documentation
(done)
* deploy the above "py.test --apigen" run on codespeak
regularly, determine directory locations and URL namespace design.
* integrate rest directive into py/documentation/conftest.py
with help code from py.__.rest.directive....
make sure that the txt files in py/documentation/ use it
(done XXX functions/methods)
testing
-----------
* windows tests (rev 36514 passes without errors, many skips) (XXX guido)
* these should all work on 0.9 and on the py lib and pypy:
- running "py.test -s"
- running "py.test --pdb"
- running "py.test --looponfailing"
- running "py.test" distributed on some hosts
* consider --box on trunk (XXX guido - find tests that depend on each other)
* tests should not create any tempdirectories in the source code base
(done)
* try to be as 2.2 compatible as possible
(use e.g. py.builtin.enumerate instead of "enumerate" directly)
(done)
distributed testing
----------------------
code quality
-----------------
* no function implementation longer than 30 lines
* no lines longer than 80 characters
* review the pylib issue tracker
(cfbolz: done: what has a 0.8.0 or a 0.9.0 tag should be looked at again)
py.test
-------
* adjust py.test documentation to reflect new
collector/session architecture (mostly done)
* document py.test's conftest.py approach (somewhat done)
* py.test fails to parse strangely formatted code after assertion failure
Missing docstrings
------------------
::
code.Traceback.recursionindex misses a docstring
code.Traceback.filter misses a docstring
code.Traceback.cut misses a docstring
code.Traceback.__getitem__ misses a docstring
code.Traceback.Entry misses a docstring
code.Traceback.Entry.ishidden misses a docstring
code.Traceback.Entry.getfirstlinesource misses a docstring
code.Traceback.Entry.__str__ misses a docstring
code.Traceback.Entry.__repr__ misses a docstring
code.Traceback.Entry.__init__ misses a docstring
code.Source.getblockend misses a docstring
code.Source.__str__ misses a docstring
code.Source.__len__ misses a docstring
code.Source.__init__ misses a docstring
code.Source.__getslice__ misses a docstring
code.Source.__getitem__ misses a docstring
code.Source.__eq__ misses a docstring
code.Frame.repr misses a docstring
code.Frame.is_true misses a docstring
code.Frame.getargs misses a docstring
code.Frame.exec_ misses a docstring
code.Frame.eval misses a docstring
code.Frame.__init__ misses a docstring
code.ExceptionInfo.exconly misses a docstring
code.ExceptionInfo.errisinstance misses a docstring
code.ExceptionInfo.__str__ misses a docstring
code.ExceptionInfo.__init__ misses a docstring
code.Code misses a docstring
code.Code.source misses a docstring
code.Code.getargs misses a docstring
code.Code.__init__ misses a docstring
code.Code.__eq__ misses a docstring
execnet.SshGateway misses a docstring
execnet.SshGateway.join misses a docstring
execnet.SshGateway.exit misses a docstring
execnet.SshGateway.__repr__ misses a docstring
execnet.SshGateway.__init__ misses a docstring
execnet.SshGateway.ThreadOut.write misses a docstring
execnet.SshGateway.ThreadOut.setwritefunc misses a docstring
execnet.SshGateway.ThreadOut.setdefaultwriter misses a docstring
execnet.SshGateway.ThreadOut.resetdefault misses a docstring
execnet.SshGateway.ThreadOut.isatty misses a docstring
execnet.SshGateway.ThreadOut.flush misses a docstring
execnet.SshGateway.ThreadOut.delwritefunc misses a docstring
execnet.SshGateway.ThreadOut.deinstall misses a docstring
execnet.SocketGateway misses a docstring
execnet.SocketGateway.join misses a docstring
execnet.SocketGateway.exit misses a docstring
execnet.SocketGateway.__repr__ misses a docstring
execnet.SocketGateway.__init__ misses a docstring
execnet.PopenGateway misses a docstring
execnet.PopenGateway.remote_bootstrap_gateway misses a docstring
execnet.PopenGateway.join misses a docstring
execnet.PopenGateway.exit misses a docstring
execnet.PopenGateway.__repr__ misses a docstring
execnet.PopenGateway.__init__ misses a docstring
initpkg misses a docstring
io.dupfile misses a docstring
log.setconsumer misses a docstring
log.get misses a docstring
log.Syslog misses a docstring
log.STDOUT misses a docstring
log.STDERR misses a docstring
log.Producer.set_consumer misses a docstring
log.Producer.get_consumer misses a docstring
log.Producer.__repr__ misses a docstring
log.Producer.__init__ misses a docstring
log.Producer.__getattr__ misses a docstring
log.Producer.__call__ misses a docstring
log.Producer.Message misses a docstring
log.Producer.Message.prefix misses a docstring
log.Producer.Message.content misses a docstring
log.Producer.Message.__str__ misses a docstring
log.Producer.Message.__init__ misses a docstring
log.Path misses a docstring
log.Path.__init__ misses a docstring
log.Path.__call__ misses a docstring
magic.View.__viewkey__ misses a docstring
magic.View.__repr__ misses a docstring
magic.View.__new__ misses a docstring
magic.View.__matchkey__ misses a docstring
magic.View.__getattr__ misses a docstring
magic.AssertionError misses a docstring
path.svnwc.visit misses a docstring
path.svnwc.mkdir misses a docstring
path.svnwc.dump misses a docstring
path.svnwc.check misses a docstring
path.svnwc.add misses a docstring
path.svnwc.__str__ misses a docstring
path.svnwc.__repr__ misses a docstring
path.svnwc.__new__ misses a docstring
path.svnwc.__iter__ misses a docstring
path.svnwc.__hash__ misses a docstring
path.svnwc.__eq__ misses a docstring
path.svnwc.__div__ misses a docstring
path.svnwc.__contains__ misses a docstring
path.svnwc.__cmp__ misses a docstring
path.svnwc.__add__ misses a docstring
path.svnwc.Checkers misses a docstring
path.svnurl.visit misses a docstring
path.svnurl.check misses a docstring
path.svnurl.__repr__ misses a docstring
path.svnurl.__new__ misses a docstring
path.svnurl.__ne__ misses a docstring
path.svnurl.__iter__ misses a docstring
path.svnurl.__hash__ misses a docstring
path.svnurl.__div__ misses a docstring
path.svnurl.__contains__ misses a docstring
path.svnurl.__cmp__ misses a docstring
path.svnurl.__add__ misses a docstring
path.svnurl.Checkers misses a docstring
path.local.visit misses a docstring
path.local.sysexec has an 'XXX' in its docstring
path.local.check misses a docstring
path.local.__repr__ misses a docstring
path.local.__iter__ misses a docstring
path.local.__hash__ misses a docstring
path.local.__eq__ misses a docstring
path.local.__div__ misses a docstring
path.local.__contains__ misses a docstring
path.local.__cmp__ misses a docstring
path.local.__add__ misses a docstring
path.local.Checkers misses a docstring
path.extpy.visit misses a docstring
path.extpy.samefile misses a docstring
path.extpy.relto misses a docstring
path.extpy.listobj misses a docstring
path.extpy.listdir misses a docstring
path.extpy.join misses a docstring
path.extpy.getpymodule misses a docstring
path.extpy.getfilelineno misses a docstring
path.extpy.dirpath misses a docstring
path.extpy.check misses a docstring
path.extpy.__str__ misses a docstring
path.extpy.__repr__ misses a docstring
path.extpy.__new__ misses a docstring
path.extpy.__iter__ misses a docstring
path.extpy.__hash__ misses a docstring
path.extpy.__contains__ misses a docstring
path.extpy.__cmp__ misses a docstring
path.extpy.__add__ misses a docstring
path.extpy.Checkers misses a docstring
test.rest.RestReporter misses a docstring
test.rest.RestReporter.summary misses a docstring
test.rest.RestReporter.skips misses a docstring
test.rest.RestReporter.repr_traceback misses a docstring
test.rest.RestReporter.repr_source misses a docstring
test.rest.RestReporter.repr_signal misses a docstring
test.rest.RestReporter.repr_failure misses a docstring
test.rest.RestReporter.report_unknown misses a docstring
test.rest.RestReporter.report_TestStarted misses a docstring
test.rest.RestReporter.report_TestFinished misses a docstring
test.rest.RestReporter.report_SkippedTryiter misses a docstring
test.rest.RestReporter.report_SendItem misses a docstring
test.rest.RestReporter.report_RsyncFinished misses a docstring
test.rest.RestReporter.report_ReceivedItemOutcome misses a docstring
test.rest.RestReporter.report_Nodes misses a docstring
test.rest.RestReporter.report_ItemStart misses a docstring
test.rest.RestReporter.report_ImmediateFailure misses a docstring
test.rest.RestReporter.report_HostReady misses a docstring
test.rest.RestReporter.report_HostRSyncing misses a docstring
test.rest.RestReporter.report_FailedTryiter misses a docstring
test.rest.RestReporter.report misses a docstring
test.rest.RestReporter.print_summary misses a docstring
test.rest.RestReporter.prepare_source misses a docstring
test.rest.RestReporter.hangs misses a docstring
test.rest.RestReporter.get_rootpath misses a docstring
test.rest.RestReporter.get_path_from_item misses a docstring
test.rest.RestReporter.get_linkwriter misses a docstring
test.rest.RestReporter.get_item_name misses a docstring
test.rest.RestReporter.get_host misses a docstring
test.rest.RestReporter.failures misses a docstring
test.rest.RestReporter.add_rest misses a docstring
test.rest.RestReporter.__init__ misses a docstring
test.rest.RelLinkWriter misses a docstring
test.rest.RelLinkWriter.get_link misses a docstring
test.rest.NoLinkWriter misses a docstring
test.rest.NoLinkWriter.get_link misses a docstring
test.rest.LinkWriter misses a docstring
test.rest.LinkWriter.get_link misses a docstring
test.rest.LinkWriter.__init__ misses a docstring
test.exit misses a docstring
test.deprecated_call misses a docstring
test.collect.Module misses a docstring
test.collect.Module.tryiter has an 'XXX' in its docstring
test.collect.Module.teardown misses a docstring
test.collect.Module.startcapture misses a docstring
test.collect.Module.skipbykeyword misses a docstring
test.collect.Module.setup misses a docstring
test.collect.Module.run misses a docstring
test.collect.Module.makeitem misses a docstring
test.collect.Module.listnames misses a docstring
test.collect.Module.join misses a docstring
test.collect.Module.haskeyword misses a docstring
test.collect.Module.getsortvalue misses a docstring
test.collect.Module.getpathlineno misses a docstring
test.collect.Module.getouterr misses a docstring
test.collect.Module.getitembynames misses a docstring
test.collect.Module.funcnamefilter misses a docstring
test.collect.Module.finishcapture misses a docstring
test.collect.Module.classnamefilter misses a docstring
test.collect.Module.buildname2items misses a docstring
test.collect.Module.__repr__ misses a docstring
test.collect.Module.__ne__ misses a docstring
test.collect.Module.__init__ misses a docstring
test.collect.Module.__hash__ misses a docstring
test.collect.Module.__eq__ misses a docstring
test.collect.Module.__cmp__ misses a docstring
test.collect.Module.Skipped misses a docstring
test.collect.Module.Passed misses a docstring
test.collect.Module.Outcome misses a docstring
test.collect.Module.Failed misses a docstring
test.collect.Module.ExceptionFailure misses a docstring
test.collect.Instance misses a docstring
test.collect.Instance.tryiter has an 'XXX' in its docstring
test.collect.Instance.teardown misses a docstring
test.collect.Instance.startcapture misses a docstring
test.collect.Instance.skipbykeyword misses a docstring
test.collect.Instance.setup misses a docstring
test.collect.Instance.run misses a docstring
test.collect.Instance.makeitem misses a docstring
test.collect.Instance.listnames misses a docstring
test.collect.Instance.join misses a docstring
test.collect.Instance.haskeyword misses a docstring
test.collect.Instance.getsortvalue misses a docstring
test.collect.Instance.getpathlineno misses a docstring
test.collect.Instance.getouterr misses a docstring
test.collect.Instance.getitembynames misses a docstring
test.collect.Instance.funcnamefilter misses a docstring
test.collect.Instance.finishcapture misses a docstring
test.collect.Instance.classnamefilter misses a docstring
test.collect.Instance.buildname2items misses a docstring
test.collect.Instance.__repr__ misses a docstring
test.collect.Instance.__ne__ misses a docstring
test.collect.Instance.__init__ misses a docstring
test.collect.Instance.__hash__ misses a docstring
test.collect.Instance.__eq__ misses a docstring
test.collect.Instance.__cmp__ misses a docstring
test.collect.Generator misses a docstring
test.collect.Generator.tryiter has an 'XXX' in its docstring
test.collect.Generator.teardown misses a docstring
test.collect.Generator.startcapture misses a docstring
test.collect.Generator.skipbykeyword misses a docstring
test.collect.Generator.setup misses a docstring
test.collect.Generator.run misses a docstring
test.collect.Generator.listnames misses a docstring
test.collect.Generator.join misses a docstring
test.collect.Generator.haskeyword misses a docstring
test.collect.Generator.getsortvalue misses a docstring
test.collect.Generator.getpathlineno misses a docstring
test.collect.Generator.getouterr misses a docstring
test.collect.Generator.getitembynames misses a docstring
test.collect.Generator.getcallargs misses a docstring
test.collect.Generator.finishcapture misses a docstring
test.collect.Generator.buildname2items misses a docstring
test.collect.Generator.__repr__ misses a docstring
test.collect.Generator.__ne__ misses a docstring
test.collect.Generator.__init__ misses a docstring
test.collect.Generator.__hash__ misses a docstring
test.collect.Generator.__eq__ misses a docstring
test.collect.Generator.__cmp__ misses a docstring
test.collect.DoctestFile misses a docstring
test.collect.DoctestFile.tryiter has an 'XXX' in its docstring
test.collect.DoctestFile.teardown misses a docstring
test.collect.DoctestFile.startcapture misses a docstring
test.collect.DoctestFile.skipbykeyword misses a docstring
test.collect.DoctestFile.setup misses a docstring
test.collect.DoctestFile.run misses a docstring
test.collect.DoctestFile.makeitem misses a docstring
test.collect.DoctestFile.listnames misses a docstring
test.collect.DoctestFile.join misses a docstring
test.collect.DoctestFile.haskeyword misses a docstring
test.collect.DoctestFile.getsortvalue misses a docstring
test.collect.DoctestFile.getpathlineno misses a docstring
test.collect.DoctestFile.getouterr misses a docstring
test.collect.DoctestFile.getitembynames misses a docstring
test.collect.DoctestFile.funcnamefilter misses a docstring
test.collect.DoctestFile.finishcapture misses a docstring
test.collect.DoctestFile.classnamefilter misses a docstring
test.collect.DoctestFile.buildname2items misses a docstring
test.collect.DoctestFile.__repr__ misses a docstring
test.collect.DoctestFile.__ne__ misses a docstring
test.collect.DoctestFile.__init__ misses a docstring
test.collect.DoctestFile.__hash__ misses a docstring
test.collect.DoctestFile.__eq__ misses a docstring
test.collect.DoctestFile.__cmp__ misses a docstring
test.collect.Directory misses a docstring
test.collect.Directory.tryiter has an 'XXX' in its docstring
test.collect.Directory.teardown misses a docstring
test.collect.Directory.startcapture misses a docstring
test.collect.Directory.skipbykeyword misses a docstring
test.collect.Directory.setup misses a docstring
test.collect.Directory.run misses a docstring
test.collect.Directory.recfilter misses a docstring
test.collect.Directory.makeitem misses a docstring
test.collect.Directory.listnames misses a docstring
test.collect.Directory.join misses a docstring
test.collect.Directory.haskeyword misses a docstring
test.collect.Directory.getsortvalue misses a docstring
test.collect.Directory.getpathlineno misses a docstring
test.collect.Directory.getouterr misses a docstring
test.collect.Directory.getitembynames misses a docstring
test.collect.Directory.finishcapture misses a docstring
test.collect.Directory.filefilter misses a docstring
test.collect.Directory.buildname2items misses a docstring
test.collect.Directory.__repr__ misses a docstring
test.collect.Directory.__ne__ misses a docstring
test.collect.Directory.__init__ misses a docstring
test.collect.Directory.__hash__ misses a docstring
test.collect.Directory.__eq__ misses a docstring
test.collect.Directory.__cmp__ misses a docstring
test.collect.Collector misses a docstring
test.collect.Collector.tryiter has an 'XXX' in its docstring
test.collect.Collector.teardown misses a docstring
test.collect.Collector.startcapture misses a docstring
test.collect.Collector.skipbykeyword misses a docstring
test.collect.Collector.setup misses a docstring
test.collect.Collector.run misses a docstring
test.collect.Collector.listnames misses a docstring
test.collect.Collector.join misses a docstring
test.collect.Collector.haskeyword misses a docstring
test.collect.Collector.getsortvalue misses a docstring
test.collect.Collector.getpathlineno misses a docstring
test.collect.Collector.getouterr misses a docstring
test.collect.Collector.getitembynames misses a docstring
test.collect.Collector.finishcapture misses a docstring
test.collect.Collector.buildname2items misses a docstring
test.collect.Collector.__repr__ misses a docstring
test.collect.Collector.__ne__ misses a docstring
test.collect.Collector.__init__ misses a docstring
test.collect.Collector.__hash__ misses a docstring
test.collect.Collector.__eq__ misses a docstring
test.collect.Collector.__cmp__ misses a docstring
test.collect.Class misses a docstring
test.collect.Class.tryiter has an 'XXX' in its docstring
test.collect.Class.teardown misses a docstring
test.collect.Class.startcapture misses a docstring
test.collect.Class.skipbykeyword misses a docstring
test.collect.Class.setup misses a docstring
test.collect.Class.run misses a docstring
test.collect.Class.makeitem misses a docstring
test.collect.Class.listnames misses a docstring
test.collect.Class.join misses a docstring
test.collect.Class.haskeyword misses a docstring
test.collect.Class.getsortvalue misses a docstring
test.collect.Class.getpathlineno misses a docstring
test.collect.Class.getouterr misses a docstring
test.collect.Class.getitembynames misses a docstring
test.collect.Class.funcnamefilter misses a docstring
test.collect.Class.finishcapture misses a docstring
test.collect.Class.classnamefilter misses a docstring
test.collect.Class.buildname2items misses a docstring
test.collect.Class.__repr__ misses a docstring
test.collect.Class.__ne__ misses a docstring
test.collect.Class.__init__ misses a docstring
test.collect.Class.__hash__ misses a docstring
test.collect.Class.__eq__ misses a docstring
test.collect.Class.__cmp__ misses a docstring
test.cmdline.main misses a docstring
test.Item misses a docstring
test.Item.tryiter has an 'XXX' in its docstring
test.Item.teardown misses a docstring
test.Item.startcapture misses a docstring
test.Item.skipbykeyword misses a docstring
test.Item.setup misses a docstring
test.Item.run misses a docstring
test.Item.listnames misses a docstring
test.Item.join misses a docstring
test.Item.haskeyword misses a docstring
test.Item.getsortvalue misses a docstring
test.Item.getpathlineno misses a docstring
test.Item.getouterr misses a docstring
test.Item.getitembynames misses a docstring
test.Item.finishcapture misses a docstring
test.Item.buildname2items misses a docstring
test.Item.__repr__ misses a docstring
test.Item.__ne__ misses a docstring
test.Item.__init__ misses a docstring
test.Item.__hash__ misses a docstring
test.Item.__eq__ misses a docstring
test.Item.__cmp__ misses a docstring
test.Function.tryiter has an 'XXX' in its docstring
test.Function.teardown misses a docstring
test.Function.startcapture misses a docstring
test.Function.skipbykeyword misses a docstring
test.Function.setup misses a docstring
test.Function.run misses a docstring
test.Function.listnames misses a docstring
test.Function.join misses a docstring
test.Function.haskeyword misses a docstring
test.Function.getsortvalue misses a docstring
test.Function.getpathlineno misses a docstring
test.Function.getouterr misses a docstring
test.Function.getitembynames misses a docstring
test.Function.finishcapture misses a docstring
test.Function.buildname2items misses a docstring
test.Function.__repr__ misses a docstring
test.Function.__ne__ misses a docstring
test.Function.__init__ misses a docstring
test.Function.__hash__ misses a docstring
test.Function.__eq__ misses a docstring
test.Function.__cmp__ misses a docstring
test.Config.__init__ misses a docstring
xml.raw.__init__ misses a docstring
xml.html misses a docstring
xml.html.__tagclass__ misses a docstring
xml.html.__tagclass__.unicode misses a docstring
xml.html.__tagclass__.__unicode__ misses a docstring
xml.html.__tagclass__.__repr__ misses a docstring
xml.html.__tagclass__.__init__ misses a docstring
xml.html.__tagclass__.Attr misses a docstring
xml.html.__tagclass__.Attr.__init__ misses a docstring
xml.html.__metaclass__ misses a docstring
xml.html.__metaclass__.__getattr__ misses a docstring
xml.html.Style misses a docstring
xml.html.Style.__init__ misses a docstring
xml.escape misses a docstring
xml.Tag misses a docstring
xml.Tag.unicode misses a docstring
xml.Tag.__unicode__ misses a docstring
xml.Tag.__repr__ misses a docstring
xml.Tag.__init__ misses a docstring
xml.Namespace misses a docstring

View File

@ -4,7 +4,7 @@ failure_demo = py.magic.autopath().dirpath('failure_demo.py')
def test_failure_demo_fails_properly(): def test_failure_demo_fails_properly():
config = py.test.config._reparse([failure_demo]) config = py.test.config._reparse([failure_demo])
session = config.getsessionclass()(config, py.std.sys.stdout) session = config.initsession()
session.main() session.main()
l = session.getitemoutcomepairs(py.test.Item.Failed) l = session.getitemoutcomepairs(py.test.Item.Failed)
assert len(l) == 21 assert len(l) == 21

View File

@ -669,11 +669,14 @@ You must create a conftest.py in any parent directory above your tests.
The options that you need to specify in that conftest.py file are: The options that you need to specify in that conftest.py file are:
* `dist_hosts` - a list of ssh addresses (including a specific path if it * **`dist_hosts`**: a required list of ssh addresses (which each may
should be different than the default: ``$HOME/pytestcache-hostname``) include a path, default path is: ``$HOME/pytestcache-HOSTNAME``)
* `dist_rsync_roots` - a list of packages to copy to the remote machines. * `dist_rsync_roots` - a list of packages to copy to the remote machines.
* `dist_remotepython` - the remote python to run. * `dist_remotepython` - the remote python executable to run.
* `SessionOptions` - containing some specific tuning options * `dist_nicelevel` - process priority of remote nodes.
* `dist_boxing` - will run each single test in a separate process
(allowing to survive segfaults for example)
* `dist_taskspernode` - Maximum number of tasks being queued to remote nodes
Sample configuration:: Sample configuration::
@ -688,17 +691,6 @@ Sample configuration::
Running server is done by ``-w`` command line option or ``--startserver`` Running server is done by ``-w`` command line option or ``--startserver``
(the former might change at some point due to conflicts). (the former might change at some point due to conflicts).
Possible `SessionOptions` arguments:
* `nice_level` - Level of nice under which tests are run
* `runner_policy` - (for `LSession` only) - contains policy for the test boxig.
`"plain_runner"` means no boxing, `"box_runner"` means boxing.
* `waittime` - Default maximum waiting time for remote host to execute
one test.
* `max_tasks_per_node` - Maximum number of tasks which can be send to one node.
* `import_pypy` - Flag to control pypy importing for js regeneration (defaults
to False)
Development Notes Development Notes
----------------- -----------------

View File

@ -7,7 +7,6 @@ def setup_module(mod):
def test_doctest_basic(): def test_doctest_basic():
# XXX get rid of the next line: # XXX get rid of the next line:
py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py'))
tmpdir.ensure('__init__.py')
xtxt = tmpdir.join('x.txt') xtxt = tmpdir.join('x.txt')
xtxt.write(py.code.Source(""" xtxt.write(py.code.Source("""
@ -29,7 +28,7 @@ def test_doctest_basic():
end end
""")) """))
config = py.test.config._reparse([xtxt]) config = py.test.config._reparse([xtxt])
session = config.getsessionclass()(config, py.std.sys.stdout) session = config.initsession()
session.main() session.main()
l = session.getitemoutcomepairs(py.test.Item.Failed) l = session.getitemoutcomepairs(py.test.Item.Failed)
assert len(l) == 0 assert len(l) == 0
@ -47,7 +46,7 @@ def test_js_ignore():
.. _`blah`: javascript:some_function() .. _`blah`: javascript:some_function()
""")) """))
config = py.test.config._reparse([xtxt]) config = py.test.config._reparse([xtxt])
session = config.getsessionclass()(config, py.std.sys.stdout) session = config.initsession()
session.main() session.main()
l = session.getitemoutcomepairs(py.test.Item.Failed) l = session.getitemoutcomepairs(py.test.Item.Failed)
assert len(l) == 0 assert len(l) == 0

View File

@ -1,73 +0,0 @@
import py
def setup_module(mod):
mod.tmpdir = py.test.ensuretemp('docdoctest')
def test_doctest_basic():
# XXX get rid of the next line:
py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py'))
xtxt = tmpdir.join('x.txt')
xtxt.write(py.code.Source("""
..
>>> from os.path import abspath
hello world
>>> assert abspath
>>> i=3
>>> print i
3
yes yes
>>> i
3
end
"""))
config = py.test.config._reparse([xtxt])
session = config.initsession()
session.main()
l = session.getitemoutcomepairs(py.test.Item.Failed)
assert len(l) == 0
l = session.getitemoutcomepairs(py.test.Item.Passed)
l2 = session.getitemoutcomepairs(py.test.Item.Skipped)
assert len(l+l2) == 2
def test_js_ignore():
py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py'))
tmpdir.ensure('__init__.py')
xtxt = tmpdir.join('x.txt')
xtxt.write(py.code.Source("""
`blah`_
.. _`blah`: javascript:some_function()
"""))
config = py.test.config._reparse([xtxt])
session = config.initsession()
session.main()
l = session.getitemoutcomepairs(py.test.Item.Failed)
assert len(l) == 0
l = session.getitemoutcomepairs(py.test.Item.Passed)
l2 = session.getitemoutcomepairs(py.test.Item.Skipped)
assert len(l+l2) == 3
def test_resolve_linkrole():
from py.__.doc.conftest import resolve_linkrole
assert resolve_linkrole('api', 'py.foo.bar') == (
'py.foo.bar', '../../apigen/api/foo.bar.html')
assert resolve_linkrole('api', 'py.foo.bar()') == (
'py.foo.bar()', '../../apigen/api/foo.bar.html')
assert resolve_linkrole('api', 'py') == (
'py', '../../apigen/api/index.html')
py.test.raises(AssertionError, 'resolve_linkrole("api", "foo.bar")')
assert resolve_linkrole('source', 'py/foo/bar.py') == (
'py/foo/bar.py', '../../apigen/source/foo/bar.py.html')
assert resolve_linkrole('source', 'py/foo/') == (
'py/foo/', '../../apigen/source/foo/index.html')
assert resolve_linkrole('source', 'py/') == (
'py/', '../../apigen/source/index.html')
py.test.raises(AssertionError, 'resolve_linkrole("source", "/foo/bar/")')

View File

@ -1,162 +1,153 @@
import py, md5 import py, os, stat, md5
from Queue import Queue
def rsync(gw, sourcedir, destdir, **options): class RSync(object):
for name in options: """ This is an example usage of py.execnet - a sample RSync
assert name in ('delete',) protocol, which can perform syncing 1-to-n.
channel = gw.remote_exec(""" Sample usage: you instantiate this class, eventually providing a
import os, stat, shutil, md5 callback when rsyncing is done, than add some targets
destdir, options = channel.receive() (gateway + destdir) by running add_target and finally
modifiedfiles = [] invoking send() which will send provided source tree remotely.
def remove(path): There is limited support for symlinks, which means that symlinks
assert path.startswith(destdir) pointing to the sourcetree will be send "as is" while external
try: symlinks will be just copied (regardless of existance of such
os.unlink(path) a path on remote side)
except OSError: """
# assume it's a dir def __init__(self, callback=None, **options):
shutil.rmtree(path) for name in options:
assert name in ('delete')
self.options = options
self.callback = callback
self.channels = {}
self.receivequeue = Queue()
self.links = []
def receive_directory_structure(path, relcomponents): def filter(self, path):
#print "receive directory structure", path return True
try:
st = os.lstat(path) def add_target(self, gateway, destdir, finishedcallback=None):
except OSError: """ Adds a target for to-be-send data
st = None """
msg = channel.receive() def itemcallback(req):
if isinstance(msg, list): self.receivequeue.put((channel, req))
if st and not stat.S_ISDIR(st.st_mode): channel = gateway.remote_exec(REMOTE_SOURCE)
os.unlink(path) channel.setcallback(itemcallback, endmarker = None)
st = None channel.send((str(destdir), self.options))
if not st: self.channels[channel] = finishedcallback
os.mkdir(path)
entrynames = {} def send(self, sourcedir):
for entryname in msg: """ Sends a sourcedir to previously prepared targets
receive_directory_structure(os.path.join(path, entryname), """
relcomponents + [entryname]) self.sourcedir = str(sourcedir)
entrynames[entryname] = True # normalize a trailing '/' away
if options.get('delete'): self.sourcedir = os.path.dirname(os.path.join(self.sourcedir, 'x'))
for othername in os.listdir(path): # send directory structure and file timestamps/sizes
if othername not in entrynames: self._send_directory_structure(self.sourcedir)
otherpath = os.path.join(path, othername)
remove(otherpath) # paths and to_send are only used for doing
# progress-related callbacks
self.paths = {}
self.to_send = {}
# send modified file to clients
while self.channels:
channel, req = self.receivequeue.get()
if req is None:
# end-of-channel
if channel in self.channels:
# too early! we must have got an error
channel.waitclose()
# or else we raise one
raise IOError('connection unexpectedly closed: %s ' % (
channel.gateway,))
else: else:
if st and stat.S_ISREG(st.st_mode): command, data = req
f = file(path, 'rb') if command == "links":
for link in self.links:
channel.send(link)
# completion marker, this host is done
channel.send(42)
elif command == "done":
finishedcallback = self.channels.pop(channel)
if finishedcallback:
finishedcallback()
elif command == "ack":
if self.callback:
self.callback("ack", self.paths[data], channel)
elif command == "list_done":
# sum up all to send
if self.callback:
s = sum([self.paths[i] for i in self.to_send[channel]])
self.callback("list", s, channel)
elif command == "send":
modified_rel_path, checksum = data
modifiedpath = os.path.join(self.sourcedir, *modified_rel_path)
f = open(modifiedpath, 'rb')
data = f.read() data = f.read()
f.close()
mychecksum = md5.md5(data).digest()
else:
if st:
remove(path)
mychecksum = None
if mychecksum != msg:
channel.send(relcomponents)
modifiedfiles.append(path)
receive_directory_structure(destdir, [])
channel.send(None) # end marker
for path in modifiedfiles:
data = channel.receive()
f = open(path, 'wb')
f.write(data)
f.close()
""")
channel.send((str(destdir), options))
def send_directory_structure(path): # provide info to progress callback function
if path.check(dir=1): modified_rel_path = "/".join(modified_rel_path)
subpaths = path.listdir() self.paths[modified_rel_path] = len(data)
print "sending directory structure", path if channel not in self.to_send:
channel.send([p.basename for p in subpaths]) self.to_send[channel] = []
self.to_send[channel].append(modified_rel_path)
f.close()
if checksum is not None and checksum == md5.md5(data).digest():
data = None # not really modified
else:
# ! there is a reason for the interning:
# sharing multiple copies of the file's data
data = intern(data)
print '%s <= %s' % (
channel.gateway._getremoteaddress(),
modified_rel_path)
channel.send(data)
del data
else:
assert "Unknown command %s" % command
def _broadcast(self, msg):
for channel in self.channels:
channel.send(msg)
def _send_link(self, basename, linkpoint):
self.links.append(("link", basename, linkpoint))
def _send_directory_structure(self, path):
st = os.lstat(path)
if stat.S_ISREG(st.st_mode):
# regular file: send a timestamp/size pair
self._broadcast((st.st_mtime, st.st_size))
elif stat.S_ISDIR(st.st_mode):
# dir: send a list of entries
names = []
subpaths = []
for name in os.listdir(path):
p = os.path.join(path, name)
if self.filter(p):
names.append(name)
subpaths.append(p)
self._broadcast(names)
for p in subpaths: for p in subpaths:
send_directory_structure(p) self._send_directory_structure(p)
elif path.check(file=1): elif stat.S_ISLNK(st.st_mode):
data = path.read() linkpoint = os.readlink(path)
checksum = md5.md5(data).digest() basename = path[len(self.sourcedir) + 1:]
channel.send(checksum) if not linkpoint.startswith(os.sep):
# relative link, just send it
self._send_link(basename, linkpoint)
elif linkpoint.startswith(self.sourcedir):
self._send_link(basename, linkpoint[len(self.sourcedir) + 1:])
else:
self._send_link(basename, linkpoint)
self._broadcast(None)
else: else:
raise ValueError, "cannot sync %r" % (path,) raise ValueError, "cannot sync %r" % (path,)
send_directory_structure(sourcedir)
while True:
modified_rel_path = channel.receive()
if modified_rel_path is None:
break
modifiedpath = sourcedir.join(*modified_rel_path)
data = modifiedpath.read()
channel.send(data)
channel.waitclose()
def copy(gw, source, dest): REMOTE_SOURCE = py.path.local(__file__).dirpath().\
channel = gw.remote_exec(""" join('rsync_remote.py').open().read() + "\nf()"
import md5
localfilename = channel.receive()
try:
f = file(localfilename, 'rb')
existingdata = f.read()
f.close()
except (IOError, OSError):
mycrc = None
else:
mycrc = md5.md5(existingdata).digest()
remotecrc = channel.receive()
if remotecrc == mycrc:
channel.send(None)
else:
channel.send(localfilename)
newdata = channel.receive()
f = file(localfilename, 'wb')
f.write(newdata)
f.close()
""")
channel.send(str(dest))
f = file(str(source), 'rb')
localdata = f.read()
f.close()
channel.send(md5.md5(localdata).digest())
status = channel.receive()
if status is not None:
assert status == str(dest) # for now
channel.send(localdata)
channel.waitclose()
def setup_module(mod):
mod.gw = py.execnet.PopenGateway()
def teardown_module(mod):
mod.gw.exit()
def test_filecopy():
dir = py.test.ensuretemp('filecopy')
source = dir.ensure('source')
dest = dir.join('dest')
source.write('hello world')
copy(gw, source, dest)
assert dest.check(file=1)
assert dest.read() == 'hello world'
source.write('something else')
copy(gw, source, dest)
assert dest.check(file=1)
assert dest.read() == 'something else'
def test_dirsync():
base = py.test.ensuretemp('dirsync')
dest = base.join('dest')
source = base.mkdir('source')
for s in ('content1', 'content2'):
source.ensure('subdir', 'file1').write(s)
rsync(gw, source, dest)
assert dest.join('subdir').check(dir=1)
assert dest.join('subdir', 'file1').check(file=1)
assert dest.join('subdir', 'file1').read() == s
source.join('subdir').remove('file1')
rsync(gw, source, dest)
assert dest.join('subdir', 'file1').check(file=1)
rsync(gw, source, dest, delete=True)
assert not dest.join('subdir', 'file1').check()

View File

@ -76,7 +76,7 @@ def f():
assert _type == "link" assert _type == "link"
path = os.path.join(destdir, relpath) path = os.path.join(destdir, relpath)
try: try:
os.unlink(path) remove(path)
except OSError: except OSError:
pass pass

View File

@ -1,5 +1,5 @@
import py import py
from py.__.test.rsession.rsync import RSync from py.execnet import RSync
def setup_module(mod): def setup_module(mod):

View File

@ -1,6 +1,19 @@
import sys, os import sys, os
terminal_width = int(os.environ.get('COLUMNS', 80))-1 def get_terminal_width():
try:
import termios,fcntl,struct
call = fcntl.ioctl(0,termios.TIOCGWINSZ,"\000"*8)
height,width = struct.unpack( "hhhh", call ) [:2]
terminal_width = width
except (SystemExit, KeyboardInterrupt), e:
raise
except:
# FALLBACK
terminal_width = int(os.environ.get('COLUMNS', 80))-1
return terminal_width
terminal_width = get_terminal_width()
def ansi_print(text, esc, file=None, newline=True, flush=False): def ansi_print(text, esc, file=None, newline=True, flush=False):
if file is None: if file is None:

View File

@ -0,0 +1,21 @@
import os
from py.__.misc.terminal_helper import get_terminal_width
def test_terminal_width():
""" Dummy test for get_terminal_width
"""
assert get_terminal_width()
try:
def f(*args):
raise ValueError
import fcntl
ioctl = fcntl.ioctl
fcntl.ioctl = f
cols = os.environ.get('COLUMNS', None)
os.environ['COLUMNS'] = '42'
assert get_terminal_width() == 41
finally:
fcntl.ioctl = ioctl
if cols:
os.environ['COLUMNS'] = cols

View File

@ -189,7 +189,7 @@ class LocalPath(common.FSPathBase, PlatformMixin):
strpath += sep strpath += sep
strpath += arg strpath += arg
obj = self.new() obj = self.new()
obj.strpath = strpath obj.strpath = os.path.normpath(strpath)
return obj return obj
def __eq__(self, other): def __eq__(self, other):

View File

@ -17,6 +17,13 @@ class LocalSetup:
class TestLocalPath(LocalSetup, CommonFSTests): class TestLocalPath(LocalSetup, CommonFSTests):
def test_join_normpath(self):
assert self.tmpdir.join(".") == self.tmpdir
p = self.tmpdir.join("../%s" % self.tmpdir.basename)
assert p == self.tmpdir
p = self.tmpdir.join("..//%s/" % self.tmpdir.basename)
assert p == self.tmpdir
def test_gethash(self): def test_gethash(self):
import md5 import md5
import sha import sha

View File

@ -8,30 +8,9 @@ def main(args=None):
warn_about_missing_assertion() warn_about_missing_assertion()
if args is None: if args is None:
args = py.std.sys.argv[1:] args = py.std.sys.argv[1:]
elif isinstance(args, basestring):
args = args.split(" ")
config = py.test.config config = py.test.config
config.parse(args) config.parse(args)
sessionclass = config.getsessionclass() session = config.initsession()
# ok, some option checks
if config.option.startserver or config.option.runbrowser:
from py.__.test.rsession.rsession import AbstractSession, LSession
if not issubclass(sessionclass, AbstractSession):
print "Cannot use web server without (R|L)Session, using lsession"
sessionclass = LSession
if config.option.apigen:
from py.__.test.rsession.rsession import AbstractSession, LSession
if not issubclass(sessionclass, AbstractSession):
sessionclass = LSession
print "Cannot generate API without (R|L)Session, using lsession"
if config.option.restreport:
from py.__.test.rsession.rsession import AbstractSession, LSession
if not issubclass(sessionclass, AbstractSession):
sessionclass = LSession
session = sessionclass(config)
try: try:
failures = session.main() failures = session.main()
if failures: if failures:

View File

@ -29,31 +29,9 @@ import py
def configproperty(name): def configproperty(name):
def fget(self): def fget(self):
#print "retrieving %r property from %s" %(name, self.fspath) #print "retrieving %r property from %s" %(name, self.fspath)
return py.test.config.getvalue(name, self.fspath) return self.config.getvalue(name, self.fspath)
return property(fget) return property(fget)
def getfscollector(fspath):
if isinstance(fspath, str):
fspath = py.path.local(fspath)
if not fspath.check():
raise py.error.ENOENT(fspath)
pkgpath = fspath.pypkgpath()
if pkgpath is None:
if fspath.check(dir=1):
pkgpath = fspath
else:
pkgpath = fspath.dirpath()
Directory = py.test.config.getvalue('Directory', pkgpath)
current = Directory(pkgpath)
#print "pkgpath", pkgpath
names = filter(None, fspath.relto(pkgpath).split(fspath.sep))
for name in names:
current = current.join(name)
assert current, "joining %r resulted in None!" % (names,)
top = current.listchain()[0]
#top._config = config
return current
class Collector(object): class Collector(object):
""" Collector instances are iteratively generated """ Collector instances are iteratively generated
(through their run() and join() methods) (through their run() and join() methods)
@ -63,10 +41,10 @@ class Collector(object):
(or None if it is the root collector) (or None if it is the root collector)
name: basename of this collector object name: basename of this collector object
""" """
def __init__(self, name, parent=None): def __init__(self, name, parent=None):
self.name = name self.name = name
self.parent = parent self.parent = parent
self.option = getattr(parent, 'option', None) self.config = getattr(parent, 'config', py.test.config)
self.fspath = getattr(parent, 'fspath', None) self.fspath = getattr(parent, 'fspath', None)
Module = configproperty('Module') Module = configproperty('Module')
@ -187,9 +165,10 @@ class Collector(object):
namelist = namelist.split("/") namelist = namelist.split("/")
cur = self cur = self
for name in namelist: for name in namelist:
next = cur.join(name) if name:
assert next is not None, (cur, name, namelist) next = cur.join(name)
cur = next assert next is not None, (cur, name, namelist)
cur = next
return cur return cur
def haskeyword(self, keyword): def haskeyword(self, keyword):
@ -211,8 +190,10 @@ class Collector(object):
newl.append(x.name) newl.append(x.name)
return ".".join(newl) return ".".join(newl)
# XXX: Copied from session
def skipbykeyword(self, keyword): def skipbykeyword(self, keyword):
""" raise Skipped() exception if the given keyword
matches for this collector.
"""
if not keyword: if not keyword:
return return
chain = self.listchain() chain = self.listchain()
@ -342,15 +323,15 @@ class PyCollectorMixin(object):
d[name] = res d[name] = res
return d return d
def makeitem(self, name, obj, usefilters=True): def makeitem(self, name, obj, usefilters=True):
if (not usefilters or self.classnamefilter(name)) and \ if (not usefilters or self.classnamefilter(name)) and \
py.std.inspect.isclass(obj): py.std.inspect.isclass(obj):
return self.Class(name, parent=self) return self.Class(name, parent=self)
elif (not usefilters or self.funcnamefilter(name)) and callable(obj): elif (not usefilters or self.funcnamefilter(name)) and callable(obj):
if obj.func_code.co_flags & 32: # generator function if obj.func_code.co_flags & 32: # generator function
return self.Generator(name, parent=self) return self.Generator(name, parent=self)
else: else:
return self.Function(name, parent=self) return self.Function(name, parent=self)
def _prepare(self): def _prepare(self):
if not hasattr(self, '_name2items'): if not hasattr(self, '_name2items'):
@ -391,7 +372,7 @@ class Module(FSCollector, PyCollectorMixin):
return res return res
def startcapture(self): def startcapture(self):
if not self.option.nocapture: if not self.config.option.nocapture:
assert not hasattr(self, '_capture') assert not hasattr(self, '_capture')
#self._capture = py.io.OutErrCapture() #self._capture = py.io.OutErrCapture()
# XXX integrate this into py.io / refactor # XXX integrate this into py.io / refactor

View File

@ -26,32 +26,65 @@ class CmdOptions(object):
class Config(object): class Config(object):
""" central hub for dealing with configuration/initialization data. """ """ central hub for dealing with configuration/initialization data. """
Option = optparse.Option Option = optparse.Option
conftest = Conftest()
def __init__(self): def __init__(self):
self.option = CmdOptions() self.option = CmdOptions()
self._parser = optparse.OptionParser( self._parser = optparse.OptionParser(
usage="usage: %prog [options] [query] [filenames of tests]") usage="usage: %prog [options] [query] [filenames of tests]")
self._parsed = False self.conftest = Conftest()
self._initialized = False
self._overwrite_dict = {}
def parse(self, args): def parse(self, args):
""" parse cmdline arguments into this config object. """ parse 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 self._parsed, ( assert not self._initialized, (
"can only parse cmdline args once per Config object") "can only parse cmdline args once per Config object")
self._parsed = True self._initialized = True
self.conftest.setinitial(args) self.conftest.setinitial(args)
self.conftest.lget('adddefaultoptions')() self.conftest.rget('adddefaultoptions')()
args = [str(x) for x in args] args = [str(x) for x in args]
self._origargs = args cmdlineoption, args = self._parser.parse_args(args)
cmdlineoption, remaining = self._parser.parse_args(args)
self.option.__dict__.update(vars(cmdlineoption)) self.option.__dict__.update(vars(cmdlineoption))
fixoptions(self.option) # XXX fixing should be moved to sessions fixoptions(self.option) # XXX fixing should be moved to sessions
if not remaining: if not args:
remaining.append(py.std.os.getcwd()) args.append(py.std.os.getcwd())
self.remaining = remaining self.topdir = gettopdir(args)
self.args = args
def initdirect(self, topdir, repr, coltrails=None):
assert not self._initialized
self._initialized = True
self.topdir = py.path.local(topdir)
self.merge_repr(repr)
self._coltrails = coltrails
def getcolitems(self):
""" return initial collectors. """
trails = getattr(self, '_coltrails', None)
return [self._getcollector(path) for path in (trails or self.args)]
def _getcollector(self, path):
if isinstance(path, tuple):
relpath, names = path
fspath = self.topdir.join(relpath)
col = self._getcollector(fspath)
else:
path = py.path.local(path)
assert path.check(), "%s: path does not exist" %(path,)
col = self._getrootcollector(path)
names = path.relto(col.fspath).split(path.sep)
return col.getitembynames(names)
def _getrootcollector(self, path):
pkgpath = path.pypkgpath()
if pkgpath is None:
pkgpath = path.check(file=1) and path.dirpath() or path
col = self.conftest.rget("Directory", pkgpath)(pkgpath)
col.config = self
return col
def addoptions(self, groupname, *specs): def addoptions(self, groupname, *specs):
""" add a named group of options to the current testing session. """ add a named group of options to the current testing session.
This function gets invoked during testing session initialization. This function gets invoked during testing session initialization.
@ -70,79 +103,186 @@ class Config(object):
if path is None, lookup the value in the initial if path is None, lookup the value in the initial
conftest modules found during command line parsing. conftest modules found during command line parsing.
""" """
return self.conftest.rget(name, path) try:
return self._overwrite_dict[name]
except KeyError:
return self.conftest.rget(name, path)
def getsessionclass(self): def initsession(self):
""" return an initialized session object. """
cls = self._getsessionclass()
session = cls(self)
#session.fixoptions()
return session
def _getsessionclass(self):
""" return Session class determined from cmdline options """ return Session class determined from cmdline options
and looked up in initial config modules. and looked up in initial config modules.
""" """
sessionname = self.option.session + 'Session' if self.option.session is not None:
try: return self.conftest.rget(self.option.session)
return self.conftest.rget(sessionname) else:
except KeyError: name = self._getsessionname()
pass importpath = globals()[name]
sessionimportpaths = self.conftest.lget('sessionimportpaths') mod = __import__(importpath, None, None, '__doc__')
importpath = sessionimportpaths[sessionname] return getattr(mod, name)
mod = __import__(importpath, None, None, ['__doc__'])
return getattr(mod, sessionname) def _getsessionname(self):
""" return default session name as determined from options. """
name = 'TerminalSession'
if self.option.dist:
name = 'RSession'
elif self.option.tkinter:
name = 'TkinterSession'
else:
optnames = 'startserver runbrowser apigen restreport boxing'.split()
for opt in optnames:
if getattr(self.option, opt, False):
name = 'LSession'
break
else:
if self.getvalue('dist_boxing'):
name = 'LSession'
if self.option.looponfailing:
name = 'RemoteTerminalSession'
elif self.option.executable:
name = 'RemoteTerminalSession'
return name
def is_boxed(self):
# XXX probably not a good idea to have this special function ...
return self.option.boxing or self.getvalue("dist_boxing")
def _reparse(self, args): def _reparse(self, args):
""" this is used from tests that want to re-invoke parse(). """ """ this is used from tests that want to re-invoke parse(). """
global config global config_per_process
oldconfig = py.test.config oldconfig = py.test.config
try: try:
config = py.test.config = Config() config_per_process = py.test.config = Config()
config.parse(args) config_per_process.parse(args)
return config return config_per_process
finally: finally:
config = py.test.config = oldconfig config_per_process = py.test.config = oldconfig
def _overwrite(self, name, value):
""" this is used from tests to overwrite values irrespectives of conftests.
"""
self._overwrite_dict[name] = value
def make_repr(self, conftestnames, optnames=None):
""" return a marshallable representation
of conftest and cmdline options.
if optnames is None, all options
on self.option will be transferred.
"""
conftestdict = {}
for name in conftestnames:
value = self.getvalue(name)
checkmarshal(name, value)
conftestdict[name] = value
cmdlineopts = {}
if optnames is None:
optnames = dir(self.option)
for name in optnames:
if not name.startswith("_"):
value = getattr(self.option, name)
checkmarshal(name, value)
cmdlineopts[name] = value
l = []
for path in self.args:
path = py.path.local(path)
l.append(path.relto(self.topdir))
return l, conftestdict, cmdlineopts
def merge_repr(self, repr):
""" merge in the conftest and cmdline option values
found in the given representation (produced
by make_repr above).
The repr-contained conftest values are
stored on the default conftest module (last
priority) and the cmdline options on self.option.
"""
class override:
def __init__(self, d):
self.__dict__.update(d)
args, conftestdict, cmdlineopts = repr
self.args = [self.topdir.join(x) for x in args]
self.conftest.setinitial(self.args)
self.conftest._path2confmods[None].append(override(conftestdict))
for name, val in cmdlineopts.items():
setattr(self.option, name, val)
def get_collector_trail(self, collector):
""" provide a trail relative to the topdir,
which can be used to reconstruct the
collector (possibly on a different host
starting from a different topdir).
"""
chain = collector.listchain()
relpath = chain[0].fspath.relto(self.topdir)
if not relpath:
if chain[0].fspath == self.topdir:
relpath = "."
else:
raise ValueError("%r not relative to %s"
%(chain[0], self.topdir))
return relpath, tuple([x.name for x in chain[1:]])
# this is the one per-process instance of py.test configuration # this is the one per-process instance of py.test configuration
config = Config() config_per_process = Config()
# default import paths for sessions
TkinterSession = 'py.__.test.tkinter.reportsession'
TerminalSession = 'py.__.test.terminal.terminal'
TerminalSession = 'py.__.test.terminal.terminal'
RemoteTerminalSession = 'py.__.test.terminal.remote'
RSession = 'py.__.test.rsession.rsession'
LSession = 'py.__.test.rsession.rsession'
# #
# helpers # helpers
# #
def checkmarshal(name, value):
try:
py.std.marshal.dumps(value)
except ValueError:
raise ValueError("%s=%r is not marshallable" %(name, value))
def fixoptions(option): def fixoptions(option):
""" sanity checks and making option values canonical. """ """ sanity checks and making option values canonical. """
if option.looponfailing and option.usepdb:
raise ValueError, "--looponfailing together with --pdb not supported yet."
if option.executable and option.usepdb:
raise ValueError, "--exec together with --pdb not supported yet."
# setting a correct executable
remote = False
if option.executable is not None:
remote = True
exe = py.path.local(py.std.os.path.expanduser(option.executable))
if not exe.check():
exe = py.path.local.sysfind(option.executable)
assert exe.check()
option.executable = exe
else:
option.executable = py.std.sys.executable
# implied options
if option.usepdb: if option.usepdb:
if not option.nocapture: if not option.nocapture:
print "--usepdb currently implies --nocapture" #print "--pdb implies --nocapture"
option.nocapture = True option.nocapture = True
# make information available about wether we should/will be remote
option._remote = remote or option.looponfailing
option._fromremote = False
# setting a correct frontend session
if option.session:
name = option.session
elif option.tkinter:
name = 'tkinter'
else:
name = 'terminal'
name = name.capitalize()
option.session = name
if option.runbrowser and not option.startserver: if option.runbrowser and not option.startserver:
print "Cannot point browser when not starting server" #print "--runbrowser implies --startserver"
option.startserver = True option.startserver = True
# conflicting options
if option.looponfailing and option.usepdb:
raise ValueError, "--looponfailing together with --pdb not supported."
if option.looponfailing and option.dist:
raise ValueError, "--looponfailing together with --dist not supported."
if option.executable and option.usepdb:
raise ValueError, "--exec together with --pdb not supported."
def gettopdir(args):
""" return the top directory for the given paths.
if the common base dir resides in a python package
parent directory of the root package is returned.
"""
args = [py.path.local(arg) for arg in args]
p = reduce(py.path.local.common, args)
assert p, "cannot determine common basedir of %s" %(args,)
pkgdir = p.pypkgpath()
if pkgdir is None:
return p
else:
return pkgdir.dirpath()

View File

@ -1,288 +0,0 @@
from __future__ import generators
import py
from conftesthandle import Conftest
optparse = py.compat.optparse
# XXX move to Config class
basetemp = None
def ensuretemp(string, dir=1):
""" return temporary directory path with
the given string as the trailing part.
"""
global basetemp
if basetemp is None:
basetemp = py.path.local.make_numbered_dir(prefix='pytest-')
return basetemp.ensure(string, dir=dir)
class CmdOptions(object):
""" pure container instance for holding cmdline options
as attributes.
"""
def __repr__(self):
return "<CmdOptions %r>" %(self.__dict__,)
class Config(object):
""" central hub for dealing with configuration/initialization data. """
Option = optparse.Option
def __init__(self):
self.option = CmdOptions()
self._parser = optparse.OptionParser(
usage="usage: %prog [options] [query] [filenames of tests]")
self.conftest = Conftest()
self._initialized = False
self._overwrite_dict = {}
def parse(self, args):
""" parse cmdline arguments into this config object.
Note that this can only be called once per testing process.
"""
assert not self._initialized, (
"can only parse cmdline args once per Config object")
self._initialized = True
self.conftest.setinitial(args)
self.conftest.rget('adddefaultoptions')()
args = [str(x) for x in args]
cmdlineoption, args = self._parser.parse_args(args)
self.option.__dict__.update(vars(cmdlineoption))
fixoptions(self.option) # XXX fixing should be moved to sessions
if not args:
args.append(py.std.os.getcwd())
self.topdir = gettopdir(args)
self.args = args
def initdirect(self, topdir, repr, coltrails=None):
assert not self._initialized
self._initialized = True
self.topdir = py.path.local(topdir)
self.merge_repr(repr)
self._coltrails = coltrails
def getcolitems(self):
""" return initial collectors. """
trails = getattr(self, '_coltrails', None)
return [self._getcollector(path) for path in (trails or self.args)]
def _getcollector(self, path):
if isinstance(path, tuple):
relpath, names = path
fspath = self.topdir.join(relpath)
col = self._getcollector(fspath)
else:
path = py.path.local(path)
assert path.check(), "%s: path does not exist" %(path,)
col = self._getrootcollector(path)
names = path.relto(col.fspath).split(path.sep)
return col.getitembynames(names)
def _getrootcollector(self, path):
pkgpath = path.pypkgpath()
if pkgpath is None:
pkgpath = path.check(file=1) and path.dirpath() or path
col = self.conftest.rget("Directory", pkgpath)(pkgpath)
col.config = self
return col
def addoptions(self, groupname, *specs):
""" add a named group of options to the current testing session.
This function gets invoked during testing session initialization.
"""
optgroup = optparse.OptionGroup(self._parser, groupname)
optgroup.add_options(specs)
self._parser.add_option_group(optgroup)
for opt in specs:
if hasattr(opt, 'default') and opt.dest:
setattr(self.option, opt.dest, opt.default)
return self.option
def getvalue(self, name, path=None):
""" return 'name' value looked up from the first conftest file
found up the path (including the path itself).
if path is None, lookup the value in the initial
conftest modules found during command line parsing.
"""
try:
return self._overwrite_dict[name]
except KeyError:
return self.conftest.rget(name, path)
def initsession(self):
""" return an initialized session object. """
cls = self._getsessionclass()
session = cls(self)
#session.fixoptions()
return session
def _getsessionclass(self):
""" return Session class determined from cmdline options
and looked up in initial config modules.
"""
if self.option.session is not None:
return self.conftest.rget(self.option.session)
else:
name = self._getsessionname()
importpath = globals()[name]
mod = __import__(importpath, None, None, '__doc__')
return getattr(mod, name)
def _getsessionname(self):
""" return default session name as determined from options. """
name = 'TerminalSession'
if self.option.dist:
name = 'RSession'
elif self.option.tkinter:
name = 'TkinterSession'
else:
optnames = 'startserver runbrowser apigen restreport boxing'.split()
for opt in optnames:
if getattr(self.option, opt, False):
name = 'LSession'
break
else:
if self.getvalue('dist_boxing'):
name = 'LSession'
if self.option.looponfailing:
name = 'RemoteTerminalSession'
elif self.option.executable:
name = 'RemoteTerminalSession'
return name
def is_boxed(self):
# XXX probably not a good idea to have this special function ...
return self.option.boxing or self.getvalue("dist_boxing")
def _reparse(self, args):
""" this is used from tests that want to re-invoke parse(). """
global config_per_process
oldconfig = py.test.config
try:
config_per_process = py.test.config = Config()
config_per_process.parse(args)
return config_per_process
finally:
config_per_process = py.test.config = oldconfig
def _overwrite(self, name, value):
""" this is used from tests to overwrite values irrespectives of conftests.
"""
self._overwrite_dict[name] = value
def make_repr(self, conftestnames, optnames=None):
""" return a marshallable representation
of conftest and cmdline options.
if optnames is None, all options
on self.option will be transferred.
"""
conftestdict = {}
for name in conftestnames:
value = self.getvalue(name)
checkmarshal(name, value)
conftestdict[name] = value
cmdlineopts = {}
if optnames is None:
optnames = dir(self.option)
for name in optnames:
if not name.startswith("_"):
value = getattr(self.option, name)
checkmarshal(name, value)
cmdlineopts[name] = value
l = []
for path in self.args:
path = py.path.local(path)
l.append(path.relto(self.topdir))
return l, conftestdict, cmdlineopts
def merge_repr(self, repr):
""" merge in the conftest and cmdline option values
found in the given representation (produced
by make_repr above).
The repr-contained conftest values are
stored on the default conftest module (last
priority) and the cmdline options on self.option.
"""
class override:
def __init__(self, d):
self.__dict__.update(d)
args, conftestdict, cmdlineopts = repr
self.args = [self.topdir.join(x) for x in args]
self.conftest.setinitial(self.args)
self.conftest._path2confmods[None].append(override(conftestdict))
for name, val in cmdlineopts.items():
setattr(self.option, name, val)
def get_collector_trail(self, collector):
""" provide a trail relative to the topdir,
which can be used to reconstruct the
collector (possibly on a different host
starting from a different topdir).
"""
chain = collector.listchain()
relpath = chain[0].fspath.relto(self.topdir)
if not relpath:
if chain[0].fspath == self.topdir:
relpath = "."
else:
raise ValueError("%r not relative to %s"
%(chain[0], self.topdir))
return relpath, tuple([x.name for x in chain[1:]])
# this is the one per-process instance of py.test configuration
config_per_process = Config()
# default import paths for sessions
TkinterSession = 'py.__.test.tkinter.reportsession'
TerminalSession = 'py.__.test.terminal.terminal'
TerminalSession = 'py.__.test.terminal.terminal'
RemoteTerminalSession = 'py.__.test.terminal.remote'
RSession = 'py.__.test.rsession.rsession'
LSession = 'py.__.test.rsession.rsession'
#
# helpers
#
def checkmarshal(name, value):
try:
py.std.marshal.dumps(value)
except ValueError:
raise ValueError("%s=%r is not marshallable" %(name, value))
def fixoptions(option):
""" sanity checks and making option values canonical. """
# implied options
if option.usepdb:
if not option.nocapture:
#print "--pdb implies --nocapture"
option.nocapture = True
if option.runbrowser and not option.startserver:
#print "--runbrowser implies --startserver"
option.startserver = True
# conflicting options
if option.looponfailing and option.usepdb:
raise ValueError, "--looponfailing together with --pdb not supported."
if option.looponfailing and option.dist:
raise ValueError, "--looponfailing together with --dist not supported."
if option.executable and option.usepdb:
raise ValueError, "--exec together with --pdb not supported."
def gettopdir(args):
""" return the top directory for the given paths.
if the common base dir resides in a python package
parent directory of the root package is returned.
"""
args = [py.path.local(arg) for arg in args]
p = reduce(py.path.local.common, args)
assert p, "cannot determine common basedir of %s" %(args,)
pkgdir = p.pypkgpath()
if pkgdir is None:
return p
else:
return pkgdir.dirpath()

View File

@ -51,9 +51,10 @@ class Conftest(object):
# affect us by always returning a copy of the actual list # affect us by always returning a copy of the actual list
return clist[:] return clist[:]
def lget(self, name, path=None): # XXX no real use case, may probably go
modules = self.getconftestmodules(path) #def lget(self, name, path=None):
return self._get(name, modules) # modules = self.getconftestmodules(path)
# return self._get(name, modules)
def rget(self, name, path=None): def rget(self, name, path=None):
modules = self.getconftestmodules(path) modules = self.getconftestmodules(path)

View File

@ -10,15 +10,25 @@ Instance = py.test.collect.Instance
additionalinfo = None additionalinfo = None
# ===================================================
# Distributed testing specific options
#dist_hosts: needs to be provided by user
#dist_rsync_roots: might be provided by user, if not present or None,
# whole pkgdir will be rsynced
dist_remotepython = "python"
dist_taskspernode = 15
dist_boxing = False
if hasattr(py.std.os, 'nice'):
dist_nicelevel = py.std.os.nice(0) # nice py.test works
else:
dist_nicelevel = 0
_dist_import_pypy = False # used for regenerating JS application
# ===================================================
Option = py.test.config.Option Option = py.test.config.Option
sessionimportpaths = {
'RSession': 'py.__.test.rsession.rsession',
'LSession': 'py.__.test.rsession.rsession',
'TerminalSession': 'py.__.test.terminal.terminal',
'TkinterSession': 'py.__.test.tkinter.reportsession',
}
def adddefaultoptions(): def adddefaultoptions():
py.test.config.addoptions('general options', py.test.config.addoptions('general options',
Option('-v', '--verbose', Option('-v', '--verbose',
@ -56,35 +66,41 @@ def adddefaultoptions():
Option('', '--traceconfig', Option('', '--traceconfig',
action="store_true", dest="traceconfig", default=False, action="store_true", dest="traceconfig", default=False,
help="trace considerations of conftest.py files."), help="trace considerations of conftest.py files."),
)
py.test.config.addoptions('EXPERIMENTAL options',
Option('-f', '--looponfailing',
action="store_true", dest="looponfailing", default=False,
help="loop on failing test set."),
Option('', '--exec',
action="store", dest="executable", default=None,
help="python executable to run the tests with."),
Option('-d', '--dist',
action="store_true", dest="dist", default=False,
help="ad-hoc distribute tests across machines (requires conftest settings)"),
Option('-w', '--startserver',
action="store_true", dest="startserver", default=False,
help="starts local web server for displaying test progress.",
),
Option('-r', '--runbrowser',
action="store_true", dest="runbrowser", default=False,
help="run browser (implies --startserver)."
),
Option('', '--tkinter',
action="store_true", dest="tkinter", default=False,
help="use tkinter test session frontend."),
Option('', '--box',
action="store_true", dest="boxing",
help="use boxing (running each test in external process)"),
Option('', '--rest',
action='store_true', dest="restreport", default=False,
help="restructured text output reporting."),
Option('', '--apigen', Option('', '--apigen',
action="store", dest="apigen", action="store", dest="apigen",
help="generate api documentation while testing (requires" help="generate api documentation while testing (requires"
"argument pointing to a script)."), "argument pointing to a script)."),
)
py.test.config.addoptions('test-session related options',
Option('', '--tkinter',
action="store_true", dest="tkinter", default=False,
help="use tkinter test session frontend."),
Option('', '--looponfailing',
action="store_true", dest="looponfailing", default=False,
help="loop on failing test set."),
Option('', '--session', Option('', '--session',
action="store", dest="session", default=None, action="store", dest="session", default=None,
help="use given sessionclass, default is terminal."), help="lookup given sessioname in conftest.py files and use it."),
Option('', '--exec',
action="store", dest="executable", default=None,
help="python executable to run the tests with."),
Option('-w', '--startserver',
action="store_true", dest="startserver", default=False,
help="start HTTP server listening on localhost:8000 for test."
),
Option('', '--runbrowser',
action="store_true", dest="runbrowser", default=False,
help="run browser to point to your freshly started web server."
),
Option('-r', '--rest',
action='store_true', dest="restreport", default=False,
help="restructured text output reporting."),
) )

View File

@ -31,7 +31,7 @@ class SetupState(object):
class Item(py.test.collect.Collector): class Item(py.test.collect.Collector):
def startcapture(self): def startcapture(self):
if not self.option.nocapture: if not self.config.option.nocapture:
# XXX refactor integrate capturing # XXX refactor integrate capturing
#self._capture = py.io.OutErrCapture() #self._capture = py.io.OutErrCapture()
from py.__.misc.simplecapture import SimpleOutErrCapture from py.__.misc.simplecapture import SimpleOutErrCapture

177
py/test/representation.py Normal file
View File

@ -0,0 +1,177 @@
""" This file intends to gather all methods of representing
failures/tracebacks etc. which should be used among
all terminal-based reporters. This methods should be general,
to allow further use outside the pylib
"""
import py
class Presenter(object):
""" Class used for presentation of various objects,
sharing common output style
"""
def __init__(self, out, config):
""" out is a file-like object (we can write to it)
"""
assert hasattr(out, 'write')
self.out = out
self.config = config
def repr_source(self, source, marker=">", marker_location=-1):
""" This one represents piece of source with possible
marker at requested position
"""
if isinstance(source, str):
# why the hell, string is iterable?
source = source.split("\n")
if marker_location < 0:
marker_location += len(source)
if marker_location < 0:
marker_location = 0
if marker_location >= len(source):
marker_location = len(source) - 1
for i in range(len(source)):
if i == marker_location:
prefix = marker + " "
else:
prefix = " "
self.out.line(prefix + source[i])
def repr_item_info(self, item):
""" This method represents py.test.Item info (path and module)
"""
root = item.fspath
modpath = item.getmodpath()
try:
fn, lineno = item.getpathlineno()
except TypeError:
assert isinstance(item.parent, py.test.collect.Generator)
# a generative test yielded a non-callable
fn, lineno = item.parent.getpathlineno()
if root == fn:
self.out.sep("_", "entrypoint: %s" %(modpath))
else:
self.out.sep("_", "entrypoint: %s %s" %(root.basename, modpath))
def repr_failure_explanation(self, excinfo, source):
try:
s = str(source.getstatement(len(source)-1))
except KeyboardInterrupt:
raise
except:
s = str(source[-1])
indent = " " * (4 + (len(s) - len(s.lstrip())))
# get the real exception information out
lines = excinfo.exconly(tryshort=True).split('\n')
self.out.line('>' + indent[:-1] + lines.pop(0))
for x in lines:
self.out.line(indent + x)
def getentrysource(self, entry):
try:
source = entry.getsource()
except py.error.ENOENT:
source = py.code.Source("[failure to get at sourcelines from %r]\n" % entry)
return source.deindent()
def repr_locals(self, f_locals):
if self.config.option.showlocals:
self.out.sep('- ', 'locals')
for name, value in f_locals.items():
if name == '__builtins__':
self.out.line("__builtins__ = <builtins>")
else:
# This formatting could all be handled by the _repr() function, which is
# only repr.Repr in disguise, so is very configurable.
str_repr = py.__.code.safe_repr._repr(value)
if len(str_repr) < 70 or not isinstance(value,
(list, tuple, dict)):
self.out.line("%-10s = %s" %(name, str_repr))
else:
self.out.line("%-10s =\\" % (name,))
py.std.pprint.pprint(value, stream=self.out)
def repr_failure_tblong(self, item, excinfo, traceback, out_err_reporter):
if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError):
recursionindex = traceback.recursionindex()
else:
recursionindex = None
last = traceback[-1]
first = traceback[0]
for index, entry in py.builtin.enumerate(traceback):
if entry == first:
if item:
self.repr_item_info(item)
self.out.line()
else:
self.out.line("")
source = self.getentrysource(entry)
firstsourceline = entry.getfirstlinesource()
marker_location = entry.lineno - firstsourceline
if entry == last:
self.repr_source(source, 'E', marker_location)
self.repr_failure_explanation(excinfo, source)
else:
self.repr_source(source, '>', marker_location)
self.out.line("")
self.out.line("[%s:%d]" %(entry.path, entry.lineno+1))
self.repr_locals(entry.locals)
# trailing info
if entry == last:
out_err_reporter()
self.out.sep("_")
else:
self.out.sep("_ ")
if index == recursionindex:
self.out.line("Recursion detected (same locals & position)")
self.out.sep("!")
break
def repr_failure_tbshort(self, item, excinfo, traceback, out_err_reporter):
# print a Python-style short traceback
if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError):
recursionindex = traceback.recursionindex()
else:
recursionindex = None
last = traceback[-1]
first = traceback[0]
self.out.line()
for index, entry in py.builtin.enumerate(traceback):
path = entry.path.basename
firstsourceline = entry.getfirstlinesource()
relline = entry.lineno - firstsourceline
self.out.line(' File "%s", line %d, in %s' % (
path, entry.lineno+1, entry.name))
try:
source = entry.getsource().lines
except py.error.ENOENT:
source = ["?"]
else:
try:
if len(source) > 1:
source = source[relline]
except IndexError:
source = []
if entry == last:
if source:
self.repr_source(source, 'E')
self.repr_failure_explanation(excinfo, source)
else:
if source:
self.repr_source(source, ' ')
self.repr_locals(entry.locals)
# trailing info
if entry == last:
out_err_reporter()
self.out.sep("_")
else:
if index == recursionindex:
self.out.line("Recursion detected (same locals & position)")
self.out.sep("!")
break
# the following is only used by the combination '--pdb --tb=no'
repr_failure_tbno = repr_failure_tbshort

View File

@ -18,12 +18,14 @@ from StringIO import StringIO
class FileBox(object): class FileBox(object):
count = 0 count = 0
def __init__(self, fun, args=None, kwargs=None): def __init__(self, fun, args=None, kwargs=None, config=None):
if args is None: if args is None:
args = [] args = []
if kwargs is None: if kwargs is None:
kwargs = {} kwargs = {}
self.fun = fun self.fun = fun
self.config = config
assert self.config
self.args = args self.args = args
self.kwargs = kwargs self.kwargs = kwargs
@ -35,8 +37,7 @@ class FileBox(object):
self.PYTESTSTDOUT = tempdir.join('stdout') self.PYTESTSTDOUT = tempdir.join('stdout')
self.PYTESTSTDERR = tempdir.join('stderr') self.PYTESTSTDERR = tempdir.join('stderr')
from py.__.test.rsession.rsession import remote_options nice_level = self.config.getvalue('dist_nicelevel')
nice_level = remote_options.nice_level
pid = os.fork() pid = os.fork()
if pid: if pid:
if not continuation: if not continuation:
@ -48,10 +49,12 @@ class FileBox(object):
outcome = self.children(nice_level) outcome = self.children(nice_level)
except: except:
excinfo = py.code.ExceptionInfo() excinfo = py.code.ExceptionInfo()
print "Internal box error" x = open("/tmp/traceback", "w")
print >>x, "Internal box error"
for i in excinfo.traceback: for i in excinfo.traceback:
print str(i)[2:-1] print >>x, str(i)[2:-1]
print excinfo print >>x, excinfo
x.close()
os._exit(1) os._exit(1)
os.close(1) os.close(1)
os.close(2) os.close(2)
@ -83,8 +86,8 @@ class FileBox(object):
retvalf.close() retvalf.close()
os._exit(0) os._exit(0)
def parent(self, pid): def parent(self, pid, waiter=os.waitpid):
pid, exitstat = os.waitpid(pid, 0) pid, exitstat = waiter(pid, 0)
self.signal = exitstat & 0x7f self.signal = exitstat & 0x7f
self.exitstat = exitstat & 0xff00 self.exitstat = exitstat & 0xff00

View File

@ -1,10 +0,0 @@
import py
Option = py.test.config.Option
option = py.test.config.addoptions("boxing test options",
Option('', '--skip-kill-test', action='store_true', default=False,
dest='skip_kill_test',
help='skip a certain test that checks for os.kill results, this '
'should be used when kill() is not allowed for the current '
'user'),
)

View File

@ -12,10 +12,12 @@ class RunExecutor(object):
""" """
wraps = False wraps = False
def __init__(self, item, usepdb=False, reporter=None): def __init__(self, item, usepdb=False, reporter=None, config=None):
self.item = item self.item = item
self.usepdb = usepdb self.usepdb = usepdb
self.reporter = reporter self.reporter = reporter
self.config = config
assert self.config
def execute(self): def execute(self):
try: try:
@ -36,7 +38,8 @@ class RunExecutor(object):
if self.usepdb: if self.usepdb:
if self.reporter is not None: if self.reporter is not None:
self.reporter(report.ImmediateFailure(self.item, self.reporter(report.ImmediateFailure(self.item,
ReprOutcome(outcome.make_repr()))) ReprOutcome(outcome.make_repr
(self.config.option.tbstyle))))
import pdb import pdb
pdb.post_mortem(excinfo._excinfo[2]) pdb.post_mortem(excinfo._excinfo[2])
# XXX hmm, we probably will not like to continue from that # XXX hmm, we probably will not like to continue from that
@ -54,8 +57,8 @@ class BoxExecutor(RunExecutor):
def execute(self): def execute(self):
def fun(): def fun():
outcome = RunExecutor.execute(self) outcome = RunExecutor.execute(self)
return outcome.make_repr() return outcome.make_repr(self.config.option.tbstyle)
b = Box(fun) b = Box(fun, config=self.config)
pid = b.run() pid = b.run()
assert pid assert pid
if b.retval is not None: if b.retval is not None:
@ -76,13 +79,13 @@ class AsyncExecutor(RunExecutor):
def execute(self): def execute(self):
def fun(): def fun():
outcome = RunExecutor.execute(self) outcome = RunExecutor.execute(self)
return outcome.make_repr() return outcome.make_repr(self.config.option.tbstyle)
b = Box(fun) b = Box(fun, config=self.config)
parent, pid = b.run(continuation=True) parent, pid = b.run(continuation=True)
def cont(): def cont(waiter=os.waitpid):
parent(pid) parent(pid, waiter=waiter)
if b.retval is not None: if b.retval is not None:
passed, setupfailure, excinfo, skipped,\ passed, setupfailure, excinfo, skipped,\
critical, _, _, _ = b.retval critical, _, _, _ = b.retval

View File

@ -5,7 +5,6 @@ import thread, threading
from py.__.test.rsession.master import \ from py.__.test.rsession.master import \
setup_slave, MasterNode setup_slave, MasterNode
from py.__.test.rsession import report from py.__.test.rsession import report
from py.__.test.rsession.rsync import RSync
class HostInfo(object): class HostInfo(object):
""" Class trying to store all necessary attributes """ Class trying to store all necessary attributes
@ -39,11 +38,11 @@ class HostInfo(object):
def __ne__(self, other): def __ne__(self, other):
return not self == other return not self == other
class HostRSync(RSync): class HostRSync(py.execnet.RSync):
""" An rsync wrapper which filters out *~, .svn/ and *.pyc """ An rsync wrapper which filters out *~, .svn/ and *.pyc
""" """
def __init__(self, rsync_roots): def __init__(self, rsync_roots):
RSync.__init__(self, delete=True) py.execnet.RSync.__init__(self, delete=True)
self.rsync_roots = rsync_roots self.rsync_roots = rsync_roots
def filter(self, path): def filter(self, path):
@ -60,104 +59,143 @@ class HostRSync(RSync):
class DummyGateway(object): class DummyGateway(object):
pass pass
def prepare_gateway(sshosts, optimise_localhost, class HostOptions(object):
remote_python, pkgdir, real_create=True): """ Dummy container for host options, not to keep that
hosts = [] as different function parameters, mostly related to
for host in sshosts: tests only
if host.hostname != 'localhost' or not optimise_localhost: """
if real_create: def __init__(self, rsync_roots=None, remote_python="python",
# for tests we want to use somtehing different optimise_localhost=True, do_sync=True,
if host.hostname == 'localhost' and optimise_localhost is False: create_gateways=True):
from py.__.execnet.register import PopenCmdGateway self.rsync_roots = rsync_roots
gw = PopenCmdGateway("cd ~; python -u -c 'exec input()'") self.remote_python = remote_python
if not host.relpath.startswith("/"): self.optimise_localhost = optimise_localhost
host.relpath = os.environ['HOME'] + '/' + host.relpath self.do_sync = do_sync
else: self.create_gateways = create_gateways
if remote_python is None:
gw = py.execnet.SshGateway(host.hostname)
else:
gw = py.execnet.SshGateway(host.hostname,
remotepython=remote_python)
else:
gw = DummyGateway()
else:
if remote_python is None:
gw = py.execnet.PopenGateway()
else:
gw = py.execnet.PopenGateway(remotepython=remote_python)
host.relpath = str(pkgdir.dirpath())
host.gw = gw
gw.host = host
return sshosts
def init_hosts(reporter, sshhosts, pkgdir, rsync_roots=None, class HostManager(object):
remote_python=None, \ def __init__(self, sshhosts, config, pkgdir, options=HostOptions()):
remote_options={}, optimise_localhost=True,\ self.sshhosts = sshhosts
do_sync=True, done_dict=None): self.pkgdir = pkgdir
if done_dict is None: self.config = config
done_dict = {} self.options = options
assert pkgdir.join("__init__.py").check(), ( if not options.create_gateways:
self.prepare_gateways = self.prepare_dummy_gateways
assert pkgdir.join("__init__.py").check(), (
"%s probably wrong" %(pkgdir,)) "%s probably wrong" %(pkgdir,))
exc_info = [None] def prepare_dummy_gateways(self):
hosts = prepare_gateway(sshhosts, optimise_localhost, for host in self.sshhosts:
remote_python, pkgdir, real_create=do_sync) gw = DummyGateway()
host.gw = gw
# rsyncing gw.host = host
rsynced = {} return self.sshhosts
if do_sync: def prepare_ssh_gateway(self, host):
rsync = HostRSync(rsync_roots) if self.options.remote_python is None:
for host in hosts: gw = py.execnet.SshGateway(host.hostname)
#for num, host, gw, remoterootpath in hosts: else:
remoterootpath = host.relpath gw = py.execnet.SshGateway(host.hostname,
if (host.hostname, remoterootpath) in rsynced or\ remotepython=self.options.remote_python)
(host.hostname == 'localhost' and optimise_localhost): return gw
reporter(report.HostReady(host))
continue
rsynced[(host.hostname, host.relpath)] = True
def done(host=host):
reporter(report.HostReady(host))
reporter(report.HostRSyncing(host))
if do_sync:
rsync.add_target(host.gw, remoterootpath, done)
if not do_sync:
return # for testing only
rsync.send(pkgdir.dirpath())
# hosts ready def prepare_popen_rsync_gateway(self, host):
return setup_nodes(hosts, pkgdir, remote_options, reporter, done_dict) """ Popen gateway, but with forced rsync
"""
from py.__.execnet.register import PopenCmdGateway
gw = PopenCmdGateway("cd ~; python -u -c 'exec input()'")
if not host.relpath.startswith("/"):
host.relpath = os.environ['HOME'] + '/' + host.relpath
return gw
def setup_nodes(hosts, pkgdir, remote_options, reporter, done_dict): def prepare_popen_gateway(self, host):
nodes = [] if self.options.remote_python is None:
for host in hosts: gw = py.execnet.PopenGateway()
ch = setup_slave(host.gw, os.path.join(host.relpath,\ else:
pkgdir.basename), remote_options) gw = py.execnet.PopenGateway(python=self.options.remote_python)
nodes.append(MasterNode(ch, reporter, done_dict)) host.relpath = str(self.pkgdir.dirpath())
return gw
return nodes
def teardown_hosts(reporter, channels, nodes, waiter=lambda : time.sleep(.1), def prepare_gateways(self):
exitfirst=False): for host in self.sshhosts:
for channel in channels: if host.hostname == 'localhost':
channel.send(None) if not self.options.optimise_localhost:
gw = self.prepare_popen_rsync_gateway(host)
from py.__.test.rsession.rsession import session_options else:
gw = self.prepare_popen_gateway(host)
else:
gw = self.prepare_ssh_gateway(host)
host.gw = gw
gw.host = host
return self.sshhosts
clean = exitfirst def need_rsync(self, rsynced, hostname, remoterootpath):
while not clean: if (hostname, remoterootpath) in rsynced:
clean = True return False
for node in nodes: if hostname == 'localhost' and self.options.optimise_localhost:
if node.pending: return False
clean = False return True
waiter()
def init_hosts(self, reporter, done_dict={}):
if done_dict is None:
done_dict = {}
hosts = self.prepare_gateways()
# rsyncing
rsynced = {}
for channel in channels: if self.options.do_sync:
try: rsync = HostRSync(self.options.rsync_roots)
report.wrapcall(reporter, channel.waitclose, int(session_options.waittime)) for host in hosts:
except KeyboardInterrupt, SystemExit: if not self.need_rsync(rsynced, host.hostname, host.relpath):
raise reporter(report.HostReady(host))
except: continue
pass rsynced[(host.hostname, host.relpath)] = True
channel.gateway.exit() def done(host=host):
reporter(report.HostReady(host))
reporter(report.HostRSyncing(host))
if self.options.do_sync:
rsync.add_target(host.gw, host.relpath, done)
if not self.options.do_sync:
return # for testing only
rsync.send(self.pkgdir.dirpath())
# hosts ready
return self.setup_nodes(reporter, done_dict)
def setup_nodes(self, reporter, done_dict):
nodes = []
for host in self.sshhosts:
ch = setup_slave(host.gw, os.path.join(host.relpath,\
self.pkgdir.basename), self.config)
nodes.append(MasterNode(ch, reporter, done_dict))
return nodes
def teardown_hosts(self, reporter, channels, nodes,
waiter=lambda : time.sleep(.1), exitfirst=False):
for channel in channels:
channel.send(None)
clean = exitfirst
while not clean:
clean = True
for node in nodes:
if node.pending:
clean = False
waiter()
self.teardown_gateways(reporter, channels)
def kill_channels(self, channels):
for channel in channels:
channel.send(42)
def teardown_gateways(self, reporter, channels):
for channel in channels:
try:
report.wrapcall(reporter, channel.waitclose)
except KeyboardInterrupt, SystemExit:
raise
except:
pass
channel.gateway.exit()

View File

@ -21,23 +21,18 @@ def finishcapture(session):
return "", "" return "", ""
def box_runner(item, session, reporter): def box_runner(item, session, reporter):
r = BoxExecutor(item) r = BoxExecutor(item, config=session.config)
return ReprOutcome(r.execute()) return ReprOutcome(r.execute())
def plain_runner(item, session, reporter): def plain_runner(item, session, reporter):
# box executor is doing stdout/err catching for us, let's do it here # box executor is doing stdout/err catching for us, let's do it here
startcapture(session) startcapture(session)
r = RunExecutor(item, usepdb=session.config.option.usepdb, reporter=reporter) r = RunExecutor(item, usepdb=session.config.option.usepdb, reporter=reporter, config=session.config)
outcome = r.execute() outcome = r.execute()
outcome = ReprOutcome(outcome.make_repr()) outcome = ReprOutcome(outcome.make_repr(session.config.option.tbstyle))
outcome.stdout, outcome.stderr = finishcapture(session) outcome.stdout, outcome.stderr = finishcapture(session)
return outcome return outcome
RunnerPolicy = {
'plain_runner':plain_runner,
'box_runner':box_runner
}
def benchmark_runner(item, session, reporter): def benchmark_runner(item, session, reporter):
raise NotImplementedError() raise NotImplementedError()

View File

@ -35,7 +35,7 @@ class MasterNode(object):
self.reporter(report.SendItem(self.channel, item)) self.reporter(report.SendItem(self.channel, item))
def itemgen(colitems, reporter, keyword, reporterror): def itemgen(colitems, reporter, keyword, reporterror):
for x in colitems: for x in colitems:
for y in x.tryiter(reporterror = lambda x: reporterror(reporter, x), keyword = keyword): for y in x.tryiter(reporterror = lambda x: reporterror(reporter, x), keyword = keyword):
yield y yield y
@ -56,9 +56,7 @@ def dispatch_loop(masternodes, itemgenerator, shouldstop,
waiter = lambda: py.std.time.sleep(0.1), waiter = lambda: py.std.time.sleep(0.1),
max_tasks_per_node=None): max_tasks_per_node=None):
if not max_tasks_per_node: if not max_tasks_per_node:
from py.__.test.rsession.rsession import session_options max_tasks_per_node = py.test.config.getvalue("dist_taskspernode")
max_tasks_per_node = session_options.max_tasks_per_node
all_tests = {} all_tests = {}
while 1: while 1:
try: try:
@ -76,12 +74,14 @@ def dispatch_loop(masternodes, itemgenerator, shouldstop,
waiter() waiter()
return all_tests return all_tests
def setup_slave(gateway, pkgpath, options): def setup_slave(gateway, pkgpath, config):
from py.__.test.rsession import slave from py.__.test.rsession import slave
import os import os
ch = gateway.remote_exec(str(py.code.Source(slave.setup, "setup()"))) ch = gateway.remote_exec(str(py.code.Source(slave.setup, "setup()")))
#if hasattr(gateway, 'sshaddress'): #if hasattr(gateway, 'sshaddress'):
# assert not os.path.isabs(pkgpath) # assert not os.path.isabs(pkgpath)
ch.send(str(pkgpath)) ch.send(str(pkgpath))
ch.send(options) ch.send(config.make_repr(defaultconftestnames))
return ch return ch
defaultconftestnames = ['dist_nicelevel']

View File

@ -20,51 +20,82 @@ class Outcome(object):
self.signal = 0 self.signal = 0
assert bool(self.passed) + bool(excinfo) + bool(skipped) == 1 assert bool(self.passed) + bool(excinfo) + bool(skipped) == 1
def make_excinfo_repr(self): def make_excinfo_repr(self, tbstyle):
if self.excinfo is None: if self.excinfo is None:
return None return None
excinfo = self.excinfo excinfo = self.excinfo
tb_info = [self.traceback_entry_repr(x) for x in excinfo.traceback] tb_info = [self.traceback_entry_repr(x, tbstyle)
return (excinfo.type.__name__, str(excinfo.value), tb_info) for x in excinfo.traceback]
rec_index = excinfo.traceback.recursionindex()
return (excinfo.type.__name__, str(excinfo.value), (tb_info, rec_index))
def traceback_entry_repr(self, tb_entry): def traceback_entry_repr(self, tb_entry, tb_style):
lineno = tb_entry.lineno lineno = tb_entry.lineno
relline = lineno - tb_entry.frame.code.firstlineno relline = lineno - tb_entry.frame.code.firstlineno
path = str(tb_entry.path) path = str(tb_entry.path)
try: #try:
from py.__.test.rsession.rsession import remote_options if tb_style == 'long':
if remote_options.tbstyle == 'long': source = str(tb_entry.getsource())
source = str(tb_entry.getsource()) else:
else: source = str(tb_entry.getsource()).split("\n")[relline]
source = str(tb_entry.getsource()).split("\n")[relline] name = tb_entry.frame.code.raw.co_name
except: # XXX: Bare except. What can getsource() raise anyway?
source = "<could not get source>" # SyntaxError, AttributeError, IndentationError for sure, check it
return (relline, lineno, source, path) #except:
# source = "<could not get source>"
return (relline, lineno, source, path, name)
def make_repr(self): def make_repr(self, tbstyle="long"):
return (self.passed, self.setupfailure, return (self.passed, self.setupfailure,
self.make_excinfo_repr(), self.make_excinfo_repr(tbstyle),
self.skipped, self.is_critical, 0, "", "") self.skipped, self.is_critical, 0, "", "")
class TracebackEntryRepr(object): class TracebackEntryRepr(object):
def __init__(self, tbentry): def __init__(self, tbentry):
relline, lineno, self.source, self.path = tbentry relline, lineno, self.source, self.path, self.name = tbentry
self.relline = int(relline) self.relline = int(relline)
self.path = py.path.local(self.path)
self.lineno = int(lineno) self.lineno = int(lineno)
self.locals = {}
def __repr__(self): def __repr__(self):
return "line %s in %s\n %s" %(self.lineno, self.path, self.source[100:]) return "line %s in %s\n %s" %(self.lineno, self.path, self.source[100:])
def getsource(self):
return py.code.Source(self.source).strip()
def getfirstlinesource(self):
return self.lineno - self.relline
class TracebackRepr(list):
def recursionindex(self):
return self.recursion_index
class ExcInfoRepr(object): class ExcInfoRepr(object):
def __init__(self, excinfo): def __init__(self, excinfo):
self.typename, self.value, tb = excinfo self.typename, self.value, tb_i = excinfo
self.traceback = [TracebackEntryRepr(x) for x in tb] tb, rec_index = tb_i
self.traceback = TracebackRepr([TracebackEntryRepr(x) for x in tb])
self.traceback.recursion_index = rec_index
def __repr__(self): def __repr__(self):
l = ["%s=%s" %(x, getattr(self, x)) l = ["%s=%s" %(x, getattr(self, x))
for x in "typename value traceback".split()] for x in "typename value traceback".split()]
return "<ExcInfoRepr %s>" %(" ".join(l),) return "<ExcInfoRepr %s>" %(" ".join(l),)
def exconly(self, tryshort=False):
""" Somehow crippled version of original one
"""
return "%s: %s" % (self.typename, self.value)
def errisinstance(self, exc_t):
if not isinstance(exc_t, tuple):
exc_t = (exc_t,)
for exc in exc_t:
if self.typename == str(exc).split('.')[-1]:
return True
return False
class ReprOutcome(object): class ReprOutcome(object):
def __init__(self, repr_tuple): def __init__(self, repr_tuple):
(self.passed, self.setupfailure, excinfo, self.skipped, (self.passed, self.setupfailure, excinfo, self.skipped,

View File

@ -124,3 +124,9 @@ class PongReceived(ReportEvent):
def __init__(self, hostid, result): def __init__(self, hostid, result):
self.hostid = hostid self.hostid = hostid
self.result = result self.result = result
class InterruptedExecution(ReportEvent):
pass
class CrashedExecution(ReportEvent):
pass

View File

@ -2,7 +2,7 @@
""" reporter - different reporter for different purposes ;-) """ reporter - different reporter for different purposes ;-)
Still lacks: Still lacks:
1. Hanging nodes are not good done 1. Hanging nodes are not done well
""" """
import py import py
@ -10,6 +10,10 @@ import py
from py.__.test.terminal.out import getout from py.__.test.terminal.out import getout
from py.__.test.rsession import report from py.__.test.rsession import report
from py.__.test.rsession import outcome from py.__.test.rsession import outcome
from py.__.misc.terminal_helper import ansi_print, get_terminal_width
from py.__.test.representation import Presenter
import sys
class AbstractReporter(object): class AbstractReporter(object):
def __init__(self, config, hosts, pkgdir=py.path.local(py.__file__)): def __init__(self, config, hosts, pkgdir=py.path.local(py.__file__)):
@ -19,6 +23,7 @@ class AbstractReporter(object):
self.failed_tests_outcome = [] self.failed_tests_outcome = []
self.skipped_tests_outcome = [] self.skipped_tests_outcome = []
self.out = getout(py.std.sys.stdout) self.out = getout(py.std.sys.stdout)
self.presenter = Presenter(self.out, config)
self.failed = dict([(host, 0) for host in hosts]) self.failed = dict([(host, 0) for host in hosts])
self.skipped = dict([(host, 0) for host in hosts]) self.skipped = dict([(host, 0) for host in hosts])
self.passed = dict([(host, 0) for host in hosts]) self.passed = dict([(host, 0) for host in hosts])
@ -46,6 +51,7 @@ class AbstractReporter(object):
def report_SendItem(self, item): def report_SendItem(self, item):
address = item.host.hostname address = item.host.hostname
assert isinstance(item.host.hostname, str)
if self.config.option.verbose: if self.config.option.verbose:
print "Sending %s to %s" % (item.item, print "Sending %s to %s" % (item.item,
address) address)
@ -108,6 +114,7 @@ class AbstractReporter(object):
self.out.sep('_', "%s on %s" % self.out.sep('_', "%s on %s" %
(" ".join(event.item.listnames()), host)) (" ".join(event.item.listnames()), host))
if event.outcome.signal: if event.outcome.signal:
self.presenter.repr_item_info(event.item)
self.repr_signal(event.item, event.outcome) self.repr_signal(event.item, event.outcome)
else: else:
self.repr_failure(event.item, event.outcome) self.repr_failure(event.item, event.outcome)
@ -119,7 +126,7 @@ class AbstractReporter(object):
def gethost(self, event): def gethost(self, event):
return event.host.hostname return event.host.hostname
def repr_failure(self, item, outcome): def repr_failure(self, item, outcome):
excinfo = outcome.excinfo excinfo = outcome.excinfo
traceback = excinfo.traceback traceback = excinfo.traceback
#if item and not self.config.option.fulltrace: #if item and not self.config.option.fulltrace:
@ -127,18 +134,11 @@ class AbstractReporter(object):
if not traceback: if not traceback:
self.out.line("empty traceback from item %r" % (item,)) self.out.line("empty traceback from item %r" % (item,))
return return
#handler = getattr(self, 'repr_failure_tb%s' % self.config.option.tbstyle)
self.repr_traceback(item, excinfo, traceback) handler = getattr(self.presenter, 'repr_failure_tb%s' % self.config.option.tbstyle)
if outcome.stdout: handler(item, excinfo, traceback, lambda: self.repr_out_err(outcome))
self.out.sep('-', " Captured process stdout: ")
self.out.write(outcome.stdout) def repr_out_err(self, outcome):
if outcome.stderr:
self.out.sep('-', " Captured process stderr: ")
self.out.write(outcome.stderr)
def repr_signal(self, item, outcome):
signal = outcome.signal
self.out.line("Received signal: %d" % outcome.signal)
if outcome.stdout: if outcome.stdout:
self.out.sep('-', " Captured process stdout: ") self.out.sep('-', " Captured process stdout: ")
self.out.write(outcome.stdout) self.out.write(outcome.stdout)
@ -146,25 +146,11 @@ class AbstractReporter(object):
self.out.sep('-', " Captured process stderr: ") self.out.sep('-', " Captured process stderr: ")
self.out.write(outcome.stderr) self.out.write(outcome.stderr)
def repr_traceback(self, item, excinfo, traceback): def repr_signal(self, item, outcome):
if self.config.option.tbstyle == 'long': signal = outcome.signal
for index, entry in py.builtin.enumerate(traceback): self.out.line("Received signal: %d" % outcome.signal)
self.out.sep('-') self.repr_out_err(outcome)
self.out.line("%s: %s" % (entry.path, entry.lineno))
self.repr_source(entry.relline, str(entry.source))
elif self.config.option.tbstyle == 'short':
for index, entry in py.builtin.enumerate(traceback):
self.out.line("%s: %s" % (entry.path, entry.lineno))
self.out.line(entry.source)
self.out.line("%s: %s" % (excinfo.typename, excinfo.value))
def repr_source(self, relline, source):
for num, line in enumerate(source.split("\n")):
if num == relline:
self.out.line(">>>>" + line)
else:
self.out.line(" " + line)
def skips(self): def skips(self):
texts = {} texts = {}
for event in self.skipped_tests_outcome: for event in self.skipped_tests_outcome:
@ -225,19 +211,30 @@ class AbstractReporter(object):
def report_ReceivedItemOutcome(self, event): def report_ReceivedItemOutcome(self, event):
host = event.host host = event.host
if event.outcome.passed: if event.outcome.passed:
status = "PASSED "
self.passed[host] += 1 self.passed[host] += 1
sys.stdout.write("%10s: PASSED " % host.hostname[:10])
elif event.outcome.skipped: elif event.outcome.skipped:
status = "SKIPPED"
self.skipped_tests_outcome.append(event) self.skipped_tests_outcome.append(event)
self.skipped[host] += 1 self.skipped[host] += 1
sys.stdout.write("%10s: SKIPPED " % host.hostname[:10])
else: else:
status = "FAILED "
self.failed[host] += 1 self.failed[host] += 1
self.failed_tests_outcome.append(event) self.failed_tests_outcome.append(event)
# we'll take care of them later sys.stdout.write("%10s: " % host.hostname[:10])
itempath = " ".join(event.item.listnames()[1:]) ansi_print("FAILED", esc=(31,1), newline=False, file=sys.stdout)
print "%10s: %s %s" %(host.hostname[:10], status, itempath) sys.stdout.write(" ")
# we should have printed 20 characters to this point
itempath = ".".join(event.item.listnames()[1:-1])
funname = event.item.listnames()[-1]
lgt = get_terminal_width() - 20
# mark the function name, to be sure
to_display = len(itempath) + len(funname) + 1
if to_display > lgt:
sys.stdout.write("..." + itempath[to_display-lgt+4:])
else:
sys.stdout.write(itempath)
sys.stdout.write(" ")
ansi_print(funname, esc=32, file=sys.stdout)
def report_Nodes(self, event): def report_Nodes(self, event):
self.nodes = event.nodes self.nodes = event.nodes

View File

@ -31,18 +31,23 @@ class RestReporter(AbstractReporter):
if self.config.option.verbose: if self.config.option.verbose:
self.add_rest(Paragraph("Unknown report: %s" % what)) self.add_rest(Paragraph("Unknown report: %s" % what))
def gethost(self, item):
if item.channel:
return item.channel.gateway.host
return self.hosts[0]
def report_SendItem(self, item): def report_SendItem(self, item):
address = item.channel.gateway.host.hostname address = self.gethost(item)
if self.config.option.verbose: if self.config.option.verbose:
self.add_rest(Paragraph('sending item %s to %s' % (item.item, self.add_rest(Paragraph('sending item %s to %s' % (item.item,
address))) address)))
def report_HostRSyncing(self, item): def report_HostRSyncing(self, item):
self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.hostname[:10], self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.host.hostname[:10],
item.remoterootpath))) item.remoterootpath)))
def report_HostReady(self, item): def report_HostReady(self, item):
self.add_rest(LiteralBlock('%10s: READY' % (item.hostname[:10],))) self.add_rest(LiteralBlock('%10s: READY' % (item.host.hostname[:10],)))
def report_TestStarted(self, event): def report_TestStarted(self, event):
txt = "Running tests on hosts: %s" % ", ".join(event.hosts) txt = "Running tests on hosts: %s" % ", ".join(event.hosts)
@ -91,7 +96,7 @@ class RestReporter(AbstractReporter):
self.out.write(self.rest.render_links()) self.out.write(self.rest.render_links())
def report_ReceivedItemOutcome(self, event): def report_ReceivedItemOutcome(self, event):
host = event.channel.gateway.host host = self.gethost(event)
if event.outcome.passed: if event.outcome.passed:
status = [Strong("PASSED")] status = [Strong("PASSED")]
self.passed[host] += 1 self.passed[host] += 1

View File

@ -1,276 +0,0 @@
""" Rest reporting stuff
"""
import py
import sys
from StringIO import StringIO
from py.__.test.rsession.reporter import AbstractReporter
from py.__.test.rsession import report
from py.__.rest.rst import *
class RestReporter(AbstractReporter):
linkwriter = None
def __init__(self, *args, **kwargs):
super(RestReporter, self).__init__(*args, **kwargs)
self.rest = Rest()
self.traceback_num = 0
def get_linkwriter(self):
if self.linkwriter is None:
try:
self.linkwriter = self.config.getvalue('linkwriter')
except KeyError:
print >>sys.stderr, ('no linkwriter configured, using default '
'one')
self.linkwriter = RelLinkWriter()
return self.linkwriter
def report_unknown(self, what):
if self.config.option.verbose:
self.add_rest(Paragraph("Unknown report: %s" % what))
def gethost(self, item):
if item.channel:
return item.channel.gateway.host
return self.hosts[0]
def report_SendItem(self, item):
address = self.gethost(item)
if self.config.option.verbose:
self.add_rest(Paragraph('sending item %s to %s' % (item.item,
address)))
def report_HostRSyncing(self, item):
self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.host.hostname[:10],
item.remoterootpath)))
def report_HostReady(self, item):
self.add_rest(LiteralBlock('%10s: READY' % (item.host.hostname[:10],)))
def report_TestStarted(self, event):
txt = "Running tests on hosts: %s" % ", ".join(event.hosts)
self.add_rest(Title(txt, abovechar='=', belowchar='='))
self.timestart = event.timestart
def report_TestFinished(self, item):
self.timeend = item.timeend
self.summary()
return len(self.failed_tests_outcome) > 0
def report_ImmediateFailure(self, item):
pass
def report_ItemStart(self, event):
item = event.item
if isinstance(item, py.test.collect.Module):
lgt = len(list(item.tryiter()))
lns = item.listnames()[1:]
name = "/".join(lns)
link = self.get_linkwriter().get_link(self.get_rootpath(item),
item.fspath)
if link:
name = Link(name, link)
txt = 'Testing module %s (%d items)' % (name, lgt)
self.add_rest(Title('Testing module', name, '(%d items)' % (lgt,),
belowchar='-'))
def get_rootpath(self, item):
root = item.parent
while root.parent is not None:
root = root.parent
return root.fspath
def print_summary(self, total, skipped_str, failed_str):
self.skips()
self.failures()
txt = "%d tests run%s%s in %.2fs (rsync: %.2f)" % \
(total, skipped_str, failed_str, self.timeend - self.timestart,
self.timersync - self.timestart)
self.add_rest(Title(txt, belowchar='-'))
# since we're rendering each item, the links haven't been rendered
# yet
self.out.write(self.rest.render_links())
def report_ReceivedItemOutcome(self, event):
host = self.gethost(event)
if event.outcome.passed:
status = [Strong("PASSED")]
self.passed[host] += 1
elif event.outcome.skipped:
status = [Strong("SKIPPED")]
self.skipped_tests_outcome.append(event)
self.skipped[host] += 1
else:
status = [Strong("FAILED"),
InternalLink("traceback%d" % self.traceback_num)]
self.traceback_num += 1
self.failed[host] += 1
self.failed_tests_outcome.append(event)
# we'll take care of them later
itempath = self.get_path_from_item(event.item)
status.append(Text(itempath))
hostname = host.hostname
self.add_rest(ListItem(Text("%10s:" % (hostname[:10],)), *status))
def skips(self):
# XXX hrmph, copied code
texts = {}
for event in self.skipped_tests_outcome:
colitem = event.item
if isinstance(event, report.ReceivedItemOutcome):
outcome = event.outcome
text = outcome.skipped
itemname = self.get_item_name(event, colitem)
elif isinstance(event, report.SkippedTryiter):
text = str(event.excinfo.value)
itemname = "/".join(colitem.listnames())
if text not in texts:
texts[text] = [itemname]
else:
texts[text].append(itemname)
if texts:
self.add_rest(Title('Reasons for skipped tests:', belowchar='+'))
for text, items in texts.items():
for item in items:
self.add_rest(ListItem('%s: %s' % (item, text)))
def get_host(self, event):
return event.channel.gateway.host
def failures(self):
self.traceback_num = 0
tbstyle = self.config.option.tbstyle
if self.failed_tests_outcome:
self.add_rest(Title('Exceptions:', belowchar='+'))
for i, event in enumerate(self.failed_tests_outcome):
if i > 0:
self.add_rest(Transition())
if isinstance(event, report.ReceivedItemOutcome):
host = self.get_host(event)
itempath = self.get_path_from_item(event.item)
root = self.get_rootpath(event.item)
link = self.get_linkwriter().get_link(root, event.item.fspath)
t = Title(belowchar='+')
if link:
t.add(Link(itempath, link))
else:
t.add(Text(itempath))
t.add(Text('on %s' % (host.hostname,)))
self.add_rest(t)
if event.outcome.signal:
self.repr_signal(event.item, event.outcome)
else:
self.repr_failure(event.item, event.outcome, tbstyle)
else:
itempath = self.get_path_from_item(event.item)
root = self.get_rootpath(event.item)
link = self.get_linkwriter().get_link(root, event.item.fspath)
t = Title(abovechar='+', belowchar='+')
if link:
t.add(Link(itempath, link))
else:
t.add(Text(itempath))
out = outcome.Outcome(excinfo=event.excinfo)
self.repr_failure(event.item,
outcome.ReprOutcome(out.make_repr()),
tbstyle)
def repr_signal(self, item, outcome):
signal = outcome.signal
self.add_rest(Title('Received signal: %d' % (outcome.signal,),
abovechar='+', belowchar='+'))
if outcome.stdout.strip():
self.add_rest(Paragraph('Captured process stdout:'))
self.add_rest(LiteralBlock(outcome.stdout))
if outcome.stderr.strip():
self.add_rest(Paragraph('Captured process stderr:'))
self.add_rest(LiteralBlock(outcome.stderr))
def repr_failure(self, item, outcome, style):
excinfo = outcome.excinfo
traceback = excinfo.traceback
if not traceback:
self.add_rest(Paragraph('empty traceback from item %r' % (item,)))
return
self.repr_traceback(item, excinfo, traceback, style)
if outcome.stdout:
self.add_rest(Title('Captured process stdout:', abovechar='+',
belowchar='+'))
self.add_rest(LiteralBlock(outcome.stdout))
if outcome.stderr:
self.add_rest(Title('Captured process stderr:', abovechar='+',
belowchar='+'))
self.add_rest(LiteralBlock(outcome.stderr))
def repr_traceback(self, item, excinfo, traceback, style):
root = self.get_rootpath(item)
self.add_rest(LinkTarget('traceback%d' % self.traceback_num, ""))
self.traceback_num += 1
if style == 'long':
for entry in traceback:
link = self.get_linkwriter().get_link(root,
py.path.local(entry.path))
if link:
self.add_rest(Title(Link(entry.path, link),
'line %d' % (entry.lineno,),
belowchar='+', abovechar='+'))
else:
self.add_rest(Title('%s line %d' % (entry.path,
entry.lineno,),
belowchar='+', abovechar='+'))
self.add_rest(LiteralBlock(self.prepare_source(entry.relline,
entry.source)))
elif style == 'short':
text = []
for entry in traceback:
text.append('%s line %d' % (entry.path, entry.lineno))
text.append(' %s' % (entry.source.strip(),))
self.add_rest(LiteralBlock('\n'.join(text)))
self.add_rest(Title(excinfo.typename, belowchar='+'))
self.add_rest(LiteralBlock(excinfo.value))
def prepare_source(self, relline, source):
text = []
for num, line in enumerate(source.split('\n')):
if num == relline:
text.append('>>> %s' % (line,))
else:
text.append(' %s' % (line,))
return '\n'.join(text)
def add_rest(self, item):
self.rest.add(item)
self.out.write('%s\n\n' % (item.text(),))
def get_path_from_item(self, item):
lns = item.listnames()[1:]
for i, ln in enumerate(lns):
if i > 0 and ln != '()':
lns[i] = '/%s' % (ln,)
itempath = ''.join(lns)
return itempath
class AbstractLinkWriter(object):
def get_link(self, base, path):
pass
class NoLinkWriter(AbstractLinkWriter):
def get_link(self, base, path):
return ''
class LinkWriter(AbstractLinkWriter):
def __init__(self, baseurl):
self.baseurl = baseurl
def get_link(self, base, path):
relpath = path.relto(base)
return self.baseurl + relpath
class RelLinkWriter(AbstractLinkWriter):
def get_link(self, base, path):
return path.relto(base)

View File

@ -11,74 +11,12 @@ import time
from py.__.test.rsession import report from py.__.test.rsession import report
from py.__.test.rsession.master import \ from py.__.test.rsession.master import \
setup_slave, MasterNode, dispatch_loop, itemgen, randomgen setup_slave, MasterNode, dispatch_loop, itemgen, randomgen
from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts, HostInfo from py.__.test.rsession.hostmanage import HostInfo, HostOptions, HostManager
from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\
box_runner, RunnerPolicy box_runner
from py.__.test.rsession.reporter import LocalReporter, RemoteReporter from py.__.test.rsession.reporter import LocalReporter, RemoteReporter
class RemoteOptions(object):
def __init__(self, d):
self.d = d
def __getattr__(self, attr):
if attr == 'd':
return self.__dict__['d']
return self.d[attr]
def __setitem__(self, item, val):
self.d[item] = val
# XXX: Must be initialised somehow
remote_options = RemoteOptions({'we_are_remote':False})
class SessionOptions:
defaults = {
'max_tasks_per_node' : 15,
'runner_policy' : 'plain_runner',
'nice_level' : 0,
'waittime' : 100.0,
'import_pypy' : False,
}
config = None
def getvalue(self, opt):
try:
return getattr(self.config.getvalue('SessionOptions'), opt)
except (KeyError, AttributeError):
try:
return self.defaults[opt]
except KeyError:
raise AttributeError("Option %s undeclared" % opt)
def bind_config(self, config):
self.config = config
# copy to remote session options
try:
ses_opt = self.config.getvalue('SessionOptions').__dict__
except KeyError:
ses_opt = self.defaults
for key in self.defaults:
try:
val = ses_opt[key]
except KeyError:
val = self.defaults[key]
remote_options[key] = val
# copy to remote all options
for item, val in config.option.__dict__.items():
remote_options[item] = val
def __repr__(self):
return "<SessionOptions %s>" % self.config
def __getattr__(self, attr):
if self.config is None:
raise AttributeError("Need to set up config first")
return self.getvalue(attr)
session_options = SessionOptions()
class AbstractSession(object): class AbstractSession(object):
""" """
An abstract session executes collectors/items through a runner. An abstract session executes collectors/items through a runner.
@ -114,17 +52,29 @@ class AbstractSession(object):
getpkgdir = staticmethod(getpkgdir) getpkgdir = staticmethod(getpkgdir)
def init_reporter(self, reporter, sshhosts, reporter_class, arg=""): def init_reporter(self, reporter, sshhosts, reporter_class, arg=""):
""" This initialises so called `reporter` class, which will
handle all event presenting to user. Does not get called
if main received custom reporter
"""
startserverflag = self.config.option.startserver startserverflag = self.config.option.startserver
restflag = self.config.option.restreport restflag = self.config.option.restreport
if startserverflag and reporter is None: if startserverflag and reporter is None:
from py.__.test.rsession.web import start_server, exported_methods from py.__.test.rsession.web import start_server, exported_methods
if self.config.option.runbrowser:
from socket import INADDR_ANY
port = INADDR_ANY # pick a random port when starting browser
else:
port = 8000 # stick to a fixed port otherwise
reporter = exported_methods.report reporter = exported_methods.report
start_server() httpd = start_server(server_address = ('', port))
port = httpd.server_port
if self.config.option.runbrowser: if self.config.option.runbrowser:
import webbrowser import webbrowser, thread
webbrowser.open("http://localhost:8000") # webbrowser.open() may block until the browser finishes or not
url = "http://localhost:%d" % (port,)
thread.start_new_thread(webbrowser.open, (url,))
elif reporter is None: elif reporter is None:
if restflag: if restflag:
from py.__.test.rsession.rest import RestReporter from py.__.test.rsession.rest import RestReporter
@ -150,6 +100,8 @@ class AbstractSession(object):
reporterror = staticmethod(reporterror) reporterror = staticmethod(reporterror)
def kill_server(self, startserverflag): def kill_server(self, startserverflag):
""" Kill web server
"""
if startserverflag: if startserverflag:
from py.__.test.rsession.web import kill_server from py.__.test.rsession.web import kill_server
kill_server() kill_server()
@ -172,6 +124,8 @@ class AbstractSession(object):
return new_reporter, checkfun return new_reporter, checkfun
def parse_directories(sshhosts): def parse_directories(sshhosts):
""" Parse sshadresses of hosts to have distinct hostname/hostdir
"""
directories = {} directories = {}
for host in sshhosts: for host in sshhosts:
m = re.match("^(.*?):(.*)$", host.hostname) m = re.match("^(.*?):(.*)$", host.hostname)
@ -186,9 +140,8 @@ class RSession(AbstractSession):
""" """
def main(self, reporter=None): def main(self, reporter=None):
""" main loop for running tests. """ """ main loop for running tests. """
args = self.config.remaining args = self.config.args
session_options.bind_config(self.config)
sshhosts, remotepython, rsync_roots = self.read_distributed_config() sshhosts, remotepython, rsync_roots = self.read_distributed_config()
reporter, startserverflag = self.init_reporter(reporter, reporter, startserverflag = self.init_reporter(reporter,
sshhosts, RemoteReporter) sshhosts, RemoteReporter)
@ -198,22 +151,42 @@ class RSession(AbstractSession):
pkgdir = self.getpkgdir(args[0]) pkgdir = self.getpkgdir(args[0])
done_dict = {} done_dict = {}
nodes = init_hosts(reporter, sshhosts, pkgdir, hostopts = HostOptions(rsync_roots=rsync_roots,
rsync_roots, remotepython, remote_options=remote_options.d, remote_python=remotepython,
optimise_localhost=self.optimise_localhost, done_dict=done_dict) optimise_localhost=self.optimise_localhost)
reporter(report.RsyncFinished()) hostmanager = HostManager(sshhosts, self.config, pkgdir, hostopts)
try: try:
self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict) nodes = hostmanager.init_hosts(reporter, done_dict)
finally: reporter(report.RsyncFinished())
teardown_hosts(reporter, [node.channel for node in nodes], nodes, try:
self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict)
except (KeyboardInterrupt, SystemExit):
print >>sys.stderr, "C-c pressed waiting for gateways to teardown..."
channels = [node.channel for node in nodes]
hostmanager.kill_channels(channels)
hostmanager.teardown_gateways(reporter, channels)
print >>sys.stderr, "... Done"
raise
channels = [node.channel for node in nodes]
hostmanager.teardown_hosts(reporter, channels, nodes,
exitfirst=self.config.option.exitfirst) exitfirst=self.config.option.exitfirst)
reporter(report.Nodes(nodes)) reporter(report.Nodes(nodes))
retval = reporter(report.TestFinished()) retval = reporter(report.TestFinished())
self.kill_server(startserverflag) self.kill_server(startserverflag)
return retval return retval
except (KeyboardInterrupt, SystemExit):
reporter(report.InterruptedExecution())
self.kill_server(startserverflag)
raise
except:
reporter(report.CrashedExecution())
self.kill_server(startserverflag)
raise
def read_distributed_config(self): def read_distributed_config(self):
""" Read from conftest file the configuration of distributed testing
"""
try: try:
rsync_roots = self.config.getvalue("distrsync_roots") rsync_roots = self.config.getvalue("distrsync_roots")
except: except:
@ -221,10 +194,7 @@ class RSession(AbstractSession):
sshhosts = [HostInfo(i) for i in sshhosts = [HostInfo(i) for i in
self.config.getvalue("disthosts")] self.config.getvalue("disthosts")]
parse_directories(sshhosts) parse_directories(sshhosts)
try: remotepython = self.config.getvalue("dist_remotepython")
remotepython = self.config.getvalue("dist_remotepython")
except:
remotepython = None
return sshhosts, remotepython, rsync_roots return sshhosts, remotepython, rsync_roots
def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict): def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict):
@ -247,14 +217,13 @@ class LSession(AbstractSession):
""" """
def main(self, reporter=None, runner=None): def main(self, reporter=None, runner=None):
# check out if used options makes any sense # check out if used options makes any sense
args = self.config.remaining args = self.config.args
sshhosts = [HostInfo('localhost')] # this is just an info to reporter sshhosts = [HostInfo('localhost')] # this is just an info to reporter
if not self.config.option.nomagic: if not self.config.option.nomagic:
py.magic.invoke(assertion=1) py.magic.invoke(assertion=1)
session_options.bind_config(self.config)
reporter, startserverflag = self.init_reporter(reporter, reporter, startserverflag = self.init_reporter(reporter,
sshhosts, LocalReporter, args[0]) sshhosts, LocalReporter, args[0])
reporter, checkfun = self.wrap_reporter(reporter) reporter, checkfun = self.wrap_reporter(reporter)
@ -321,4 +290,8 @@ class LSession(AbstractSession):
self.tracer = Tracer(self.docstorage) self.tracer = Tracer(self.docstorage)
return apigen_runner return apigen_runner
else: else:
return RunnerPolicy[session_options.runner_policy] if (self.config.getvalue('dist_boxing') or self.config.option.boxing)\
and not self.config.option.nocapture:
return box_runner
return plain_runner

View File

@ -1,297 +0,0 @@
""" Remote session base class
"""
import os
import py
import sys
import re
import time
from py.__.test.rsession import report
from py.__.test.rsession.master import \
setup_slave, MasterNode, dispatch_loop, itemgen, randomgen
from py.__.test.rsession.hostmanage import HostInfo, HostOptions, HostManager
from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\
box_runner
from py.__.test.rsession.reporter import LocalReporter, RemoteReporter
class AbstractSession(object):
"""
An abstract session executes collectors/items through a runner.
"""
def __init__(self, config, optimise_localhost=True):
self.config = config
self.optimise_localhost = optimise_localhost
def make_colitems(paths, baseon):
# we presume that from the base we can simply get to
# the target paths by joining the basenames
res = []
for x in paths:
x = py.path.local(x)
current = py.test.collect.Directory(baseon)
relparts = x.relto(baseon).split(x.sep)
assert relparts
for part in relparts:
next = current.join(part)
assert next is not None, (current, part)
current = next
res.append(current)
return res
make_colitems = staticmethod(make_colitems)
def getpkgdir(path):
path = py.path.local(path)
pkgpath = path.pypkgpath()
if pkgpath is None:
pkgpath = path
return pkgpath
getpkgdir = staticmethod(getpkgdir)
def init_reporter(self, reporter, sshhosts, reporter_class, arg=""):
""" This initialises so called `reporter` class, which will
handle all event presenting to user. Does not get called
if main received custom reporter
"""
startserverflag = self.config.option.startserver
restflag = self.config.option.restreport
if startserverflag and reporter is None:
from py.__.test.rsession.web import start_server, exported_methods
if self.config.option.runbrowser:
from socket import INADDR_ANY
port = INADDR_ANY # pick a random port when starting browser
else:
port = 8000 # stick to a fixed port otherwise
reporter = exported_methods.report
httpd = start_server(server_address = ('', port))
port = httpd.server_port
if self.config.option.runbrowser:
import webbrowser, thread
# webbrowser.open() may block until the browser finishes or not
url = "http://localhost:%d" % (port,)
thread.start_new_thread(webbrowser.open, (url,))
elif reporter is None:
if restflag:
from py.__.test.rsession.rest import RestReporter
reporter_class = RestReporter
if arg:
reporter_instance = reporter_class(self.config, sshhosts, self.getpkgdir(arg))
else:
reporter_instance = reporter_class(self.config, sshhosts)
reporter = reporter_instance.report
else:
startserverflag = False
return reporter, startserverflag
def reporterror(reporter, data):
excinfo, item = data
if excinfo is None:
reporter(report.ItemStart(item))
elif excinfo.type is py.test.Item.Skipped:
reporter(report.SkippedTryiter(excinfo, item))
else:
reporter(report.FailedTryiter(excinfo, item))
reporterror = staticmethod(reporterror)
def kill_server(self, startserverflag):
""" Kill web server
"""
if startserverflag:
from py.__.test.rsession.web import kill_server
kill_server()
def wrap_reporter(self, reporter):
""" We wrap reporter around, which makes it possible to us to track
number of failures
"""
self.was_failure = False
def new_reporter(event):
if isinstance(event, report.ReceivedItemOutcome) and \
not event.outcome.passed and \
not event.outcome.skipped:
self.was_failure = True
return reporter(event)
checkfun = lambda : self.config.option.exitfirst and \
self.was_failure
# for tests
self.checkfun = checkfun
return new_reporter, checkfun
def parse_directories(sshhosts):
""" Parse sshadresses of hosts to have distinct hostname/hostdir
"""
directories = {}
for host in sshhosts:
m = re.match("^(.*?):(.*)$", host.hostname)
if m:
host.hostname = m.group(1)
host.relpath = m.group(2) + "-" + host.hostname
else:
host.relpath = "pytestcache-%s" % host.hostname
class RSession(AbstractSession):
""" Remote version of session
"""
def main(self, reporter=None):
""" main loop for running tests. """
args = self.config.args
sshhosts, remotepython, rsync_roots = self.read_distributed_config()
reporter, startserverflag = self.init_reporter(reporter,
sshhosts, RemoteReporter)
reporter, checkfun = self.wrap_reporter(reporter)
reporter(report.TestStarted(sshhosts))
pkgdir = self.getpkgdir(args[0])
done_dict = {}
hostopts = HostOptions(rsync_roots=rsync_roots,
remote_python=remotepython,
optimise_localhost=self.optimise_localhost)
hostmanager = HostManager(sshhosts, self.config, pkgdir, hostopts)
try:
nodes = hostmanager.init_hosts(reporter, done_dict)
reporter(report.RsyncFinished())
try:
self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict)
except (KeyboardInterrupt, SystemExit):
print >>sys.stderr, "C-c pressed waiting for gateways to teardown..."
channels = [node.channel for node in nodes]
hostmanager.kill_channels(channels)
hostmanager.teardown_gateways(reporter, channels)
print >>sys.stderr, "... Done"
raise
channels = [node.channel for node in nodes]
hostmanager.teardown_hosts(reporter, channels, nodes,
exitfirst=self.config.option.exitfirst)
reporter(report.Nodes(nodes))
retval = reporter(report.TestFinished())
self.kill_server(startserverflag)
return retval
except (KeyboardInterrupt, SystemExit):
reporter(report.InterruptedExecution())
self.kill_server(startserverflag)
raise
except:
reporter(report.CrashedExecution())
self.kill_server(startserverflag)
raise
def read_distributed_config(self):
""" Read from conftest file the configuration of distributed testing
"""
try:
rsync_roots = self.config.getvalue("distrsync_roots")
except:
rsync_roots = None # all files and directories in the pkgdir
sshhosts = [HostInfo(i) for i in
self.config.getvalue("disthosts")]
parse_directories(sshhosts)
remotepython = self.config.getvalue("dist_remotepython")
return sshhosts, remotepython, rsync_roots
def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict):
colitems = self.make_colitems(args, baseon=pkgdir.dirpath())
keyword = self.config.option.keyword
itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror)
all_tests = dispatch_loop(nodes, itemgenerator, checkfun)
#if all_tests:
# todo = {}
# for key, value in all_tests.items():
# if key not in done_dict:
# todo[key] = True
# rg = randomgen(todo, done_dict)
# dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1)
class LSession(AbstractSession):
""" Local version of session
"""
def main(self, reporter=None, runner=None):
# check out if used options makes any sense
args = self.config.args
sshhosts = [HostInfo('localhost')] # this is just an info to reporter
if not self.config.option.nomagic:
py.magic.invoke(assertion=1)
reporter, startserverflag = self.init_reporter(reporter,
sshhosts, LocalReporter, args[0])
reporter, checkfun = self.wrap_reporter(reporter)
reporter(report.TestStarted(sshhosts))
pkgdir = self.getpkgdir(args[0])
colitems = self.make_colitems(args, baseon=pkgdir.dirpath())
reporter(report.RsyncFinished())
if runner is None:
runner = self.init_runner(pkgdir)
keyword = self.config.option.keyword
itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror)
local_loop(self, reporter, itemgenerator, checkfun, self.config, runner=runner)
retval = reporter(report.TestFinished())
self.kill_server(startserverflag)
if not self.config.option.nomagic:
py.magic.revoke(assertion=1)
self.write_docs(pkgdir)
return retval
def write_docs(self, pkgdir):
if self.config.option.apigen:
from py.__.apigen.tracer.docstorage import DocStorageAccessor
apigen = py.path.local(self.config.option.apigen).pyimport()
print >>sys.stderr, 'building documentation'
capture = py.io.OutErrCapture()
try:
try:
apigen.build(pkgdir, DocStorageAccessor(self.docstorage))
except (ValueError, AttributeError):
#import traceback
#exc, e, tb = sys.exc_info()
#print '%s - %s' % (exc, e)
#print ''.join(traceback.format_tb(tb))
#del tb
#print '-' * 79
raise NotImplementedError("Provided script does not seem "
"to contain build function")
finally:
capture.reset()
def init_runner(self, pkgdir):
if self.config.option.apigen:
from py.__.apigen.tracer.tracer import Tracer, DocStorage
module = py
try:
apigen = py.path.local(self.config.option.apigen).pyimport()
items = apigen.get_documentable_items(pkgdir)
if isinstance(items, dict):
self.docstorage = DocStorage().from_dict(items)
else:
self.docstorage = DocStorage().from_pkg(items)
except ImportError:
raise ImportError("Provided script cannot be imported")
except (ValueError, AttributeError):
raise NotImplementedError("Provided script does not seem "
"to contain get_documentable_items")
self.tracer = Tracer(self.docstorage)
return apigen_runner
else:
if (self.config.getvalue('dist_boxing') or self.config.option.boxing)\
and not self.config.option.nocapture:
return box_runner
return plain_runner

View File

@ -1,137 +0,0 @@
import py, os, stat, md5
from Queue import Queue
class RSync(object):
def __init__(self, callback=None, **options):
for name in options:
assert name in ('delete')
self.options = options
self.callback = callback
self.channels = {}
self.receivequeue = Queue()
self.links = []
def filter(self, path):
return True
def add_target(self, gateway, destdir, finishedcallback=None):
def itemcallback(req):
self.receivequeue.put((channel, req))
channel = gateway.remote_exec(REMOTE_SOURCE)
channel.setcallback(itemcallback, endmarker = None)
channel.send((str(destdir), self.options))
self.channels[channel] = finishedcallback
def send(self, sourcedir):
self.sourcedir = str(sourcedir)
# normalize a trailing '/' away
self.sourcedir = os.path.dirname(os.path.join(self.sourcedir, 'x'))
# send directory structure and file timestamps/sizes
self._send_directory_structure(self.sourcedir)
# paths and to_send are only used for doing
# progress-related callbacks
self.paths = {}
self.to_send = {}
# send modified file to clients
while self.channels:
channel, req = self.receivequeue.get()
if req is None:
# end-of-channel
if channel in self.channels:
# too early! we must have got an error
channel.waitclose()
# or else we raise one
raise IOError('connection unexpectedly closed: %s ' % (
channel.gateway,))
else:
command, data = req
if command == "links":
for link in self.links:
channel.send(link)
# completion marker, this host is done
channel.send(42)
elif command == "done":
finishedcallback = self.channels.pop(channel)
if finishedcallback:
finishedcallback()
elif command == "ack":
if self.callback:
self.callback("ack", self.paths[data], channel)
elif command == "list_done":
# sum up all to send
if self.callback:
s = sum([self.paths[i] for i in self.to_send[channel]])
self.callback("list", s, channel)
elif command == "send":
modified_rel_path, checksum = data
modifiedpath = os.path.join(self.sourcedir, *modified_rel_path)
f = open(modifiedpath, 'rb')
data = f.read()
# provide info to progress callback function
modified_rel_path = "/".join(modified_rel_path)
self.paths[modified_rel_path] = len(data)
if channel not in self.to_send:
self.to_send[channel] = []
self.to_send[channel].append(modified_rel_path)
f.close()
if checksum is not None and checksum == md5.md5(data).digest():
data = None # not really modified
else:
# ! there is a reason for the interning:
# sharing multiple copies of the file's data
data = intern(data)
print '%s <= %s' % (
channel.gateway._getremoteaddress(),
modified_rel_path)
channel.send(data)
del data
else:
assert "Unknown command %s" % command
def _broadcast(self, msg):
for channel in self.channels:
channel.send(msg)
def _send_link(self, basename, linkpoint):
self.links.append(("link", basename, linkpoint))
def _send_directory_structure(self, path):
st = os.lstat(path)
if stat.S_ISREG(st.st_mode):
# regular file: send a timestamp/size pair
self._broadcast((st.st_mtime, st.st_size))
elif stat.S_ISDIR(st.st_mode):
# dir: send a list of entries
names = []
subpaths = []
for name in os.listdir(path):
p = os.path.join(path, name)
if self.filter(p):
names.append(name)
subpaths.append(p)
self._broadcast(names)
for p in subpaths:
self._send_directory_structure(p)
elif stat.S_ISLNK(st.st_mode):
linkpoint = os.readlink(path)
basename = path[len(self.sourcedir) + 1:]
if not linkpoint.startswith(os.sep):
# relative link, just send it
self._send_link(basename, linkpoint)
elif linkpoint.startswith(self.sourcedir):
self._send_link(basename, linkpoint[len(self.sourcedir) + 1:])
else:
self._send_link(basename, linkpoint)
self._broadcast(None)
else:
raise ValueError, "cannot sync %r" % (path,)
REMOTE_SOURCE = py.path.local(__file__).dirpath().\
join('rsync_remote.py').open().read() + "\nf()"

View File

@ -5,28 +5,65 @@ Node code for slaves.
import py import py
from py.__.test.rsession.executor import RunExecutor, BoxExecutor, AsyncExecutor from py.__.test.rsession.executor import RunExecutor, BoxExecutor, AsyncExecutor
from py.__.test.rsession.outcome import Outcome from py.__.test.rsession.outcome import Outcome
import thread
import os
class Info: class PidInfo(object):
pid = None """ Pure container class to store information of actually running
pid
"""
def __init__(self):
self.pid = 0
self.lock = thread.allocate_lock()
def set_pid(self, pid):
self.lock.acquire()
try:
self.pid = pid
finally:
self.lock.release()
def kill(self):
self.lock.acquire()
try:
if self.pid:
os.kill(self.pid, 15)
self.pid = 0
finally:
self.lock.release()
def waitandclear(self, pid, num):
""" This is an obscure hack to keep locking properly, adhere to posix semantics
and try to clean it as much as possible, not clean at all
"""
self.lock.acquire()
try:
retval = os.waitpid(self.pid, 0)
self.pid = 0
return retval
finally:
self.lock.release()
class SlaveNode(object): class SlaveNode(object):
def __init__(self, rootcollector, executor=AsyncExecutor): def __init__(self, rootcollector, config, pidinfo, executor=AsyncExecutor):
self.rootcollector = rootcollector self.rootcollector = rootcollector
self.config = config
self.executor = executor self.executor = executor
self.pidinfo = pidinfo
def execute(self, itemspec): def execute(self, itemspec):
item = self.rootcollector.getitembynames(itemspec) item = self.rootcollector.getitembynames(itemspec)
#if isinstance(item, py.test.Function): #if isinstance(item, py.test.Function):
# ex = Executor(item.obj, setup=item.setup) # ex = Executor(item.obj, setup=item.setup)
#else: #else:
ex = self.executor(item) ex = self.executor(item, config=self.config)
if self.executor is AsyncExecutor: if self.executor is AsyncExecutor:
cont, pid = ex.execute() cont, pid = ex.execute()
self.pidinfo.set_pid(pid)
else: else:
# for tests only # for tests only
return ex.execute() return ex.execute()
Info.pid = pid return cont(self.pidinfo.waitandclear)
return cont()
def run(self, itemspec): def run(self, itemspec):
#outcome = self.execute(itemspec) #outcome = self.execute(itemspec)
@ -35,9 +72,9 @@ class SlaveNode(object):
if self.executor.wraps: if self.executor.wraps:
return outcome return outcome
else: else:
return outcome.make_repr() return outcome.make_repr(self.config.option.tbstyle)
def slave_main(receive, send, path, info = None): def slave_main(receive, send, path, config, pidinfo):
import os import os
assert os.path.exists(path) assert os.path.exists(path)
path = os.path.abspath(path) path = os.path.abspath(path)
@ -47,7 +84,7 @@ def slave_main(receive, send, path, info = None):
if node is not None: if node is not None:
return node return node
col = py.test.collect.Directory(str(py.path.local(path).join(item[0]))) col = py.test.collect.Directory(str(py.path.local(path).join(item[0])))
node = nodes[item[0]] = SlaveNode(col) node = nodes[item[0]] = SlaveNode(col, config, pidinfo)
return node return node
while 1: while 1:
nextitem = receive() nextitem = receive()
@ -62,8 +99,7 @@ def slave_main(receive, send, path, info = None):
excinfo = py.code.ExceptionInfo() excinfo = py.code.ExceptionInfo()
send(Outcome(excinfo=excinfo, is_critical=True).make_repr()) send(Outcome(excinfo=excinfo, is_critical=True).make_repr())
else: else:
from py.__.test.rsession.rsession import remote_options if not res[0] and not res[3] and config.option.exitfirst:
if not res[0] and not res[3] and remote_options.exitfirst:
# we're finished, but need to eat what we can # we're finished, but need to eat what we can
send(res) send(res)
break break
@ -71,43 +107,42 @@ def slave_main(receive, send, path, info = None):
while nextitem is not None: while nextitem is not None:
nextitem = receive() nextitem = receive()
def setup(): def setup():
def callback_gen(queue): def callback_gen(channel, queue, info):
from py.__.test.rsession.slave import Info
def callback(item): def callback(item):
if item == 42: # magic call-cleanup if item == 42: # magic call-cleanup
# XXX should kill a pid here # XXX should kill a pid here
if Info.pid: info.kill()
os.kill(Info.pid, 15) channel.close()
sys.exit(0) sys.exit(0)
queue.put(item) queue.put(item)
return callback return callback
import os, sys import os, sys
from Queue import Queue
pkgdir = channel.receive() # path is ready pkgdir = channel.receive() # path is ready
options = channel.receive() # options stuff, should be dictionary config_repr = channel.receive()
basedir = os.path.dirname(pkgdir) basedir = os.path.dirname(pkgdir)
pkgname = os.path.basename(pkgdir) pkgname = os.path.basename(pkgdir)
# setup defaults... # setup defaults...
sys.path.insert(0, basedir) sys.path.insert(0, basedir)
import py import py
options['we_are_remote'] = True config = py.test.config
from py.__.test.rsession.rsession import RemoteOptions if config._initialized:
from py.__.test.rsession.slave import Info config = config._reparse([basedir])
Info.pid = 0 config.merge_repr(config_repr)
from py.__.test.rsession import rsession else:
rsession.remote_options = RemoteOptions(options) config.initdirect(basedir, config_repr)
# XXX the following assumes that py lib is there, a bit #config.conftest.lget('adddefaultoptions')()
# much of an assumtion if not config.option.nomagic:
if not rsession.remote_options.nomagic:
py.magic.invoke(assertion=1) py.magic.invoke(assertion=1)
mod = __import__(pkgname) mod = __import__(pkgname)
assert py.path.local(mod.__file__).dirpath() == py.path.local(pkgdir) assert py.path.local(mod.__file__).dirpath() == py.path.local(pkgdir)
from py.__.test.rsession.slave import slave_main from py.__.test.rsession.slave import slave_main, PidInfo
queue = Queue() queue = py.std.Queue.Queue()
channel.setcallback(callback_gen(queue)) pidinfo = PidInfo()
slave_main(queue.get, channel.send, basedir) channel.setcallback(callback_gen(channel, queue, pidinfo))
if not rsession.remote_options.nomagic: slave_main(queue.get, channel.send, basedir, config, pidinfo)
if not config.option.nomagic:
py.magic.revoke(assertion=1) py.magic.revoke(assertion=1)
channel.close()

View File

@ -9,17 +9,16 @@ if sys.platform == 'win32':
from py.__.test.rsession.box import Box from py.__.test.rsession.box import Box
from py.__.test.rsession.testing import example2 from py.__.test.rsession.testing import example2
from py.__.test.rsession.conftest import option
def setup_module(mod): def setup_module(mod):
from py.__.test.rsession.rsession import remote_options mod.rootdir = py.path.local(py.__file__).dirpath().dirpath()
remote_options['nice_level'] = 0 mod.config = py.test.config._reparse([mod.rootdir])
def test_basic_boxing(): def test_basic_boxing():
# XXX: because we do not have option transfer # XXX: because we do not have option transfer
## if not hasattr(option, 'nocapture') or not option.nocapture: ## if not hasattr(option, 'nocapture') or not option.nocapture:
## py.test.skip("Interacts with pylib i/o skipping which is bad actually") ## py.test.skip("Interacts with pylib i/o skipping which is bad actually")
b = Box(example2.boxf1) b = Box(example2.boxf1, config=config)
b.run() b.run()
assert b.stdoutrepr == "some out\n" assert b.stdoutrepr == "some out\n"
assert b.stderrrepr == "some err\n" assert b.stderrrepr == "some err\n"
@ -28,7 +27,7 @@ def test_basic_boxing():
assert b.retval == 1 assert b.retval == 1
def test_boxing_on_fds(): def test_boxing_on_fds():
b = Box(example2.boxf2) b = Box(example2.boxf2, config=config)
b.run() b.run()
assert b.stdoutrepr == "someout" assert b.stdoutrepr == "someout"
assert b.stderrrepr == "someerr" assert b.stderrrepr == "someerr"
@ -37,13 +36,13 @@ def test_boxing_on_fds():
assert b.retval == 2 assert b.retval == 2
def test_boxing_signal(): def test_boxing_signal():
b = Box(example2.boxseg) b = Box(example2.boxseg, config=config)
b.run() b.run()
assert b.signal == 11 assert b.signal == 11
assert b.retval is None assert b.retval is None
def test_boxing_huge_data(): def test_boxing_huge_data():
b = Box(example2.boxhuge) b = Box(example2.boxhuge, config=config)
b.run() b.run()
assert b.stdoutrepr assert b.stdoutrepr
assert b.exitstat == 0 assert b.exitstat == 0
@ -53,7 +52,7 @@ def test_boxing_huge_data():
def test_box_seq(): def test_box_seq():
# we run many boxes with huge data, just one after another # we run many boxes with huge data, just one after another
for i in xrange(100): for i in xrange(100):
b = Box(example2.boxhuge) b = Box(example2.boxhuge, config=config)
b.run() b.run()
assert b.stdoutrepr assert b.stdoutrepr
assert b.exitstat == 0 assert b.exitstat == 0
@ -62,13 +61,13 @@ def test_box_seq():
def test_box_in_a_box(): def test_box_in_a_box():
def boxfun(): def boxfun():
b = Box(example2.boxf2) b = Box(example2.boxf2, config=config)
b.run() b.run()
print b.stdoutrepr print b.stdoutrepr
print >>sys.stderr, b.stderrrepr print >>sys.stderr, b.stderrrepr
return b.retval return b.retval
b = Box(boxfun) b = Box(boxfun, config=config)
b.run() b.run()
assert b.stdoutrepr == "someout\n" assert b.stdoutrepr == "someout\n"
assert b.stderrrepr == "someerr\n" assert b.stderrrepr == "someerr\n"
@ -77,8 +76,6 @@ def test_box_in_a_box():
assert b.retval == 2 assert b.retval == 2
def test_box_killer(): def test_box_killer():
if option.skip_kill_test:
py.test.skip('skipping kill test')
class A: class A:
pass pass
info = A() info = A()
@ -87,7 +84,7 @@ def test_box_killer():
def box_fun(): def box_fun():
time.sleep(10) # we don't want to last forever here time.sleep(10) # we don't want to last forever here
b = Box(box_fun) b = Box(box_fun, config=config)
par, pid = b.run(continuation=True) par, pid = b.run(continuation=True)
os.kill(pid, 15) os.kill(pid, 15)
par(pid) par(pid)

View File

@ -1,24 +0,0 @@
""" test session config options
"""
import py
from py.__.test.rsession.rsession import session_options, SessionOptions,\
remote_options
def test_session_opts():
tmpdir = py.test.ensuretemp("sessionopts")
tmpdir.ensure("conftest.py").write("""class SessionOptions:
max_tasks_per_node = 5
nice_level = 10
""")
tmp2 = py.test.ensuretemp("xxx")
args = [str(tmpdir)]
config = py.test.config._reparse(args)
session_options.bind_config(config)
assert session_options.max_tasks_per_node == 5
assert remote_options.nice_level == 10
config = py.test.config._reparse([str(tmp2)])
session_options.bind_config(config)
assert session_options.max_tasks_per_node == \
SessionOptions.defaults['max_tasks_per_node']

View File

@ -10,8 +10,7 @@ from py.__.test.rsession.testing.test_slave import funcprint_spec, \
def setup_module(mod): def setup_module(mod):
mod.rootdir = py.path.local(py.__file__).dirpath().dirpath() mod.rootdir = py.path.local(py.__file__).dirpath().dirpath()
from py.__.test.rsession.rsession import remote_options mod.config = py.test.config._reparse([mod.rootdir])
remote_options['nice_level'] = 0
def XXXtest_executor_passing_function(): def XXXtest_executor_passing_function():
ex = Executor(example1.f1) ex = Executor(example1.f1)
@ -61,32 +60,32 @@ class ItemTestSkipping(py.test.Item):
py.test.skip("hello") py.test.skip("hello")
def test_run_executor(): def test_run_executor():
ex = RunExecutor(ItemTestPassing("pass")) ex = RunExecutor(ItemTestPassing("pass"), config=config)
outcome = ex.execute() outcome = ex.execute()
assert outcome.passed assert outcome.passed
ex = RunExecutor(ItemTestFailing("fail")) ex = RunExecutor(ItemTestFailing("fail"), config=config)
outcome = ex.execute() outcome = ex.execute()
assert not outcome.passed assert not outcome.passed
ex = RunExecutor(ItemTestSkipping("skip")) ex = RunExecutor(ItemTestSkipping("skip"), config=config)
outcome = ex.execute() outcome = ex.execute()
assert outcome.skipped assert outcome.skipped
assert not outcome.passed assert not outcome.passed
assert not outcome.excinfo assert not outcome.excinfo
def test_box_executor(): def test_box_executor():
ex = BoxExecutor(ItemTestPassing("pass")) ex = BoxExecutor(ItemTestPassing("pass"), config=config)
outcome_repr = ex.execute() outcome_repr = ex.execute()
outcome = ReprOutcome(outcome_repr) outcome = ReprOutcome(outcome_repr)
assert outcome.passed assert outcome.passed
ex = BoxExecutor(ItemTestFailing("fail")) ex = BoxExecutor(ItemTestFailing("fail"), config=config)
outcome_repr = ex.execute() outcome_repr = ex.execute()
outcome = ReprOutcome(outcome_repr) outcome = ReprOutcome(outcome_repr)
assert not outcome.passed assert not outcome.passed
ex = BoxExecutor(ItemTestSkipping("skip")) ex = BoxExecutor(ItemTestSkipping("skip"), config=config)
outcome_repr = ex.execute() outcome_repr = ex.execute()
outcome = ReprOutcome(outcome_repr) outcome = ReprOutcome(outcome_repr)
assert outcome.skipped assert outcome.skipped
@ -96,7 +95,7 @@ def test_box_executor():
def test_box_executor_stdout(): def test_box_executor_stdout():
rootcol = py.test.collect.Directory(rootdir) rootcol = py.test.collect.Directory(rootdir)
item = rootcol.getitembynames(funcprint_spec) item = rootcol.getitembynames(funcprint_spec)
ex = BoxExecutor(item) ex = BoxExecutor(item, config=config)
outcome_repr = ex.execute() outcome_repr = ex.execute()
outcome = ReprOutcome(outcome_repr) outcome = ReprOutcome(outcome_repr)
assert outcome.passed assert outcome.passed
@ -105,7 +104,7 @@ def test_box_executor_stdout():
def test_box_executor_stdout_error(): def test_box_executor_stdout_error():
rootcol = py.test.collect.Directory(rootdir) rootcol = py.test.collect.Directory(rootdir)
item = rootcol.getitembynames(funcprintfail_spec) item = rootcol.getitembynames(funcprintfail_spec)
ex = BoxExecutor(item) ex = BoxExecutor(item, config=config)
outcome_repr = ex.execute() outcome_repr = ex.execute()
outcome = ReprOutcome(outcome_repr) outcome = ReprOutcome(outcome_repr)
assert not outcome.passed assert not outcome.passed
@ -114,7 +113,7 @@ def test_box_executor_stdout_error():
def test_cont_executor(): def test_cont_executor():
rootcol = py.test.collect.Directory(rootdir) rootcol = py.test.collect.Directory(rootdir)
item = rootcol.getitembynames(funcprintfail_spec) item = rootcol.getitembynames(funcprintfail_spec)
ex = AsyncExecutor(item) ex = AsyncExecutor(item, config=config)
cont, pid = ex.execute() cont, pid = ex.execute()
assert pid assert pid
outcome_repr = cont() outcome_repr = cont()

View File

@ -5,7 +5,7 @@
import py import py
from py.__.test.rsession.rsession import LSession from py.__.test.rsession.rsession import LSession
from py.__.test.rsession import report from py.__.test.rsession import report
from py.__.test.rsession.local import box_runner, plain_runner from py.__.test.rsession.local import box_runner, plain_runner, apigen_runner
def setup_module(mod): def setup_module(mod):
mod.tmp = py.test.ensuretemp("lsession_module") mod.tmp = py.test.ensuretemp("lsession_module")
@ -82,22 +82,26 @@ class TestLSession(object):
l = [] l = []
def some_fun(*args): def some_fun(*args):
l.append(args) l.append(args)
pdb.post_mortem = some_fun
args = [str(tmpdir.join(subdir)), '--pdb']
config = py.test.config._reparse(args)
lsession = LSession(config)
allevents = []
try: try:
lsession.main(reporter=allevents.append, runner=plain_runner) post_mortem = pdb.post_mortem
except SystemExit: pdb.post_mortem = some_fun
pass args = [str(tmpdir.join(subdir)), '--pdb']
else: config = py.test.config._reparse(args)
py.test.fail("Didn't raise system exit") lsession = LSession(config)
failure_events = [event for event in allevents if isinstance(event, allevents = []
report.ImmediateFailure)] try:
assert len(failure_events) == 1 lsession.main(reporter=allevents.append, runner=plain_runner)
assert len(l) == 1 except SystemExit:
pass
else:
py.test.fail("Didn't raise system exit")
failure_events = [event for event in allevents if isinstance(event,
report.ImmediateFailure)]
assert len(failure_events) == 1
assert len(l) == 1
finally:
pdb.post_mortem = post_mortem
def test_minus_x(self): def test_minus_x(self):
if not hasattr(py.std.os, 'fork'): if not hasattr(py.std.os, 'fork'):
@ -262,3 +266,31 @@ class TestLSession(object):
assert testevents[0].outcome.passed assert testevents[0].outcome.passed
assert testevents[0].outcome.stderr == "" assert testevents[0].outcome.stderr == ""
assert testevents[0].outcome.stdout == "1\n2\n3\n" assert testevents[0].outcome.stdout == "1\n2\n3\n"
def test_runner_selection(self):
tmpdir = py.test.ensuretemp("lsession_runner_selection")
tmpdir.ensure("apigen.py").write(py.code.Source("""
def get_documentable_items(*args):
return {}
"""))
opt_mapping = {
'': plain_runner,
'--box': box_runner,
'--apigen=%s/apigen.py' % str(tmpdir): apigen_runner,
}
pkgdir = tmpdir.dirpath()
for opt in opt_mapping.keys():
if opt:
all = opt + " " + str(tmpdir)
else:
all = str(tmpdir)
config = py.test.config._reparse(all.split(" "))
lsession = LSession(config)
assert lsession.init_runner(pkgdir) is opt_mapping[opt]
#tmpdir.dirpath().ensure("conftest.py").write(py.code.Source("""
#dist_boxing=True
#"""))
#config = py.test.config._reparse([str(tmpdir)])
#lsession = LSession(config)
#assert lsession.init_runner(pkgdir) is box_runner
# XXX check why it fails

View File

@ -11,17 +11,14 @@ if sys.platform == 'win32':
from py.__.test.rsession.master import dispatch_loop, setup_slave, MasterNode, randomgen from py.__.test.rsession.master import dispatch_loop, setup_slave, MasterNode, randomgen
from py.__.test.rsession.outcome import ReprOutcome, Outcome from py.__.test.rsession.outcome import ReprOutcome, Outcome
from py.__.test.rsession.testing.test_slave import funcpass_spec, funcfail_spec from py.__.test.rsession.testing.test_slave import funcpass_spec, funcfail_spec, funchang_spec
from py.__.test.rsession import report from py.__.test.rsession import report
from py.__.test.rsession.rsession import session_options, remote_options
from py.__.test.rsession.hostmanage import HostInfo from py.__.test.rsession.hostmanage import HostInfo
def setup_module(mod): def setup_module(mod):
# bind an empty config # bind an empty config
config = py.test.config._reparse([]) config = py.test.config._reparse([])
config.option.max_tasks_per_node = 10 config._overwrite('dist_taskspernode', 10)
session_options.bind_config(config)
#assert not remote_options.exitfirst
mod.pkgdir = py.path.local(py.__file__).dirpath() mod.pkgdir = py.path.local(py.__file__).dirpath()
class DummyGateway(object): class DummyGateway(object):
@ -92,11 +89,12 @@ def test_dispatch_loop():
def test_slave_setup(): def test_slave_setup():
gw = py.execnet.PopenGateway() gw = py.execnet.PopenGateway()
channel = setup_slave(gw, pkgdir, remote_options.d) config = py.test.config._reparse([])
channel = setup_slave(gw, pkgdir, config)
channel.send(funcpass_spec) channel.send(funcpass_spec)
output = ReprOutcome(channel.receive()) output = ReprOutcome(channel.receive())
assert output.passed assert output.passed
channel.send(None) channel.send(42)
channel.waitclose(10) channel.waitclose(10)
gw.exit() gw.exit()
@ -114,7 +112,8 @@ def test_slave_running():
gw = py.execnet.PopenGateway() gw = py.execnet.PopenGateway()
gw.host = HostInfo("localhost") gw.host = HostInfo("localhost")
gw.host.gw = gw gw.host.gw = gw
channel = setup_slave(gw, pkgdir, remote_options.d) config = py.test.config._reparse([])
channel = setup_slave(gw, pkgdir, config)
mn = MasterNode(channel, simple_report, {}) mn = MasterNode(channel, simple_report, {})
return mn return mn
@ -127,6 +126,34 @@ def test_slave_running():
shouldstop = lambda : False shouldstop = lambda : False
dispatch_loop(master_nodes, itemgenerator, shouldstop) dispatch_loop(master_nodes, itemgenerator, shouldstop)
def test_slave_running_interrupted():
#def simple_report(event):
# if not isinstance(event, report.ReceivedItemOutcome):
# return
# item = event.item
# if item.code.name == 'funcpass':
# assert event.outcome.passed
# else:
# assert not event.outcome.passed
reports = []
def open_gw():
gw = py.execnet.PopenGateway()
gw.host = HostInfo("localhost")
gw.host.gw = gw
config = py.test.config._reparse([])
channel = setup_slave(gw, pkgdir, config)
mn = MasterNode(channel, reports.append, {})
return mn, gw, channel
mn, gw, channel = open_gw()
rootcol = py.test.collect.Directory(pkgdir.dirpath())
funchang_item = rootcol.getitembynames(funchang_spec)
mn.send(funchang_item)
mn.send(StopIteration)
# XXX: We have to wait here a bit to make sure that it really did happen
channel.waitclose(2)
def test_randomgen(): def test_randomgen():
d = {} d = {}
gen = randomgen({1:True, 2:True, 3:True}, d) gen = randomgen({1:True, 2:True, 3:True}, d)

View File

@ -28,7 +28,7 @@ def test_exception_info_repr():
except: except:
outcome = Outcome(excinfo=py.code.ExceptionInfo()) outcome = Outcome(excinfo=py.code.ExceptionInfo())
repr = outcome.make_excinfo_repr() repr = outcome.make_excinfo_repr("long")
assert marshal.dumps(repr) assert marshal.dumps(repr)
excinfo = ExcInfoRepr(repr) excinfo = ExcInfoRepr(repr)

View File

@ -166,11 +166,12 @@ class AbstractTestReporter(object):
r.report(report.HostReady(hosts[2])) r.report(report.HostReady(hosts[2]))
out, err = cap.reset() out, err = cap.reset()
assert not err assert not err
expected = """================= Test started, hosts: host1, host2, host3 ================== expected1 = "Test started, hosts: host1, host2, host3"
host1: READY (still 2 to go) expected2 = """host1: READY (still 2 to go)
host2: READY (still 1 to go) host2: READY (still 1 to go)
host3: READY""" host3: READY"""
assert out.find(expected) != -1 assert out.find(expected1) != -1
assert out.find(expected2) != -1
class TestLocalReporter(AbstractTestReporter): class TestLocalReporter(AbstractTestReporter):
reporter = LocalReporter reporter = LocalReporter
@ -181,8 +182,9 @@ class TestLocalReporter(AbstractTestReporter):
def test_module(self): def test_module(self):
#py.test.skip("XXX rewrite test to not rely on exact formatting") #py.test.skip("XXX rewrite test to not rely on exact formatting")
assert self._test_module().endswith("test_slave.py[9] FsF."),\ output = self._test_module()
self._test_module() assert output.find("test_slave") != -1
assert output.endswith("FsF."), output
def test_full_module(self): def test_full_module(self):
#py.test.skip("XXX rewrite test to not rely on exact formatting") #py.test.skip("XXX rewrite test to not rely on exact formatting")
@ -202,20 +204,20 @@ class TestRemoteReporter(AbstractTestReporter):
def test_report_received_item_outcome(self): def test_report_received_item_outcome(self):
#py.test.skip("XXX rewrite test to not rely on exact formatting") #py.test.skip("XXX rewrite test to not rely on exact formatting")
val = self.report_received_item_outcome() val = self.report_received_item_outcome()
expected = """ localhost: FAILED py test rsession testing test_slave.py funcpass expected = """ localhost: FAILED py.test.rsession.testing.test_slave.py funcpass
localhost: SKIPPED py test rsession testing test_slave.py funcpass localhost: SKIPPED py.test.rsession.testing.test_slave.py funcpass
localhost: FAILED py test rsession testing test_slave.py funcpass localhost: FAILED py.test.rsession.testing.test_slave.py funcpass
localhost: PASSED py test rsession testing test_slave.py funcpass localhost: PASSED py.test.rsession.testing.test_slave.py funcpass
""" """
assert val.find(expected) != -1 assert val.find(expected) != -1
def test_module(self): def test_module(self):
val = self._test_module() val = self._test_module()
print val print val
expected = """ localhost: FAILED py test rsession testing test_slave.py funcpass expected = """ localhost: FAILED py.test.rsession.testing.test_slave.py funcpass
localhost: SKIPPED py test rsession testing test_slave.py funcpass localhost: SKIPPED py.test.rsession.testing.test_slave.py funcpass
localhost: FAILED py test rsession testing test_slave.py funcpass localhost: FAILED py.test.rsession.testing.test_slave.py funcpass
localhost: PASSED py test rsession testing test_slave.py funcpass localhost: PASSED py.test.rsession.testing.test_slave.py funcpass
""" """
assert val.find(expected) != -1 assert val.find(expected) != -1

View File

@ -10,6 +10,8 @@ from py.__.test.rsession.rest import RestReporter, NoLinkWriter
from py.__.rest.rst import * from py.__.rest.rst import *
from py.__.test.rsession.hostmanage import HostInfo from py.__.test.rsession.hostmanage import HostInfo
py.test.skip("This tests are not really testing, needs rewrite")
class RestTestReporter(RestReporter): class RestTestReporter(RestReporter):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if args: if args:

View File

@ -5,12 +5,12 @@
import py import py
from py.__.test.rsession import report from py.__.test.rsession import report
from py.__.test.rsession.rsession import RSession, parse_directories,\ from py.__.test.rsession.rsession import RSession, parse_directories,\
session_options, remote_options, parse_directories parse_directories
from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts,\ from py.__.test.rsession.hostmanage import HostOptions, HostManager,\
HostInfo HostInfo
from py.__.test.rsession.testing.test_slave import funcfail_spec,\ from py.__.test.rsession.testing.test_slave import funcfail_spec,\
funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \
funcoptioncustom_spec, funcoption_spec funcoptioncustom_spec
def setup_module(mod): def setup_module(mod):
mod.pkgdir = py.path.local(py.__file__).dirpath() mod.pkgdir = py.path.local(py.__file__).dirpath()
@ -18,7 +18,8 @@ def setup_module(mod):
def test_setup_non_existing_hosts(): def test_setup_non_existing_hosts():
setup_events = [] setup_events = []
hosts = [HostInfo("alskdjalsdkjasldkajlsd")] hosts = [HostInfo("alskdjalsdkjasldkajlsd")]
cmd = "init_hosts(setup_events.append, hosts, pkgdir)" hm = HostManager(hosts, None, pkgdir)
cmd = "hm.init_hosts(setup_events.append)"
py.test.raises((py.process.cmdexec.Error, IOError, EOFError), cmd) py.test.raises((py.process.cmdexec.Error, IOError, EOFError), cmd)
#assert setup_events #assert setup_events
@ -105,7 +106,7 @@ class TestRSessionRemote:
tmpdir = py.test.ensuretemp("example_distribution") tmpdir = py.test.ensuretemp("example_distribution")
tmpdir.ensure(subdir, "conftest.py").write(py.code.Source(""" tmpdir.ensure(subdir, "conftest.py").write(py.code.Source("""
disthosts = [%r] disthosts = [%r]
distrsync_roots = ["%s"] distrsync_roots = ["%s", "py"]
""" % ('localhost', subdir))) """ % ('localhost', subdir)))
tmpdir.ensure(subdir, "__init__.py") tmpdir.ensure(subdir, "__init__.py")
tmpdir.ensure(subdir, "test_one.py").write(py.code.Source(""" tmpdir.ensure(subdir, "test_one.py").write(py.code.Source("""
@ -119,7 +120,11 @@ class TestRSessionRemote:
pass pass
def test_5(): def test_5():
assert __file__ != '%s' assert __file__ != '%s'
""" % str(tmpdir.join(subdir)))) def test_6():
import py
assert py.__file__ != '%s'
""" % (str(tmpdir.join(subdir)), py.__file__)))
tmpdir.join("py").mksymlinkto(py.path.local(py.__file__).dirpath())
args = [str(tmpdir.join(subdir))] args = [str(tmpdir.join(subdir))]
config = py.test.config._reparse(args) config = py.test.config._reparse(args)
rsession = RSession(config, optimise_localhost=False) rsession = RSession(config, optimise_localhost=False)
@ -131,21 +136,21 @@ class TestRSessionRemote:
passevents = [i for i in testevents if i.outcome.passed] passevents = [i for i in testevents if i.outcome.passed]
failevents = [i for i in testevents if i.outcome.excinfo] failevents = [i for i in testevents if i.outcome.excinfo]
skippedevents = [i for i in testevents if i.outcome.skipped] skippedevents = [i for i in testevents if i.outcome.skipped]
assert len(testevents) == 5 assert len(testevents) == 6
assert len(passevents) == 2 assert len(passevents) == 3
assert len(failevents) == 3 assert len(failevents) == 3
tb = failevents[0].outcome.excinfo.traceback tb = failevents[0].outcome.excinfo.traceback
assert tb[0].path.find("test_one") != -1 assert str(tb[0].path).find("test_one") != -1
assert tb[0].source.find("test_2") != -1 assert tb[0].source.find("test_2") != -1
assert failevents[0].outcome.excinfo.typename == 'AssertionError' assert failevents[0].outcome.excinfo.typename == 'AssertionError'
tb = failevents[1].outcome.excinfo.traceback tb = failevents[1].outcome.excinfo.traceback
assert tb[0].path.find("test_one") != -1 assert str(tb[0].path).find("test_one") != -1
assert tb[0].source.find("test_3") != -1 assert tb[0].source.find("test_3") != -1
assert failevents[1].outcome.excinfo.typename == 'ValueError' assert failevents[1].outcome.excinfo.typename == 'ValueError'
assert failevents[1].outcome.excinfo.value == '23' assert failevents[1].outcome.excinfo.value == '23'
tb = failevents[2].outcome.excinfo.traceback tb = failevents[2].outcome.excinfo.traceback
assert failevents[2].outcome.excinfo.typename == 'TypeError' assert failevents[2].outcome.excinfo.typename == 'TypeError'
assert tb[0].path.find("executor") != -1 assert str(tb[0].path).find("executor") != -1
assert tb[0].source.find("execute") != -1 assert tb[0].source.find("execute") != -1
def test_setup_teardown_ssh(self): def test_setup_teardown_ssh(self):
@ -155,10 +160,10 @@ class TestRSessionRemote:
teardown_events = [] teardown_events = []
config = py.test.config._reparse([]) config = py.test.config._reparse([])
session_options.bind_config(config) opts = HostOptions(optimise_localhost=False, rsync_roots=['py'])
nodes = init_hosts(setup_events.append, hosts, pkgdir, hm = HostManager(hosts, config, pkgdir, opts)
rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) nodes = hm.init_hosts(setup_events.append)
teardown_hosts(teardown_events.append, hm.teardown_hosts(teardown_events.append,
[node.channel for node in nodes], nodes) [node.channel for node in nodes], nodes)
count_rsyn_calls = [i for i in setup_events count_rsyn_calls = [i for i in setup_events
@ -182,9 +187,9 @@ class TestRSessionRemote:
allevents = [] allevents = []
config = py.test.config._reparse([]) config = py.test.config._reparse([])
session_options.bind_config(config) opts = HostOptions(optimise_localhost=False, rsync_roots=['py'])
nodes = init_hosts(allevents.append, hosts, pkgdir, hm = HostManager(hosts, config, pkgdir, opts)
rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) nodes = hm.init_hosts(allevents.append)
from py.__.test.rsession.testing.test_executor \ from py.__.test.rsession.testing.test_executor \
import ItemTestPassing, ItemTestFailing, ItemTestSkipping import ItemTestPassing, ItemTestFailing, ItemTestSkipping
@ -202,7 +207,7 @@ class TestRSessionRemote:
node.send(itemskip) node.send(itemskip)
node.send(itemprint) node.send(itemprint)
teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes)
events = [i for i in allevents events = [i for i in allevents
if isinstance(i, report.ReceivedItemOutcome)] if isinstance(i, report.ReceivedItemOutcome)]
@ -224,31 +229,33 @@ class TestRSessionRemote:
hosts = [HostInfo('localhost')] hosts = [HostInfo('localhost')]
parse_directories(hosts) parse_directories(hosts)
config = py.test.config._reparse([]) config = py.test.config._reparse([])
session_options.bind_config(config) config._overwrite('custom', 'custom')
d = remote_options.d.copy() # we need to overwrite default list to serialize
d['custom'] = 'custom' from py.__.test.rsession.master import defaultconftestnames
nodes = init_hosts(allevents.append, hosts, pkgdir, defaultconftestnames.append("custom")
rsync_roots=["py"], remote_options=d, try:
optimise_localhost=False) opts = HostOptions(optimise_localhost=False, rsync_roots=['py'])
hm = HostManager(hosts, config, pkgdir, opts)
rootcol = py.test.collect.Directory(pkgdir.dirpath()) nodes = hm.init_hosts(allevents.append)
itempass = rootcol.getitembynames(funcoption_spec)
itempassaswell = rootcol.getitembynames(funcoptioncustom_spec) rootcol = py.test.collect.Directory(pkgdir.dirpath())
itempass = rootcol.getitembynames(funcoptioncustom_spec)
for node in nodes:
node.send(itempass) for node in nodes:
node.send(itempassaswell) node.send(itempass)
teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes)
events = [i for i in allevents events = [i for i in allevents
if isinstance(i, report.ReceivedItemOutcome)] if isinstance(i, report.ReceivedItemOutcome)]
passed = [i for i in events passed = [i for i in events
if i.outcome.passed] if i.outcome.passed]
skipped = [i for i in events skipped = [i for i in events
if i.outcome.skipped] if i.outcome.skipped]
assert len(passed) == 2 * len(nodes) assert len(passed) == 1 * len(nodes)
assert len(skipped) == 0 assert len(skipped) == 0
assert len(events) == len(passed) assert len(events) == len(passed)
finally:
defaultconftestnames.remove("custom")
def test_nice_level(self): def test_nice_level(self):
""" Tests if nice level behaviour is ok """ Tests if nice level behaviour is ok
@ -258,14 +265,16 @@ class TestRSessionRemote:
parse_directories(hosts) parse_directories(hosts)
tmpdir = py.test.ensuretemp("nice") tmpdir = py.test.ensuretemp("nice")
tmpdir.ensure("__init__.py") tmpdir.ensure("__init__.py")
tmpdir.ensure("conftest.py").write("""disthosts = ['localhost']""") tmpdir.ensure("conftest.py").write(py.code.Source("""
disthosts = ['localhost']
dist_nicelevel = 10
"""))
tmpdir.ensure("test_one.py").write("""def test_nice(): tmpdir.ensure("test_one.py").write("""def test_nice():
import os import os
assert os.nice(0) == 10 assert os.nice(0) == 10
""") """)
config = py.test.config._reparse([tmpdir]) config = py.test.config._reparse([tmpdir])
config.option.nice_level = 10
rsession = RSession(config) rsession = RSession(config)
allevents = [] allevents = []
rsession.main(reporter=allevents.append) rsession.main(reporter=allevents.append)
@ -298,7 +307,10 @@ class TestInithosts(object):
hostnames = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home'] hostnames = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home']
hosts = [HostInfo(i) for i in hostnames] hosts = [HostInfo(i) for i in hostnames]
parse_directories(hosts) parse_directories(hosts)
init_hosts(testevents.append, hosts, pkgdir, do_sync=False) config = py.test.config._reparse([])
opts = HostOptions(do_sync=False, create_gateways=False)
hm = HostManager(hosts, config, pkgdir, opts)
nodes = hm.init_hosts(testevents.append)
events = [i for i in testevents if isinstance(i, report.HostRSyncing)] events = [i for i in testevents if isinstance(i, report.HostRSyncing)]
assert len(events) == 4 assert len(events) == 4
assert events[0].host.hostname == 'h1' assert events[0].host.hostname == 'h1'

View File

@ -1,324 +0,0 @@
""" Tests various aspects of rsession, like ssh hosts setup/teardown
"""
import py
from py.__.test.rsession import report
from py.__.test.rsession.rsession import RSession, parse_directories,\
parse_directories
from py.__.test.rsession.hostmanage import HostOptions, HostManager,\
HostInfo
from py.__.test.rsession.testing.test_slave import funcfail_spec,\
funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \
funcoptioncustom_spec
def setup_module(mod):
mod.pkgdir = py.path.local(py.__file__).dirpath()
def test_setup_non_existing_hosts():
setup_events = []
hosts = [HostInfo("alskdjalsdkjasldkajlsd")]
hm = HostManager(hosts, None, pkgdir)
cmd = "hm.init_hosts(setup_events.append)"
py.test.raises((py.process.cmdexec.Error, IOError, EOFError), cmd)
#assert setup_events
def test_getpkdir():
one = pkgdir.join("initpkg.py")
two = pkgdir.join("path", "__init__.py")
p1 = RSession.getpkgdir(one)
p2 = RSession.getpkgdir(two)
assert p1 == p2
assert p1 == pkgdir
def test_getpkdir_no_inits():
tmp = py.test.ensuretemp("getpkdir1")
fn = tmp.ensure("hello.py")
assert RSession.getpkgdir(fn) == fn
def test_make_colitems():
one = pkgdir.join("initpkg.py")
two = pkgdir.join("path", "__init__.py")
cols = RSession.make_colitems([one, two], baseon=pkgdir)
assert len(cols) == 2
col_one, col_two = cols
assert col_one.listnames() == ["py", "initpkg.py"]
assert col_two.listnames() == ["py", "path", "__init__.py"]
cols = RSession.make_colitems([one, two], baseon=pkgdir.dirpath())
assert len(cols) == 2
col_one, col_two = cols
assert col_one.listnames() == [pkgdir.dirpath().basename,
"py", "initpkg.py"]
assert col_two.listnames() == [pkgdir.dirpath().basename,
"py", "path", "__init__.py"]
def test_example_tryiter():
events = []
tmpdir = py.test.ensuretemp("tryitertest")
tmpdir.ensure("a", "__init__.py")
tmpdir.ensure("conftest.py").write(py.code.Source("""
import py
py.test.skip("Reason")
"""))
tmpdir.ensure("a", "test_empty.py").write(py.code.Source("""
def test_empty():
pass
"""))
rootcol = py.test.collect.Directory(tmpdir)
data = list(rootcol.tryiter(reporterror=events.append))
assert len(events) == 2
assert str(events[1][0].value) == "Reason"
class TestRSessionRemote:
def test_example_distribution_minus_x(self):
tmpdir = py.test.ensuretemp("example_distribution_minus_x")
tmpdir.ensure("sub", "conftest.py").write(py.code.Source("""
disthosts = [%r]
""" % ('localhost',)))
tmpdir.ensure("sub", "__init__.py")
tmpdir.ensure("sub", "test_one.py").write(py.code.Source("""
def test_1():
pass
def test_x():
import py
py.test.skip("aaa")
def test_2():
assert 0
def test_3():
raise ValueError(23)
def test_4(someargs):
pass
"""))
args = [str(tmpdir.join("sub")), "-x"]
config = py.test.config._reparse(args)
rsession = RSession(config)
allevents = []
rsession.main(reporter=allevents.append)
testevents = [x for x in allevents
if isinstance(x, report.ReceivedItemOutcome)]
assert len(testevents) == 3
assert rsession.checkfun()
def test_example_distribution(self):
subdir = "sub_example_dist"
tmpdir = py.test.ensuretemp("example_distribution")
tmpdir.ensure(subdir, "conftest.py").write(py.code.Source("""
disthosts = [%r]
distrsync_roots = ["%s", "py"]
""" % ('localhost', subdir)))
tmpdir.ensure(subdir, "__init__.py")
tmpdir.ensure(subdir, "test_one.py").write(py.code.Source("""
def test_1():
pass
def test_2():
assert 0
def test_3():
raise ValueError(23)
def test_4(someargs):
pass
def test_5():
assert __file__ != '%s'
def test_6():
import py
assert py.__file__ != '%s'
""" % (str(tmpdir.join(subdir)), py.__file__)))
tmpdir.join("py").mksymlinkto(py.path.local(py.__file__).dirpath())
args = [str(tmpdir.join(subdir))]
config = py.test.config._reparse(args)
rsession = RSession(config, optimise_localhost=False)
allevents = []
rsession.main(reporter=allevents.append)
testevents = [x for x in allevents
if isinstance(x, report.ReceivedItemOutcome)]
assert len(testevents)
passevents = [i for i in testevents if i.outcome.passed]
failevents = [i for i in testevents if i.outcome.excinfo]
skippedevents = [i for i in testevents if i.outcome.skipped]
assert len(testevents) == 6
assert len(passevents) == 3
assert len(failevents) == 3
tb = failevents[0].outcome.excinfo.traceback
assert str(tb[0].path).find("test_one") != -1
assert tb[0].source.find("test_2") != -1
assert failevents[0].outcome.excinfo.typename == 'AssertionError'
tb = failevents[1].outcome.excinfo.traceback
assert str(tb[0].path).find("test_one") != -1
assert tb[0].source.find("test_3") != -1
assert failevents[1].outcome.excinfo.typename == 'ValueError'
assert failevents[1].outcome.excinfo.value == '23'
tb = failevents[2].outcome.excinfo.traceback
assert failevents[2].outcome.excinfo.typename == 'TypeError'
assert str(tb[0].path).find("executor") != -1
assert tb[0].source.find("execute") != -1
def test_setup_teardown_ssh(self):
hosts = [HostInfo('localhost')]
parse_directories(hosts)
setup_events = []
teardown_events = []
config = py.test.config._reparse([])
opts = HostOptions(optimise_localhost=False, rsync_roots=['py'])
hm = HostManager(hosts, config, pkgdir, opts)
nodes = hm.init_hosts(setup_events.append)
hm.teardown_hosts(teardown_events.append,
[node.channel for node in nodes], nodes)
count_rsyn_calls = [i for i in setup_events
if isinstance(i, report.HostRSyncing)]
assert len(count_rsyn_calls) == len([i for i in hosts])
count_ready_calls = [i for i in setup_events
if isinstance(i, report.HostReady)]
assert len(count_ready_calls) == len([i for i in hosts])
# same for teardown events
teardown_wait_starts = [i for i in teardown_events
if isinstance(i, report.CallStart)]
teardown_wait_ends = [i for i in teardown_events
if isinstance(i, report.CallFinish)]
assert len(teardown_wait_starts) == len(hosts)
assert len(teardown_wait_ends) == len(hosts)
def test_setup_teardown_run_ssh(self):
hosts = [HostInfo('localhost')]
parse_directories(hosts)
allevents = []
config = py.test.config._reparse([])
opts = HostOptions(optimise_localhost=False, rsync_roots=['py'])
hm = HostManager(hosts, config, pkgdir, opts)
nodes = hm.init_hosts(allevents.append)
from py.__.test.rsession.testing.test_executor \
import ItemTestPassing, ItemTestFailing, ItemTestSkipping
rootcol = py.test.collect.Directory(pkgdir.dirpath())
itempass = rootcol.getitembynames(funcpass_spec)
itemfail = rootcol.getitembynames(funcfail_spec)
itemskip = rootcol.getitembynames(funcskip_spec)
itemprint = rootcol.getitembynames(funcprint_spec)
# actually run some tests
for node in nodes:
node.send(itempass)
node.send(itemfail)
node.send(itemskip)
node.send(itemprint)
hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes)
events = [i for i in allevents
if isinstance(i, report.ReceivedItemOutcome)]
passed = [i for i in events
if i.outcome.passed]
skipped = [i for i in events
if i.outcome.skipped]
assert len(passed) == 2 * len(nodes)
assert len(skipped) == len(nodes)
assert len(events) == 4 * len(nodes)
# one of passed for each node has non-empty stdout
passed_stdout = [i for i in passed if i.outcome.stdout.find('samfing') != -1]
assert len(passed_stdout) == len(nodes), passed
def test_config_pass(self):
""" Tests options object passing master -> server
"""
allevents = []
hosts = [HostInfo('localhost')]
parse_directories(hosts)
config = py.test.config._reparse([])
config._overwrite('custom', 'custom')
# we need to overwrite default list to serialize
from py.__.test.rsession.master import defaultconftestnames
defaultconftestnames.append("custom")
try:
opts = HostOptions(optimise_localhost=False, rsync_roots=['py'])
hm = HostManager(hosts, config, pkgdir, opts)
nodes = hm.init_hosts(allevents.append)
rootcol = py.test.collect.Directory(pkgdir.dirpath())
itempass = rootcol.getitembynames(funcoptioncustom_spec)
for node in nodes:
node.send(itempass)
hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes)
events = [i for i in allevents
if isinstance(i, report.ReceivedItemOutcome)]
passed = [i for i in events
if i.outcome.passed]
skipped = [i for i in events
if i.outcome.skipped]
assert len(passed) == 1 * len(nodes)
assert len(skipped) == 0
assert len(events) == len(passed)
finally:
defaultconftestnames.remove("custom")
def test_nice_level(self):
""" Tests if nice level behaviour is ok
"""
allevents = []
hosts = [HostInfo('localhost')]
parse_directories(hosts)
tmpdir = py.test.ensuretemp("nice")
tmpdir.ensure("__init__.py")
tmpdir.ensure("conftest.py").write(py.code.Source("""
disthosts = ['localhost']
dist_nicelevel = 10
"""))
tmpdir.ensure("test_one.py").write("""def test_nice():
import os
assert os.nice(0) == 10
""")
config = py.test.config._reparse([tmpdir])
rsession = RSession(config)
allevents = []
rsession.main(reporter=allevents.append)
testevents = [x for x in allevents
if isinstance(x, report.ReceivedItemOutcome)]
passevents = [x for x in testevents if x.outcome.passed]
assert len(passevents) == 1
class XxxTestDirectories(object):
# need complete rewrite, and unsure if it makes sense at all
def test_simple_parse(self):
sshhosts = [HostInfo(i) for i in ['h1', 'h2', 'h3']]
parse_directories(sshhosts)
def test_sophisticated_parse(self):
sshhosts = ['a@h1:/tmp', 'h2:tmp', 'h3']
dirs = parse_directories(sshhosts)
assert py.builtin.sorted(
dirs.values()) == ['/tmp', 'pytestcache', 'tmp']
def test_parse_multiple_hosts(self):
hosts = ['h1', 'h1', 'h1:/tmp']
dirs = parse_directories(hosts)
assert dirs == {(0, 'h1'): 'pytestcache', (1, 'h1'): 'pytestcache',
(2, 'h1'):'/tmp'}
class TestInithosts(object):
def test_inithosts(self):
testevents = []
hostnames = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home']
hosts = [HostInfo(i) for i in hostnames]
parse_directories(hosts)
config = py.test.config._reparse([])
opts = HostOptions(do_sync=False, create_gateways=False)
hm = HostManager(hosts, config, pkgdir, opts)
nodes = hm.init_hosts(testevents.append)
events = [i for i in testevents if isinstance(i, report.HostRSyncing)]
assert len(events) == 4
assert events[0].host.hostname == 'h1'
assert events[0].host.relpath == '/tmp-h1'
assert events[1].host.hostname == 'h1'
assert events[1].host.relpath == '/other-h1'
assert events[2].host.hostname == 'h2'
assert events[2].host.relpath == 'pytestcache-h2'
assert events[3].host.hostname == 'h2'
assert events[3].host.relpath == 'home-h2'

View File

@ -1,6 +1,6 @@
""" Testing the slave side node code (in a local way). """ """ Testing the slave side node code (in a local way). """
from py.__.test.rsession.slave import SlaveNode, slave_main from py.__.test.rsession.slave import SlaveNode, slave_main, setup, PidInfo
from py.__.test.rsession.outcome import ReprOutcome from py.__.test.rsession.outcome import ReprOutcome
import py, sys import py, sys
@ -11,10 +11,7 @@ if sys.platform == 'win32':
py.test.skip("rsession is unsupported on Windows.") py.test.skip("rsession is unsupported on Windows.")
def setup_module(module): def setup_module(module):
from py.__.test.rsession.rsession import session_options
module.rootdir = py.path.local(py.__file__).dirpath().dirpath() module.rootdir = py.path.local(py.__file__).dirpath().dirpath()
config = py.test.config._reparse([])
session_options.bind_config(config)
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# inlined testing functions used below # inlined testing functions used below
@ -34,13 +31,8 @@ def funcprintfail():
print "samfing elz" print "samfing elz"
asddsa asddsa
def funcoption():
from py.__.test.rsession.rsession import remote_options
assert remote_options.we_are_remote
def funcoptioncustom(): def funcoptioncustom():
from py.__.test.rsession.rsession import remote_options assert py.test.config.getvalue("custom")
assert remote_options.custom == "custom"
def funchang(): def funchang():
import time import time
@ -52,7 +44,6 @@ funcfail_spec = (BASE + "funcfail").split("/")
funcskip_spec = (BASE + "funcskip").split("/") funcskip_spec = (BASE + "funcskip").split("/")
funcprint_spec = (BASE + "funcprint").split("/") funcprint_spec = (BASE + "funcprint").split("/")
funcprintfail_spec = (BASE + "funcprintfail").split("/") funcprintfail_spec = (BASE + "funcprintfail").split("/")
funcoption_spec = (BASE + "funcoption").split("/")
funcoptioncustom_spec = (BASE + "funcoptioncustom").split("/") funcoptioncustom_spec = (BASE + "funcoptioncustom").split("/")
funchang_spec = (BASE + "funchang").split("/") funchang_spec = (BASE + "funchang").split("/")
mod_spec = BASE[:-1].split("/") mod_spec = BASE[:-1].split("/")
@ -63,7 +54,9 @@ from py.__.test.rsession.executor import RunExecutor
def gettestnode(): def gettestnode():
rootcol = py.test.collect.Directory(rootdir) rootcol = py.test.collect.Directory(rootdir)
node = SlaveNode(rootcol, executor=RunExecutor) config = py.test.config._reparse([rootdir])
pidinfo = PidInfo()
node = SlaveNode(rootcol, config, pidinfo, executor=RunExecutor)
return node return node
def test_slave_run_passing(): def test_slave_run_passing():
@ -116,7 +109,9 @@ def test_slave_main_simple():
funcpass_spec, funcpass_spec,
funcfail_spec funcfail_spec
] ]
slave_main(q.pop, res.append, str(rootdir)) config = py.test.config._reparse([])
pidinfo = PidInfo()
slave_main(q.pop, res.append, str(rootdir), config, pidinfo)
assert len(res) == 2 assert len(res) == 2
res_repr = [ReprOutcome(r) for r in res] res_repr = [ReprOutcome(r) for r in res]
assert not res_repr[0].passed and res_repr[1].passed assert not res_repr[0].passed and res_repr[1].passed
@ -126,8 +121,8 @@ def test_slave_run_different_stuff():
node.run("py doc log.txt".split()) node.run("py doc log.txt".split())
def test_slave_setup_fails_on_import_error(): def test_slave_setup_fails_on_import_error():
from py.__.test.rsession.slave import setup
tmp = py.test.ensuretemp("slavesetup") tmp = py.test.ensuretemp("slavesetup")
config = py.test.config._reparse([tmp])
class C: class C:
def __init__(self): def __init__(self):
self.count = 0 self.count = 0
@ -136,12 +131,15 @@ def test_slave_setup_fails_on_import_error():
if self.count == 0: if self.count == 0:
retval = str(tmp) retval = str(tmp)
elif self.count == 1: elif self.count == 1:
from py.__.test.rsession.rsession import remote_options retval = config.make_repr(conftestnames=['dist_nicelevel'])
retval = remote_options.d
else: else:
raise NotImplementedError("more data") raise NotImplementedError("more data")
self.count += 1 self.count += 1
return retval return retval
def close(self):
pass
try: try:
exec py.code.Source(setup, "setup()").compile() in { exec py.code.Source(setup, "setup()").compile() in {
'channel': C()} 'channel': C()}
@ -153,16 +151,14 @@ def test_slave_setup_fails_on_import_error():
def test_slave_setup_exit(): def test_slave_setup_exit():
tmp = py.test.ensuretemp("slaveexit") tmp = py.test.ensuretemp("slaveexit")
tmp.ensure("__init__.py") tmp.ensure("__init__.py")
from py.__.test.rsession.slave import setup q = py.std.Queue.Queue()
from Queue import Queue config = py.test.config._reparse([tmp])
q = Queue()
class C: class C:
res = [] res = []
def __init__(self): def __init__(self):
from py.__.test.rsession.rsession import remote_options
self.q = [str(tmp), self.q = [str(tmp),
remote_options.d, config.make_repr(conftestnames=['dist_nicelevel']),
funchang_spec, funchang_spec,
42, 42,
funcpass_spec] funcpass_spec]
@ -178,6 +174,9 @@ def test_slave_setup_exit():
callback(self.q.pop()) callback(self.q.pop())
f() f()
#thread.start_new_thread(f, ()) #thread.start_new_thread(f, ())
def close(self):
pass
send = res.append send = res.append
try: try:
@ -188,8 +187,8 @@ def test_slave_setup_exit():
py.test.fail("Did not exit") py.test.fail("Did not exit")
def test_slave_setup_fails_on_missing_pkg(): def test_slave_setup_fails_on_missing_pkg():
from py.__.test.rsession.slave import setup
tmp = py.test.ensuretemp("slavesetup2") tmp = py.test.ensuretemp("slavesetup2")
config = py.test.config._reparse([tmp])
x = tmp.ensure("sometestpackage", "__init__.py") x = tmp.ensure("sometestpackage", "__init__.py")
class C: class C:
def __init__(self): def __init__(self):
@ -199,8 +198,7 @@ def test_slave_setup_fails_on_missing_pkg():
if self.count == 0: if self.count == 0:
retval = str(x.dirpath()) retval = str(x.dirpath())
elif self.count == 1: elif self.count == 1:
from py.__.test.rsession.rsession import remote_options retval = config.make_repr(conftestnames=['dist_nicelevel'])
retval = remote_options.d
else: else:
raise NotImplementedError("more data") raise NotImplementedError("more data")
self.count += 1 self.count += 1
@ -223,3 +221,19 @@ def test_slave_setup_fails_on_missing_pkg():
else: else:
py.test.fail("missing exception") py.test.fail("missing exception")
def test_pidinfo():
if not hasattr(os, 'fork') or not hasattr(os, 'waitpid'):
py.test.skip("Platform does not support fork")
pidinfo = PidInfo()
pid = os.fork()
if pid:
pidinfo.set_pid(pid)
pidinfo.waitandclear(pid, 0)
else:
import time, sys
time.sleep(.3)
os._exit(0)
# check if this really exits
py.test.raises(OSError, "os.waitpid(pid, 0)")

View File

@ -14,9 +14,7 @@ except ImportError:
def setup_module(mod): def setup_module(mod):
config = py.test.config._reparse([]) config = py.test.config._reparse([])
from py.__.test.rsession.rsession import session_options config._overwrite('_dist_import_pypy', True)
session_options.bind_config(config)
session_options.import_pypy = True
from py.__.test.rsession.web import TestHandler as _TestHandler from py.__.test.rsession.web import TestHandler as _TestHandler
from py.__.test.rsession.web import MultiQueue from py.__.test.rsession.web import MultiQueue
mod._TestHandler = _TestHandler mod._TestHandler = _TestHandler

View File

@ -23,9 +23,7 @@ def setup_module(mod):
dom.window = dom.Window(html) dom.window = dom.Window(html)
dom.document = dom.window.document dom.document = dom.window.document
config = py.test.config._reparse([]) config = py.test.config._reparse([])
from py.__.test.rsession.rsession import session_options config._overwrite('_dist_import_pypy', True)
session_options.bind_config(config)
session_options.import_pypy = True
from py.__.test.rsession import webjs from py.__.test.rsession import webjs
from py.__.test.rsession.web import exported_methods from py.__.test.rsession.web import exported_methods
mod.webjs = webjs mod.webjs = webjs

View File

@ -1,138 +0,0 @@
import py
try:
import pypy
from pypy.translator.js.modules import dom
from pypy.translator.js.tester import schedule_callbacks
from py.__.test.rsession.rsession import session_options
dom.Window # check whether dom was properly imported or is just a
# leftover in sys.modules
except (ImportError, AttributeError):
py.test.skip('PyPy not found')
from py.__.test.rsession import webjs
from py.__.test.rsession.web import exported_methods
here = py.magic.autopath().dirpath()
def setup_module(mod):
# load HTML into window object
html = here.join('../webdata/index.html').read()
mod.html = html
from pypy.translator.js.modules import dom
mod.dom = dom
dom.window = dom.Window(html)
dom.document = dom.window.document
config = py.test.config._reparse([])
config._overwrite('_dist_import_pypy', True)
from py.__.test.rsession import webjs
from py.__.test.rsession.web import exported_methods
mod.webjs = webjs
mod.exported_methods = exported_methods
def setup_function(f):
dom.window = dom.Window(html)
dom.document = dom.window.document
def test_html_loaded():
body = dom.window.document.getElementsByTagName('body')[0]
assert len(body.childNodes) > 0
assert str(body.childNodes[1].nodeName) == 'A'
def test_set_msgbox():
msgbox = dom.window.document.getElementById('messagebox')
assert len(msgbox.childNodes) == 0
webjs.set_msgbox('foo', 'bar')
assert len(msgbox.childNodes) == 1
assert msgbox.childNodes[0].nodeName == 'PRE'
assert msgbox.childNodes[0].childNodes[0].nodeValue == 'foo\nbar'
def test_show_info():
info = dom.window.document.getElementById('info')
info.style.visibility = 'hidden'
info.innerHTML = ''
webjs.show_info('foobar')
content = info.innerHTML
assert content == 'foobar'
bgcolor = info.style.backgroundColor
assert bgcolor == 'beige'
def test_hide_info():
info = dom.window.document.getElementById('info')
info.style.visibility = 'visible'
webjs.hide_info()
assert info.style.visibility == 'hidden'
def test_process():
main_t = dom.window.document.getElementById('main_table')
assert len(main_t.getElementsByTagName('tr')) == 0
assert not webjs.process({})
msg = {'type': 'ItemStart',
'itemtype': 'Module',
'itemname': 'foo.py',
'fullitemname': 'modules/foo.py',
'length': 10,
}
assert webjs.process(msg)
trs = main_t.getElementsByTagName('tr')
assert len(trs) == 1
tr = trs[0]
assert len(tr.childNodes) == 2
assert tr.childNodes[0].nodeName == 'TD'
assert tr.childNodes[0].innerHTML == 'foo.py[0/10]'
assert tr.childNodes[1].nodeName == 'TD'
assert tr.childNodes[1].childNodes[0].nodeName == 'TABLE'
assert len(tr.childNodes[1].getElementsByTagName('tr')) == 0
def test_process_two():
main_t = dom.window.document.getElementById('main_table')
msg = {'type': 'ItemStart',
'itemtype': 'Module',
'itemname': 'foo.py',
'fullitemname': 'modules/foo.py',
'length': 10,
}
webjs.process(msg)
msg = {'type': 'ReceivedItemOutcome',
'fullmodulename': 'modules/foo.py',
'passed' : 'True',
'fullitemname' : 'modules/foo.py/test_item',
'hostkey': None,
}
webjs.process(msg)
trs = main_t.getElementsByTagName('tr')
tds = trs[0].getElementsByTagName('td')
# two cells in the row, one in the table inside one of the cells
assert len(tds) == 3
html = tds[0].innerHTML
assert html == 'foo.py[1/10]'
assert tds[2].innerHTML == '.'
def test_signal():
main_t = dom.window.document.getElementById('main_table')
msg = {'type': 'ItemStart',
'itemtype': 'Module',
'itemname': 'foo.py',
'fullitemname': 'modules/foo.py',
'length': 10,
}
webjs.process(msg)
msg = {'type': 'ReceivedItemOutcome',
'fullmodulename': 'modules/foo.py',
'passed' : 'False',
'fullitemname' : 'modules/foo.py/test_item',
'hostkey': None,
'signal': '10',
'skipped': 'False',
}
exported_methods.fail_reasons['modules/foo.py/test_item'] = 'Received signal 10'
exported_methods.stdout['modules/foo.py/test_item'] = ''
exported_methods.stderr['modules/foo.py/test_item'] = ''
webjs.process(msg)
schedule_callbacks(exported_methods)
# ouch
assert dom.document.getElementById('modules/foo.py').childNodes[0].\
childNodes[0].childNodes[0].childNodes[0].nodeValue == 'F'
# XXX: Write down test for full run

View File

@ -14,18 +14,18 @@ import sys
import socket import socket
import py import py
from py.__.test.rsession.rsession import RSession, session_options from py.__.test.rsession.rsession import RSession
from py.__.test.rsession import report from py.__.test.rsession import report
from py.__.test import collect from py.__.test import collect
from py.__.test.rsession.webdata import json from py.__.test.rsession.webdata import json
DATADIR = py.path.local(__file__).dirpath("webdata") DATADIR = py.path.local(__file__).dirpath("webdata")
FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info", FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info",
"show_host", "hide_host", "hide_messagebox"] "show_host", "hide_host", "hide_messagebox", "opt_scroll"]
try: try:
try: try:
if not session_options.import_pypy: if not py.test.config.getvalue('_dist_import_pypy'):
raise ImportError raise ImportError
except AttributeError: except AttributeError:
pass pass
@ -416,10 +416,10 @@ def start_server(server_address = ('', 8000), handler=TestHandler, start_new=Tru
if start_new: if start_new:
thread.start_new_thread(httpd.serve_forever, ()) thread.start_new_thread(httpd.serve_forever, ())
print "Server started, listening on %s" % (server_address,) print "Server started, listening on port %d" % (httpd.server_port,)
return httpd return httpd
else: else:
print "Server started, listening on %s" % (server_address,) print "Server started, listening on port %d" % (httpd.server_port,)
httpd.serve_forever() httpd.serve_forever()
def kill_server(): def kill_server():

View File

@ -1,430 +0,0 @@
""" web server for py.test
"""
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
import thread, threading
import re
import time
import random
import Queue
import os
import sys
import socket
import py
from py.__.test.rsession.rsession import RSession
from py.__.test.rsession import report
from py.__.test import collect
from py.__.test.rsession.webdata import json
DATADIR = py.path.local(__file__).dirpath("webdata")
FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info",
"show_host", "hide_host", "hide_messagebox", "opt_scroll"]
try:
try:
if not py.test.config.getvalue('_dist_import_pypy'):
raise ImportError
except AttributeError:
pass
from pypy.rpython.ootypesystem.bltregistry import MethodDesc, BasicExternal,\
described
from pypy.translator.js.main import rpython2javascript
from pypy.translator.js import commproxy
from pypy.rpython.extfunc import _callable
commproxy.USE_MOCHIKIT = False
IMPORTED_PYPY = True
except (ImportError, NameError):
class BasicExternal(object):
pass
def described(*args, **kwargs):
def decorator(func):
return func
return decorator
def _callable(*args, **kwargs):
pass
IMPORTED_PYPY = False
def add_item(event):
""" A little helper
"""
item = event.item
itemtype = item.__class__.__name__
itemname = item.name
fullitemname = "/".join(item.listnames())
d = {'fullitemname': fullitemname, 'itemtype': itemtype,
'itemname': itemname}
#if itemtype == 'Module':
try:
d['length'] = str(len(list(event.item.tryiter())))
except:
d['length'] = "?"
return d
class MultiQueue(object):
""" a tailor-made queue (internally using Queue) for py.test.rsession.web
API-wise the main difference is that the get() method gets a sessid
argument, which is used to determine what data to feed to the client
when a data queue for a sessid doesn't yet exist, it is created, and
filled with data that has already been fed to the other clients
"""
def __init__(self):
self._cache = []
self._session_queues = {}
self._lock = py.std.thread.allocate_lock()
def put(self, item):
self._lock.acquire()
try:
self._cache.append(item)
for key, q in self._session_queues.items():
q.put(item)
finally:
self._lock.release()
def _del(self, sessid):
self._lock.acquire()
try:
del self._session_queues[sessid]
finally:
self._lock.release()
def get(self, sessid):
self._lock.acquire()
try:
if not sessid in self._session_queues:
self._create_session_queue(sessid)
finally:
self._lock.release()
return self._session_queues[sessid].get(sessid)
def empty(self):
self._lock.acquire()
try:
if not self._session_queues:
return not len(self._cache)
for q in self._session_queues.values():
if not q.empty():
return False
finally:
self._lock.release()
return True
def empty_queue(self, sessid):
return self._session_queues[sessid].empty()
def _create_session_queue(self, sessid):
self._session_queues[sessid] = q = Queue.Queue()
for item in self._cache:
q.put(item)
class ExportedMethods(BasicExternal):
_render_xmlhttp = True
def __init__(self):
self.pending_events = MultiQueue()
self.start_event = threading.Event()
self.end_event = threading.Event()
self.skip_reasons = {}
self.fail_reasons = {}
self.stdout = {}
self.stderr = {}
self.all = 0
def findmodule(self, item):
# find the most outwards parent which is module
current = item
while current:
if isinstance(current, collect.Module):
break
current = current.parent
if current is not None:
return str(current.name), str("/".join(current.listnames()))
else:
return str(item.parent.name), str("/".join(item.parent.listnames()))
def show_hosts(self):
self.start_event.wait()
to_send = {}
for host in self.hosts:
to_send[host.hostid] = host.hostname
return to_send
show_hosts = described(retval={str:str}, args=[('callback',
_callable([{str:str}]))])(show_hosts)
def show_skip(self, item_name="aa"):
return {'item_name': item_name,
'reason': self.skip_reasons[item_name]}
show_skip = described(retval={str:str}, args=[('item_name',str),('callback',
_callable([{str:str}]))])(show_skip)
def show_fail(self, item_name="aa"):
return {'item_name':item_name,
'traceback':str(self.fail_reasons[item_name]),
'stdout':self.stdout[item_name],
'stderr':self.stderr[item_name]}
show_fail = described(retval={str:str}, args=[('item_name',str),('callback',
_callable([{str:str}]))])(show_fail)
_sessids = None
_sesslock = py.std.thread.allocate_lock()
def show_sessid(self):
if not self._sessids:
self._sessids = []
self._sesslock.acquire()
try:
while 1:
chars = list(py.std.string.lowercase)
py.std.random.shuffle(chars)
sessid = ''.join(chars[:8])
if sessid not in self._sessids:
self._sessids.append(sessid)
break
finally:
self._sesslock.release()
return sessid
show_sessid = described(retval=str, args=[('callback',
_callable([str]))])(show_sessid)
def failed(self, **kwargs):
if not 'sessid' in kwargs:
return
sessid = kwargs['sessid']
to_del = -1
for num, i in enumerate(self._sessids):
if i == sessid:
to_del = num
if to_del != -1:
del self._sessids[to_del]
self.pending_events._del(kwargs['sessid'])
def show_all_statuses(self, sessid=-1):
retlist = [self.show_status_change(sessid)]
while not self.pending_events.empty_queue(sessid):
retlist.append(self.show_status_change(sessid))
retval = retlist
return retval
show_all_statuses = described(retval=[{str:str}],args=
[('sessid',str), ('callback',_callable([[{str:str}]]))])(show_all_statuses)
def show_status_change(self, sessid):
event = self.pending_events.get(sessid)
if event is None:
self.end_event.set()
return {}
# some dispatcher here
if isinstance(event, report.ReceivedItemOutcome):
args = {}
outcome = event.outcome
for key, val in outcome.__dict__.iteritems():
args[key] = str(val)
args.update(add_item(event))
mod_name, mod_fullname = self.findmodule(event.item)
args['modulename'] = str(mod_name)
args['fullmodulename'] = str(mod_fullname)
fullitemname = args['fullitemname']
if outcome.skipped:
self.skip_reasons[fullitemname] = outcome.skipped
elif outcome.excinfo:
self.fail_reasons[fullitemname] = self.repr_failure_tblong(
event.item, outcome.excinfo, outcome.excinfo.traceback)
self.stdout[fullitemname] = outcome.stdout
self.stderr[fullitemname] = outcome.stderr
elif outcome.signal:
self.fail_reasons[fullitemname] = "Received signal %d" % outcome.signal
self.stdout[fullitemname] = outcome.stdout
self.stderr[fullitemname] = outcome.stderr
if event.channel:
args['hostkey'] = event.channel.gateway.host.hostid
else:
args['hostkey'] = ''
elif isinstance(event, report.ItemStart):
args = add_item(event)
elif isinstance(event, report.TestFinished):
args = {}
args['run'] = str(self.all)
args['fails'] = str(len(self.fail_reasons))
args['skips'] = str(len(self.skip_reasons))
elif isinstance(event, report.SendItem):
args = add_item(event)
args['hostkey'] = event.channel.gateway.host.hostid
elif isinstance(event, report.HostReady):
self.ready_hosts[event.host] = True
args = {'hostname' : event.host.hostname, 'hostkey' : event.host.hostid}
elif isinstance(event, report.FailedTryiter):
args = add_item(event)
elif isinstance(event, report.SkippedTryiter):
args = add_item(event)
args['reason'] = str(event.excinfo.value)
else:
args = {}
args['event'] = str(event)
args['type'] = event.__class__.__name__
return args
def repr_failure_tblong(self, item, excinfo, traceback):
lines = []
for index, entry in py.builtin.enumerate(traceback):
lines.append('----------')
lines.append("%s: %s" % (entry.path, entry.lineno))
lines += self.repr_source(entry.relline, entry.source)
lines.append("%s: %s" % (excinfo.typename, excinfo.value))
return "\n".join(lines)
def repr_source(self, relline, source):
lines = []
for num, line in enumerate(source.split("\n")):
if num == relline:
lines.append(">>>>" + line)
else:
lines.append(" " + line)
return lines
def report_ReceivedItemOutcome(self, event):
self.all += 1
self.pending_events.put(event)
def report_ItemStart(self, event):
if isinstance(event.item, py.test.collect.Module):
self.pending_events.put(event)
def report_unknown(self, event):
# XXX: right now, we just pass it for showing
self.pending_events.put(event)
def report_TestStarted(self, event):
# XXX: It overrides out self.hosts
self.hosts = {}
self.ready_hosts = {}
for host in event.hosts:
self.hosts[host] = host
self.ready_hosts[host] = False
self.start_event.set()
self.pending_events.put(event)
def report(self, what):
repfun = getattr(self, "report_" + what.__class__.__name__,
self.report_unknown)
try:
repfun(what)
except (KeyboardInterrupt, SystemExit):
raise
except:
print "Internal reporting problem"
excinfo = py.code.ExceptionInfo()
for i in excinfo.traceback:
print str(i)[2:-1]
print excinfo
## try:
## self.wait_flag.acquire()
## self.pending_events.insert(0, event)
## self.wait_flag.notify()
## finally:
## self.wait_flag.release()
exported_methods = ExportedMethods()
class TestHandler(BaseHTTPRequestHandler):
exported_methods = exported_methods
def do_GET(self):
path = self.path
if path.endswith("/"):
path = path[:-1]
if path.startswith("/"):
path = path[1:]
m = re.match('^(.*)\?(.*)$', path)
if m:
path = m.group(1)
getargs = m.group(2)
else:
getargs = ""
name_path = path.replace(".", "_")
method_to_call = getattr(self, "run_" + name_path, None)
if method_to_call is None:
exec_meth = getattr(self.exported_methods, name_path, None)
if exec_meth is None:
self.send_error(404, "File %s not found" % path)
else:
try:
self.serve_data('text/json',
json.write(exec_meth(**self.parse_args(getargs))))
except socket.error:
# client happily disconnected
exported_methods.failed(**self.parse_args(getargs))
else:
method_to_call()
def parse_args(self, getargs):
# parse get argument list
if getargs == "":
return {}
unquote = py.std.urllib.unquote
args = {}
arg_pairs = getargs.split("&")
for arg in arg_pairs:
key, value = arg.split("=")
args[unquote(key)] = unquote(value)
return args
def log_message(self, format, *args):
# XXX just discard it
pass
do_POST = do_GET
def run_(self):
self.run_index()
def run_index(self):
data = py.path.local(DATADIR).join("index.html").open().read()
self.serve_data("text/html", data)
def run_jssource(self):
js_name = py.path.local(__file__).dirpath("webdata").join("source.js")
web_name = py.path.local(__file__).dirpath().join("webjs.py")
if IMPORTED_PYPY and web_name.mtime() > js_name.mtime():
from py.__.test.rsession import webjs
javascript_source = rpython2javascript(webjs,
FUNCTION_LIST, use_pdb=False)
open(str(js_name), "w").write(javascript_source)
self.serve_data("text/javascript", javascript_source)
else:
js_source = open(str(js_name), "r").read()
self.serve_data("text/javascript", js_source)
def serve_data(self, content_type, data):
self.send_response(200)
self.send_header("Content-type", content_type)
self.send_header("Content-length", len(data))
self.end_headers()
self.wfile.write(data)
def start_server(server_address = ('', 8000), handler=TestHandler, start_new=True):
httpd = HTTPServer(server_address, handler)
if start_new:
thread.start_new_thread(httpd.serve_forever, ())
print "Server started, listening on port %d" % (httpd.server_port,)
return httpd
else:
print "Server started, listening on port %d" % (httpd.server_port,)
httpd.serve_forever()
def kill_server():
exported_methods.pending_events.put(None)
while not exported_methods.pending_events.empty():
time.sleep(.1)
exported_methods.end_event.wait()

View File

@ -97,6 +97,12 @@
<tr> <tr>
<td><a href="#aftermessage">End of Traceback</a></td> <td><a href="#aftermessage">End of Traceback</a></td>
</tr> </tr>
<tr>
<td class="title">Options</td>
</tr>
<tr>
<td><input type="checkbox" id="opt_scroll" onchange="javascript:opt_scroll()"/><u>S</u>croll with tests arriving</td>
</tr>
</tbody> </tbody>
</table> </table>
<table id="main_table"> <table id="main_table">

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,14 @@ class Globals(object):
glob = Globals() glob = Globals()
class Options(object):
""" Store global options
"""
def __init__(self):
self.scroll = True
opts = Options()
def comeback(msglist): def comeback(msglist):
if len(msglist) == 0: if len(msglist) == 0:
return return
@ -62,11 +70,29 @@ def hide_info():
info = dom.document.getElementById("info") info = dom.document.getElementById("info")
info.style.visibility = "hidden" info.style.visibility = "hidden"
def show_interrupt():
glob.finished = True
dom.document.title = "Py.test [interrupted]"
dom.document.getElementById("Tests").childNodes[0].nodeValue = "Tests [interrupted]"
def show_crash():
glob.finished = True
dom.document.title = "Py.test [crashed]"
dom.document.getElementById("Tests").childNodes[0].nodeValue = "Tests [crashed]"
SCROLL_LINES = 50 SCROLL_LINES = 50
def opt_scroll():
if opts.scroll:
opts.scroll = False
else:
opts.scroll = True
def scroll_down_if_needed(mbox): def scroll_down_if_needed(mbox):
if dom.window.scrollMaxY - dom.window.scrollY < SCROLL_LINES: if not opts.scroll:
mbox.parentNode.scrollIntoView() return
#if dom.window.scrollMaxY - dom.window.scrollY < SCROLL_LINES:
mbox.parentNode.scrollIntoView()
def hide_messagebox(): def hide_messagebox():
mbox = dom.document.getElementById("messagebox") mbox = dom.document.getElementById("messagebox")
@ -175,6 +201,7 @@ def process(msg):
add_received_item_outcome(msg, module_part) add_received_item_outcome(msg, module_part)
elif msg['type'] == 'TestFinished': elif msg['type'] == 'TestFinished':
text = "FINISHED %s run, %s failures, %s skipped" % (msg['run'], msg['fails'], msg['skips']) text = "FINISHED %s run, %s failures, %s skipped" % (msg['run'], msg['fails'], msg['skips'])
glob.finished = True
dom.document.title = "Py.test %s" % text dom.document.title = "Py.test %s" % text
dom.document.getElementById("Tests").childNodes[0].nodeValue = \ dom.document.getElementById("Tests").childNodes[0].nodeValue = \
"Tests [%s]" % text "Tests [%s]" % text
@ -202,6 +229,10 @@ def process(msg):
module_part.appendChild(tr) module_part.appendChild(tr)
elif msg['type'] == 'RsyncFinished': elif msg['type'] == 'RsyncFinished':
glob.rsync_done = True glob.rsync_done = True
elif msg['type'] == 'InterruptedExecution':
show_interrupt()
elif msg['type'] == 'CrashedExecution':
show_crash()
if glob.data_empty: if glob.data_empty:
mbox = dom.document.getElementById('messagebox') mbox = dom.document.getElementById('messagebox')
scroll_down_if_needed(mbox) scroll_down_if_needed(mbox)
@ -262,6 +293,8 @@ def hide_host():
glob.host = "" glob.host = ""
def update_rsync(): def update_rsync():
if glob.finished:
return
elem = dom.document.getElementById("Tests") elem = dom.document.getElementById("Tests")
if glob.rsync_done is True: if glob.rsync_done is True:
elem.childNodes[0].nodeValue = "Tests" elem.childNodes[0].nodeValue = "Tests"
@ -294,11 +327,23 @@ def host_init(host_dict):
for key in host_dict.keys(): for key in host_dict.keys():
glob.host_pending[key] = [] glob.host_pending[key] = []
def key_pressed(key):
if key.charCode == ord('s'):
scroll_box = dom.document.getElementById("opt_scroll")
if opts.scroll:
scroll_box.removeAttribute("checked")
opts.scroll = False
else:
scroll_box.setAttribute("checked", "true")
opts.scroll = True
def sessid_comeback(id): def sessid_comeback(id):
glob.sessid = id glob.sessid = id
exported_methods.show_all_statuses(id, comeback) exported_methods.show_all_statuses(id, comeback)
def main(): def main():
glob.finished = False
exported_methods.show_hosts(host_init) exported_methods.show_hosts(host_init)
exported_methods.show_sessid(sessid_comeback) exported_methods.show_sessid(sessid_comeback)
dom.document.onkeypress = key_pressed
dom.document.getElementById("opt_scroll").setAttribute("checked", "True")

View File

@ -38,12 +38,11 @@ class Session(object):
def main(self): def main(self):
""" main loop for running tests. """ """ main loop for running tests. """
colitems = self._map2colitems(self.config.remaining) colitems = self.config.getcolitems()
try: try:
self.header(colitems) self.header(colitems)
try: try:
for colitem in colitems: for colitem in colitems:
colitem.option = self.config.option
self.runtraced(colitem) self.runtraced(colitem)
except KeyboardInterrupt: except KeyboardInterrupt:
raise raise
@ -53,9 +52,6 @@ class Session(object):
self.footer(colitems) self.footer(colitems)
except Exit, ex: except Exit, ex:
pass pass
# return [(fspath as string, [names as string])]
return [(str(item.listchain()[0].fspath), item.listnames())
for item, outcome in self.getitemoutcomepairs(py.test.Item.Failed)]
def runtraced(self, colitem): def runtraced(self, colitem):
if self.shouldclose(): if self.shouldclose():
@ -91,7 +87,7 @@ class Session(object):
if self.config.option.collectonly and isinstance(colitem, py.test.Item): if self.config.option.collectonly and isinstance(colitem, py.test.Item):
return return
if isinstance(colitem, py.test.Item): if isinstance(colitem, py.test.Item):
self.skipbykeyword(colitem) colitem.skipbykeyword(self.config.option.keyword)
res = colitem.run() res = colitem.run()
if res is None: if res is None:
return py.test.Item.Passed() return py.test.Item.Passed()
@ -110,39 +106,6 @@ class Session(object):
finish() finish()
return res return res
def skipbykeyword(self, colitem):
keyword = self.config.option.keyword
if not keyword:
return
chain = colitem.listchain()
for key in filter(None, keyword.split()):
eor = key[:1] == '-'
if eor:
key = key[1:]
if not (eor ^ self._matchonekeyword(key, chain)):
py.test.skip("test not selected by keyword %r" %(keyword,))
def _matchonekeyword(self, key, chain):
for subitem in chain:
if subitem.haskeyword(key):
return True
return False
def _map2colitems(items):
# first convert all path objects into collectors
from py.__.test.collect import getfscollector
colitems = []
for item in items:
if isinstance(item, (list, tuple)):
colitems.extend(Session._map2colitems(item))
elif not isinstance(item, py.test.collect.Collector):
colitems.append(getfscollector(item))
else:
colitems.append(item)
return colitems
_map2colitems = staticmethod(_map2colitems)
class Exit(Exception): class Exit(Exception):
""" for immediate program exits without tracebacks and reporter/summary. """ """ for immediate program exits without tracebacks and reporter/summary. """
def __init__(self, msg="unknown reason", item=None): def __init__(self, msg="unknown reason", item=None):

View File

@ -10,8 +10,9 @@ class Out(object):
def __init__(self, file): def __init__(self, file):
self.file = py.io.dupfile(file) self.file = py.io.dupfile(file)
def sep(self, sepchar, title=None): def sep(self, sepchar, title=None, fullwidth=None):
fullwidth = self.fullwidth if not fullwidth:
fullwidth = self.fullwidth
# the goal is to have the line be as long as possible # the goal is to have the line be as long as possible
# under the condition that len(line) <= fullwidth # under the condition that len(line) <= fullwidth
if title is not None: if title is not None:
@ -37,15 +38,10 @@ class TerminalOut(Out):
tty = True tty = True
def __init__(self, file): def __init__(self, file):
super(TerminalOut, self).__init__(file) super(TerminalOut, self).__init__(file)
try:
import termios,fcntl,struct def sep(self, sepchar, title=None):
call = fcntl.ioctl(0,termios.TIOCGWINSZ,"\000"*8) super(TerminalOut, self).sep(sepchar, title,
height,width = struct.unpack( "hhhh", call ) [:2] terminal_helper.get_terminal_width())
self.fullwidth = width
except (SystemExit, KeyboardInterrupt), e:
raise
except:
pass
def write(self, s): def write(self, s):
self.file.write(str(s)) self.file.write(str(s))
@ -84,8 +80,10 @@ def getout(file):
# #
if file is None: if file is None:
file = py.std.sys.stdout file = py.std.sys.stdout
elif hasattr(file, 'send'): # likely a channel like thing elif hasattr(file, 'send'):
file = WriteFile(file.send) file = WriteFile(file.send)
elif callable(file):
file = WriteFile(file)
if hasattr(file, 'isatty') and file.isatty(): if hasattr(file, 'isatty') and file.isatty():
return TerminalOut(file) return TerminalOut(file)
else: else:

View File

@ -29,46 +29,6 @@ def checkpyfilechange(rootdir, statcache={}):
changed = True changed = True
return changed return changed
class FailingCollector(py.test.collect.Collector):
def __init__(self, faileditems):
self._faileditems = faileditems
def __iter__(self):
for x in self._faileditems:
yield x
def waitfinish(channel):
try:
while 1:
try:
channel.waitclose(0.1)
except (IOError, py.error.Error):
continue
else:
return channel.receive()
finally:
#print "closing down channel and gateway"
channel.gateway.exit()
def failure_slave(channel):
""" we run this on the other side. """
args, failures = channel.receive()
config = py.test.config._reparse(args)
# making this session definitely non-remote
config.option.executable = py.std.sys.executable
config.option.looponfailing = False
config.option._remote = False
config.option._fromremote = True
if failures:
cols = getfailureitems(failures)
else:
cols = args
#print "processing", cols
session = config.getsessionclass()(config)
session.shouldclose = channel.isclosed
failures = session.main()
channel.send(failures)
def getfailureitems(failures): def getfailureitems(failures):
l = [] l = []
for rootpath, names in failures: for rootpath, names in failures:
@ -91,61 +51,77 @@ def getfailureitems(failures):
l.append(current) l.append(current)
return l return l
def failure_master(executable, out, args, failures): class RemoteTerminalSession(object):
gw = py.execnet.PopenGateway(executable) def __init__(self, config, file=None):
channel = gw.remote_exec(""" self.config = config
from py.__.test.terminal.remote import failure_slave self._setexecutable()
failure_slave(channel) if file is None:
""", stdout=out, stderr=out) file = py.std.sys.stdout
channel.send((args, failures)) self._file = file
return waitfinish(channel) self.out = getout(file)
def generalize(p1, p2): def _setexecutable(self):
general = p1 name = self.config.option.executable
for x, y in zip(p1.parts(), p2.parts()): if name is None:
if x != y: executable = py.std.sys.executable
break else:
general = x executable = py.path.local.sysfind(name)
return general assert executable is not None, executable
self.executable = executable
def getrootdir(args): def main(self):
tops = [] rootdir = self.config.topdir
for arg in args: wasfailing = False
p = py.path.local(arg) failures = []
tops.append(p.pypkgpath() or p) while 1:
p = reduce(generalize, tops) if self.config.option.looponfailing and (failures or not wasfailing):
if p.check(file=1): while not checkpyfilechange(rootdir):
p = p.dirpath() py.std.time.sleep(0.4)
return p wasfailing = len(failures)
failures = self.run_remote_session(failures)
if not self.config.option.looponfailing:
break
print "#" * 60
print "# looponfailing: mode: %d failures args" % len(failures)
for root, names in failures:
name = "/".join(names) # XXX
print "Failure at: %r" % (name,)
print "# watching py files below %s" % rootdir
print "# ", "^" * len(str(rootdir))
return failures
def main(config, file, args): def run_remote_session(self, failures):
""" testing process and output happens at a remote place. """ print "* opening PopenGateway: ", self.executable
assert file gw = py.execnet.PopenGateway(self.executable)
if hasattr(file, 'write'): channel = gw.remote_exec("""
def out(data): from py.__.test.terminal.remote import slaverun_TerminalSession
file.write(data) slaverun_TerminalSession(channel)
file.flush() """, stdout=self.out, stderr=self.out)
else: print "MASTER: triggered slave terminal session ->"
out = file repr = self.config.make_repr(conftestnames=[])
failures = [] channel.send((str(self.config.topdir), repr, failures))
args = list(args) print "MASTER: send start info"
rootdir = getrootdir(config.remaining) try:
#print "rootdir", rootdir return channel.receive()
wasfailing = False except channel.RemoteError, e:
while 1: print e
if config.option.looponfailing and (failures or not wasfailing): return []
while not checkpyfilechange(rootdir):
py.std.time.sleep(0.4)
wasfailing = len(failures)
failures = failure_master(config.option.executable, out, args, failures)
if not config.option.looponfailing:
break
print "#" * 60
print "# looponfailing: mode: %d failures remaining" % len(failures)
for root, names in failures:
name = "/".join(names) # XXX
print "Failure at: %r" % (name,)
print "# watching py files below %s" % rootdir
print "# ", "^" * len(str(rootdir))
return failures
def slaverun_TerminalSession(channel):
""" we run this on the other side. """
print "SLAVE: starting"
topdir, repr, failures = channel.receive()
print "SLAVE: received configuration"
config = py.test.config
config.initdirect(topdir, repr, failures)
config.option.looponfailing = False
config.option.usepdb = False
config.option.executable = None
session = config.initsession()
session.shouldclose = channel.isclosed
print "SLAVE: starting session.main()"
session.main()
failures = session.getitemoutcomepairs(py.test.Item.Failed)
failures = [config.get_collector_trail(item) for item,_ in failures]
channel.send(failures)

View File

@ -4,6 +4,7 @@ from time import time as now
Item = py.test.Item Item = py.test.Item
from py.__.test.terminal.out import getout from py.__.test.terminal.out import getout
import py.__.code.safe_repr import py.__.code.safe_repr
from py.__.test.representation import Presenter
def getrelpath(source, dest): def getrelpath(source, dest):
base = source.common(dest) base = source.common(dest)
@ -25,15 +26,8 @@ class TerminalSession(Session):
file = py.std.sys.stdout file = py.std.sys.stdout
self._file = file self._file = file
self.out = getout(file) self.out = getout(file)
self._started = {}
self._opencollectors = [] self._opencollectors = []
self.presenter = Presenter(self.out, config)
def main(self):
if self.config.option._remote:
from py.__.test.terminal import remote
return remote.main(self.config, self._file, self.config._origargs)
else:
return super(TerminalSession, self).main()
# --------------------- # ---------------------
# PROGRESS information # PROGRESS information
@ -128,10 +122,10 @@ class TerminalSession(Session):
for name in 'looponfailing', 'exitfirst', 'nomagic': for name in 'looponfailing', 'exitfirst', 'nomagic':
if getattr(option, name): if getattr(option, name):
modes.append(name) modes.append(name)
if option._fromremote: #if self._isremoteoption._fromremote:
modes.insert(0, 'child process') # modes.insert(0, 'child process')
else: #else:
modes.insert(0, 'inprocess') # modes.insert(0, 'inprocess')
mode = "/".join(modes) mode = "/".join(modes)
self.out.line("testing-mode: %s" % mode) self.out.line("testing-mode: %s" % mode)
self.out.line("executable: %s (%s)" % self.out.line("executable: %s (%s)" %
@ -146,7 +140,7 @@ class TerminalSession(Session):
self.out.line("test target: %s" %(x.fspath,)) self.out.line("test target: %s" %(x.fspath,))
conftestmodules = self.config.conftest.getconftestmodules(None) conftestmodules = self.config.conftest.getconftestmodules(None)
for i,x in py.builtin.enumerate(conftestmodules): for i,x in py.builtin.enumerate(conftestmodules):
self.out.line("initial conf %d: %s" %(i, x.__file__)) self.out.line("initial conf %d: %s" %(i, x.__file__))
#for i, x in py.builtin.enumerate(py.test.config.configpaths): #for i, x in py.builtin.enumerate(py.test.config.configpaths):
@ -279,173 +273,8 @@ class TerminalSession(Session):
if not traceback: if not traceback:
self.out.line("empty traceback from item %r" % (item,)) self.out.line("empty traceback from item %r" % (item,))
return return
handler = getattr(self, 'repr_failure_tb%s' % self.config.option.tbstyle) handler = getattr(self.presenter, 'repr_failure_tb%s' % self.config.option.tbstyle)
handler(item, excinfo, traceback) handler(item, excinfo, traceback, lambda : self.repr_out_err(item))
def repr_failure_tblong(self, item, excinfo, traceback):
if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError):
recursionindex = traceback.recursionindex()
else:
recursionindex = None
last = traceback[-1]
first = traceback[0]
for index, entry in py.builtin.enumerate(traceback):
if entry == first:
if item:
self.repr_failure_info(item, entry)
self.out.line()
else:
self.out.line("")
source = self.getentrysource(entry)
firstsourceline = entry.getfirstlinesource()
marker_location = entry.lineno - firstsourceline
if entry == last:
self.repr_source(source, 'E', marker_location)
self.repr_failure_explanation(excinfo, source)
else:
self.repr_source(source, '>', marker_location)
self.out.line("")
self.out.line("[%s:%d]" %(entry.frame.code.path, entry.lineno+1))
self.repr_locals(entry)
# trailing info
if entry == last:
#if item:
# self.repr_failure_info(item, entry)
self.repr_out_err(item)
self.out.sep("_")
else:
self.out.sep("_ ")
if index == recursionindex:
self.out.line("Recursion detected (same locals & position)")
self.out.sep("!")
break
def repr_failure_tbshort(self, item, excinfo, traceback):
# print a Python-style short traceback
if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError):
recursionindex = traceback.recursionindex()
else:
recursionindex = None
last = traceback[-1]
first = traceback[0]
self.out.line()
for index, entry in py.builtin.enumerate(traceback):
code = entry.frame.code
self.out.line(' File "%s", line %d, in %s' % (
code.raw.co_filename, entry.lineno+1, code.raw.co_name))
try:
fullsource = entry.frame.code.fullsource
except py.error.ENOENT:
source = ["?"]
else:
try:
source = [fullsource[entry.lineno].lstrip()]
except IndexError:
source = []
if entry == last:
if source:
self.repr_source(source, 'E')
self.repr_failure_explanation(excinfo, source)
else:
if source:
self.repr_source(source, ' ')
self.repr_locals(entry)
# trailing info
if entry == last:
#if item:
# self.repr_failure_info(item, entry)
self.repr_out_err(item)
self.out.sep("_")
else:
if index == recursionindex:
self.out.line("Recursion detected (same locals & position)")
self.out.sep("!")
break
# the following is only used by the combination '--pdb --tb=no'
repr_failure_tbno = repr_failure_tbshort
def repr_failure_info(self, item, entry):
root = item.fspath
modpath = item.getmodpath()
try:
fn, lineno = item.getpathlineno()
except TypeError:
assert isinstance(item.parent, py.test.collect.Generator)
# a generative test yielded a non-callable
fn, lineno = item.parent.getpathlineno()
# hum, the following overloads traceback output
#if fn != entry.frame.code.path or \
# entry.frame.code.firstlineno != lineno:
# self.out.line("testcode: %s:%d" % (fn, lineno+1))
if root == fn:
self.out.sep("_", "entrypoint: %s" %(modpath))
else:
self.out.sep("_", "entrypoint: %s %s" %(root.basename, modpath))
def getentrysource(self, entry):
try:
source = entry.getsource()
except py.error.ENOENT:
source = py.code.Source("[failure to get at sourcelines from %r]\n" % entry)
return source.deindent()
def repr_source(self, source, marker=">", marker_location=-1):
if marker_location < 0:
marker_location += len(source)
if marker_location < 0:
marker_location = 0
if marker_location >= len(source):
marker_location = len(source) - 1
for i in range(len(source)):
if i == marker_location:
prefix = marker + " "
else:
prefix = " "
self.out.line(prefix + source[i])
def repr_failure_explanation(self, excinfo, source):
try:
s = str(source.getstatement(len(source)-1))
except KeyboardInterrupt:
raise
except:
s = str(source[-1])
indent = " " * (4 + (len(s) - len(s.lstrip())))
# get the real exception information out
lines = excinfo.exconly(tryshort=True).split('\n')
self.out.line('>' + indent[:-1] + lines.pop(0))
for x in lines:
self.out.line(indent + x)
return
# XXX reinstate the following with a --magic option?
# the following line gets user-supplied messages (e.g.
# for "assert 0, 'custom message'")
msg = getattr(getattr(excinfo, 'value', ''), 'msg', '')
info = None
if not msg:
special = excinfo.errisinstance((SyntaxError, SystemExit, KeyboardInterrupt))
if not self.config.option.nomagic and not special:
try:
info = excinfo.traceback[-1].reinterpret() # very detailed info
except KeyboardInterrupt:
raise
except:
if self.config.option.verbose >= 1:
self.out.line("[reinterpretation traceback]")
py.std.traceback.print_exc(file=py.std.sys.stdout)
else:
self.out.line("[reinterpretation failed, increase "
"verbosity to see details]")
# print reinterpreted info if any
if info:
lines = info.split('\n')
self.out.line('>' + indent[:-1] + lines.pop(0))
for x in lines:
self.out.line(indent + x)
def repr_out_err(self, colitem): def repr_out_err(self, colitem):
for parent in colitem.listchain(): for parent in colitem.listchain():
@ -453,23 +282,6 @@ class TerminalSession(Session):
if obj: if obj:
self.out.sep("- ", "%s: recorded std%s" % (parent.name, name)) self.out.sep("- ", "%s: recorded std%s" % (parent.name, name))
self.out.line(obj) self.out.line(obj)
def repr_locals(self, entry):
if self.config.option.showlocals:
self.out.sep('- ', 'locals')
for name, value in entry.frame.f_locals.items():
if name == '__builtins__':
self.out.line("__builtins__ = <builtins>")
else:
# This formatting could all be handled by the _repr() function, which is
# only repr.Repr in disguise, so is very configurable.
str_repr = py.__.code.safe_repr._repr(value)
if len(str_repr) < 70 or not isinstance(value,
(list, tuple, dict)):
self.out.line("%-10s = %s" %(name, str_repr))
else:
self.out.line("%-10s =\\" % (name,))
py.std.pprint.pprint(value, stream=self.out)
def repr_pythonversion(): def repr_pythonversion():
v = py.std.sys.version_info v = py.std.sys.version_info

View File

@ -12,18 +12,6 @@ def test_failing_import_execfile():
py.test.raises(ImportError, col.run) py.test.raises(ImportError, col.run)
py.test.raises(ImportError, col.run) py.test.raises(ImportError, col.run)
def XXXtest_finds_root():
fn = datadir / 'filetest.py'
col = py.test.collect.Module(fn)
root, namelist = col.fromroot()
assert isinstance(root, py.test.collect.Directory)
cur = root
for x in namelist:
cur = cur.join(x)
assert cur.name == col.name
assert cur.parent == col.parent
assert cur.fspath == cur.fspath
def test_collect_listnames_and_back(): def test_collect_listnames_and_back():
col1 = py.test.collect.Directory(datadir.dirpath()) col1 = py.test.collect.Directory(datadir.dirpath())
col2 = col1.join(datadir.basename) col2 = col1.join(datadir.basename)
@ -203,10 +191,10 @@ def test_custom_python_collection_from_conftest():
assert 23 == 23 assert 23 == 23
""") """)
from py.__.test.collect import getfscollector
for x in (o, checkfile, checkfile.dirpath()): for x in (o, checkfile, checkfile.dirpath()):
config = py.test.config._reparse([x])
#print "checking that %s returns custom items" % (x,) #print "checking that %s returns custom items" % (x,)
col = getfscollector(x) col = config._getcollector(x)
assert len(list(col.tryiter(py.test.Item))) == 2 assert len(list(col.tryiter(py.test.Item))) == 2
#assert items[1].__class__.__name__ == 'MyFunction' #assert items[1].__class__.__name__ == 'MyFunction'
@ -215,7 +203,7 @@ def test_custom_python_collection_from_conftest():
try: try:
config = py.test.config._reparse([]) config = py.test.config._reparse([])
out = py.std.cStringIO.StringIO() out = py.std.cStringIO.StringIO()
session = config.getsessionclass()(config, out) session = config._getsessionclass()(config, out)
session.main() session.main()
l = session.getitemoutcomepairs(py.test.Item.Passed) l = session.getitemoutcomepairs(py.test.Item.Passed)
assert len(l) == 2 assert len(l) == 2
@ -225,7 +213,7 @@ def test_custom_python_collection_from_conftest():
# test that running the file directly works # test that running the file directly works
config = py.test.config._reparse([str(checkfile)]) config = py.test.config._reparse([str(checkfile)])
out = py.std.cStringIO.StringIO() out = py.std.cStringIO.StringIO()
session = config.getsessionclass()(config, out) session = config._getsessionclass()(config, out)
session.main() session.main()
l = session.getitemoutcomepairs(py.test.Item.Passed) l = session.getitemoutcomepairs(py.test.Item.Passed)
assert len(l) == 2 assert len(l) == 2
@ -250,10 +238,10 @@ def test_custom_NONpython_collection_from_conftest():
""") """)
checkfile = o.ensure('somedir', 'moredir', 'check_something.txt') checkfile = o.ensure('somedir', 'moredir', 'check_something.txt')
from py.__.test.collect import getfscollector
for x in (o, checkfile, checkfile.dirpath()): for x in (o, checkfile, checkfile.dirpath()):
print "checking that %s returns custom items" % (x,) print "checking that %s returns custom items" % (x,)
col = getfscollector(x) config = py.test.config._reparse([x])
col = config._getcollector(x)
assert len(list(col.tryiter(py.test.Item))) == 1 assert len(list(col.tryiter(py.test.Item))) == 1
#assert items[1].__class__.__name__ == 'MyFunction' #assert items[1].__class__.__name__ == 'MyFunction'
@ -262,7 +250,7 @@ def test_custom_NONpython_collection_from_conftest():
try: try:
config = py.test.config._reparse([]) config = py.test.config._reparse([])
out = py.std.cStringIO.StringIO() out = py.std.cStringIO.StringIO()
session = config.getsessionclass()(config, out) session = config._getsessionclass()(config, out)
session.main() session.main()
l = session.getitemoutcomepairs(py.test.Item.Passed) l = session.getitemoutcomepairs(py.test.Item.Passed)
assert len(l) == 1 assert len(l) == 1
@ -272,12 +260,14 @@ def test_custom_NONpython_collection_from_conftest():
# test that running the file directly works # test that running the file directly works
config = py.test.config._reparse([str(checkfile)]) config = py.test.config._reparse([str(checkfile)])
out = py.std.cStringIO.StringIO() out = py.std.cStringIO.StringIO()
session = config.getsessionclass()(config, out) session = config._getsessionclass()(config, out)
session.main() session.main()
l = session.getitemoutcomepairs(py.test.Item.Passed) l = session.getitemoutcomepairs(py.test.Item.Passed)
assert len(l) == 1 assert len(l) == 1
def test_order_of_execution_generator_same_codeline(): def test_order_of_execution_generator_same_codeline():
if py.test.config.is_boxed():
py.test.skip("Does not work with boxing")
test_list = [] test_list = []
expected_list = range(6) expected_list = range(6)
@ -295,6 +285,8 @@ def test_order_of_execution_generator_same_codeline():
def test_order_of_execution_generator_different_codeline(): def test_order_of_execution_generator_different_codeline():
if py.test.config.is_boxed():
py.test.skip("Does not work with boxing")
test_list = [] test_list = []
expected_list = range(3) expected_list = range(3)

View File

@ -1,6 +1,8 @@
from __future__ import generators from __future__ import generators
import py import py
from py.__.test.config import gettopdir
def test_tmpdir(): def test_tmpdir():
d1 = py.test.ensuretemp('hello') d1 = py.test.ensuretemp('hello')
d2 = py.test.ensuretemp('hello') d2 = py.test.ensuretemp('hello')
@ -72,3 +74,266 @@ def test_siblingconftest_fails_maybe():
print py.process.cmdexec("py.test") print py.process.cmdexec("py.test")
finally: finally:
old.chdir() old.chdir()
def test_config_overwrite():
o = py.test.ensuretemp('testconfigget')
o.ensure("conftest.py").write("x=1")
config = py.test.config._reparse([str(o)])
assert config.getvalue('x') == 1
config._overwrite('x', 2)
assert config.getvalue('x') == 2
config = py.test.config._reparse([str(o)])
assert config.getvalue('x') == 1
def test_gettopdir():
tmp = py.test.ensuretemp("topdir")
assert gettopdir([tmp]) == tmp
topdir =gettopdir([tmp.join("hello"), tmp.join("world")])
assert topdir == tmp
def test_gettopdir_pypkg():
tmp = py.test.ensuretemp("topdir2")
a = tmp.ensure('a', dir=1)
b = tmp.ensure('a', 'b', '__init__.py')
c = tmp.ensure('a', 'b', 'c.py')
Z = tmp.ensure('Z', dir=1)
assert gettopdir([c]) == a
assert gettopdir([c, Z]) == tmp
def test_config_init_direct():
tmp = py.test.ensuretemp("initdirect")
tmp.ensure("__init__.py")
tmp.ensure("conftest.py").write("x=1 ; y=2")
hello = tmp.ensure("test_hello.py")
config = py.test.config._reparse([hello])
repr = config.make_repr(conftestnames=['x', 'y'])
config2 = py.test.config._reparse([tmp.dirpath()])
config2._initialized = False # we have to do that from tests
config2.initdirect(topdir=tmp.dirpath(), repr=repr)
for col1, col2 in zip(config.getcolitems(), config2.getcolitems()):
assert col1.fspath == col2.fspath
py.test.raises(AssertionError, "config2.initdirect(None, None)")
from py.__.test.config import Config
config3 = Config()
config3.initdirect(topdir=tmp.dirpath(), repr=repr,
coltrails=[(tmp.basename, (hello.basename,))])
assert config3.getvalue('x') == 1
assert config3.getvalue('y') == 2
cols = config.getcolitems()
assert len(cols) == 1
col = cols[0]
assert col.name == 'test_hello.py'
assert col.parent.name == tmp.basename
assert col.parent.parent is None
def test_config_make_and_merge_repr():
tmp = py.test.ensuretemp("reprconfig1")
tmp.ensure("__init__.py")
tmp.ensure("conftest.py").write("x=1")
config = py.test.config._reparse([tmp])
repr = config.make_repr(conftestnames=['x'])
config.option.verbose = 42
repr2 = config.make_repr(conftestnames=[], optnames=['verbose'])
config = py.test.config._reparse([tmp.dirpath()])
py.test.raises(KeyError, "config.getvalue('x')")
config.merge_repr(repr)
assert config.getvalue('x') == 1
config.merge_repr(repr2)
assert config.option.verbose == 42
def test_config_marshability():
tmp = py.test.ensuretemp("configmarshal")
tmp.ensure("__init__.py")
tmp.ensure("conftest.py").write("a = object()")
config = py.test.config._reparse([tmp])
py.test.raises(ValueError, "config.make_repr(conftestnames=['a'])")
config.option.hello = lambda x: None
py.test.raises(ValueError, "config.make_repr(conftestnames=[])")
config.make_repr(conftestnames=[], optnames=[])
def test_config_rconfig():
tmp = py.test.ensuretemp("rconfigopt")
tmp.ensure("__init__.py")
tmp.ensure("conftest.py").write(py.code.Source("""
import py
Option = py.test.config.Option
option = py.test.config.addoptions("testing group",
Option('-g', '--glong', action="store", default=42,
type="int", dest="gdest", help="g value."))
"""))
config = py.test.config._reparse([tmp, "-g", "11"])
assert config.option.gdest == 11
repr = config.make_repr(conftestnames=[])
config = py.test.config._reparse([tmp.dirpath()])
py.test.raises(AttributeError, "config.option.gdest")
config.merge_repr(repr)
assert config.option.gdest == 11
class TestSessionAndOptions:
def setup_class(cls):
cls.tmproot = py.test.ensuretemp(cls.__name__)
def setup_method(self, method):
self.tmpdir = self.tmproot.ensure(method.__name__, dir=1)
def test_sessionname_default(self):
config = py.test.config._reparse([self.tmpdir])
assert config._getsessionname() == 'TerminalSession'
def test_sessionname_dist(self):
config = py.test.config._reparse([self.tmpdir, '--dist'])
assert config._getsessionname() == 'RSession'
def test_implied_lsession(self):
optnames = 'startserver runbrowser apigen=x rest box'.split()
for x in optnames:
config = py.test.config._reparse([self.tmpdir, '--%s' % x])
assert config._getsessionname() == 'LSession'
for x in 'startserver runbrowser rest'.split():
config = py.test.config._reparse([self.tmpdir, '--dist', '--%s' % x])
assert config._getsessionname() == 'RSession'
def test_implied_remote_terminal_session(self):
config = py.test.config._reparse([self.tmpdir, '--looponfailing'])
assert config._getsessionname() == 'RemoteTerminalSession'
config = py.test.config._reparse([self.tmpdir, '--exec=x'])
assert config._getsessionname() == 'RemoteTerminalSession'
config = py.test.config._reparse([self.tmpdir, '--dist', '--exec=x'])
assert config._getsessionname() == 'RSession'
def test_tkintersession(self):
config = py.test.config._reparse([self.tmpdir, '--tkinter'])
assert config._getsessionname() == 'TkinterSession'
config = py.test.config._reparse([self.tmpdir, '--dist'])
def test_sessionname_lookup_custom(self):
self.tmpdir.join("conftest.py").write(py.code.Source("""
class MySession:
def __init__(self, config):
self.config = config
"""))
config = py.test.config._reparse(["--session=MySession", self.tmpdir])
session = config.initsession()
assert session.__class__.__name__ == 'MySession'
def test_initsession(self):
config = py.test.config._reparse([self.tmpdir])
session = config.initsession()
assert session.config is config
def test_boxing_options(self):
# XXX config.is_boxed() is probably not a good idea
tmpdir = self.tmpdir
config = py.test.config._reparse([tmpdir])
assert not config.option.boxing
assert not config.is_boxed()
#tmpdir.join("conftest.py").write("dist_boxing=True\n")
#config = py.test.config._reparse([tmpdir])
#assert config.is_boxed()
tmpdir.join("conftest.py").write("dist_boxing=False\n")
config = py.test.config._reparse([tmpdir])
assert not config.is_boxed()
config = py.test.config._reparse([tmpdir, '--box'])
assert config.is_boxed()
class TestConfigColitems:
def setup_class(cls):
cls.tmproot = py.test.ensuretemp(cls.__name__)
def setup_method(self, method):
self.tmpdir = self.tmproot.mkdir(method.__name__)
def test_getcolitems_onedir(self):
config = py.test.config._reparse([self.tmpdir])
colitems = config.getcolitems()
assert len(colitems) == 1
col = colitems[0]
assert isinstance(col, py.test.collect.Directory)
for col in col.listchain():
assert col.config is config
def test_getcolitems_twodirs(self):
config = py.test.config._reparse([self.tmpdir, self.tmpdir])
colitems = config.getcolitems()
assert len(colitems) == 2
col1, col2 = colitems
assert col1.name == col2.name
assert col1.parent == col2.parent
def test_getcolitems_curdir_and_subdir(self):
a = self.tmpdir.ensure("a", dir=1)
config = py.test.config._reparse([self.tmpdir, a])
colitems = config.getcolitems()
assert len(colitems) == 2
col1, col2 = colitems
assert col1.name == self.tmpdir.basename
assert col2.name == 'a'
for col in colitems:
for subcol in col.listchain():
assert col.config is config
def test__getcol_global_file(self):
x = self.tmpdir.ensure("x.py")
config = py.test.config._reparse([x])
col = config._getcollector(x)
assert isinstance(col, py.test.collect.Module)
assert col.name == 'x.py'
assert col.parent.name == self.tmpdir.basename
assert col.parent.parent is None
for col in col.listchain():
assert col.config is config
def test__getcol_global_dir(self):
x = self.tmpdir.ensure("a", dir=1)
config = py.test.config._reparse([x])
col = config._getcollector(x)
assert isinstance(col, py.test.collect.Directory)
print col.listchain()
assert col.name == 'a'
assert col.parent is None
assert col.config is config
def test__getcol_pkgfile(self):
x = self.tmpdir.ensure("x.py")
self.tmpdir.ensure("__init__.py")
config = py.test.config._reparse([x])
col = config._getcollector(x)
assert isinstance(col, py.test.collect.Module)
assert col.name == 'x.py'
assert col.parent.name == x.dirpath().basename
assert col.parent.parent is None
for col in col.listchain():
assert col.config is config
def test_get_collector_trail_and_back(self):
a = self.tmpdir.ensure("a", dir=1)
self.tmpdir.ensure("a", "__init__.py")
x = self.tmpdir.ensure("a", "trail.py")
config = py.test.config._reparse([x])
col = config._getcollector(x)
trail = config.get_collector_trail(col)
assert len(trail) == 2
assert trail[0] == a.relto(config.topdir)
assert trail[1] == ('trail.py',)
col2 = config._getcollector(trail)
assert col2.listchain() == col.listchain()
def test_get_collector_trail_topdir_and_beyond(self):
config = py.test.config._reparse([self.tmpdir])
col = config._getcollector(config.topdir)
trail = config.get_collector_trail(col)
assert len(trail) == 2
assert trail[0] == '.'
assert trail[1] == ()
col2 = config._getcollector(trail)
assert col2.fspath == config.topdir
assert len(col2.listchain()) == 1
col3 = config._getcollector(config.topdir.dirpath())
py.test.raises(ValueError,
"config.get_collector_trail(col3)")

View File

@ -11,6 +11,11 @@ class TestConftestValueAccessGlobal:
d.ensure("adir/conftest.py").write("a=1 ; Directory = 3") d.ensure("adir/conftest.py").write("a=1 ; Directory = 3")
d.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5") d.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5")
def test_basic_init(self):
conftest = Conftest()
conftest.setinitial([self.basedir.join("adir")])
assert conftest.rget("a") == 1
def test_immediate_initialiation_and_incremental_are_the_same(self): def test_immediate_initialiation_and_incremental_are_the_same(self):
conftest = Conftest() conftest = Conftest()
snap0 = len(conftest._path2confmods) snap0 = len(conftest._path2confmods)
@ -25,40 +30,40 @@ class TestConftestValueAccessGlobal:
def test_default_Module_setting_is_visible_always(self): def test_default_Module_setting_is_visible_always(self):
for path in self.basedir.parts(): for path in self.basedir.parts():
conftest = Conftest(path) conftest = Conftest(path)
assert conftest.lget("Module") == py.test.collect.Module #assert conftest.lget("Module") == py.test.collect.Module
assert conftest.rget("Module") == py.test.collect.Module assert conftest.rget("Module") == py.test.collect.Module
def test_default_has_lower_prio(self): def test_default_has_lower_prio(self):
conftest = Conftest(self.basedir.join("adir")) conftest = Conftest(self.basedir.join("adir"))
assert conftest.rget('Directory') == 3 assert conftest.rget('Directory') == 3
assert conftest.lget('Directory') == py.test.collect.Directory #assert conftest.lget('Directory') == py.test.collect.Directory
def test_value_access_not_existing(self): def test_value_access_not_existing(self):
conftest = Conftest(self.basedir) conftest = Conftest(self.basedir)
py.test.raises(KeyError, "conftest.rget('a')") py.test.raises(KeyError, "conftest.rget('a')")
py.test.raises(KeyError, "conftest.lget('a')") #py.test.raises(KeyError, "conftest.lget('a')")
def test_value_access_by_path(self): def test_value_access_by_path(self):
conftest = Conftest(self.basedir) conftest = Conftest(self.basedir)
assert conftest.rget("a", self.basedir.join('adir')) == 1 assert conftest.rget("a", self.basedir.join('adir')) == 1
assert conftest.lget("a", self.basedir.join('adir')) == 1 #assert conftest.lget("a", self.basedir.join('adir')) == 1
assert conftest.rget("a", self.basedir.join('adir', 'b')) == 1.5 assert conftest.rget("a", self.basedir.join('adir', 'b')) == 1.5
assert conftest.lget("a", self.basedir.join('adir', 'b')) == 1 #assert conftest.lget("a", self.basedir.join('adir', 'b')) == 1
assert conftest.lget("b", self.basedir.join('adir', 'b')) == 2 #assert conftest.lget("b", self.basedir.join('adir', 'b')) == 2
assert py.test.raises(KeyError, #assert py.test.raises(KeyError,
'conftest.lget("b", self.basedir.join("a"))' # 'conftest.lget("b", self.basedir.join("a"))'
) #)
def test_value_access_with_init_one_conftest(self): def test_value_access_with_init_one_conftest(self):
conftest = Conftest(self.basedir.join('adir')) conftest = Conftest(self.basedir.join('adir'))
assert conftest.rget("a") == 1 assert conftest.rget("a") == 1
assert conftest.lget("a") == 1 #assert conftest.lget("a") == 1
def test_value_access_with_init_two_conftests(self): def test_value_access_with_init_two_conftests(self):
conftest = Conftest(self.basedir.join("adir", "b")) conftest = Conftest(self.basedir.join("adir", "b"))
conftest.rget("a") == 1.5 conftest.rget("a") == 1.5
conftest.lget("a") == 1 #conftest.lget("a") == 1
conftest.lget("b") == 1 #conftest.lget("b") == 1
class TestConftestValueAccessInPackage(TestConftestValueAccessGlobal): class TestConftestValueAccessInPackage(TestConftestValueAccessGlobal):
def setup_class(cls): def setup_class(cls):

View File

@ -32,10 +32,10 @@ def test_collect_doctest_files_with_test_prefix():
>>> i-1 >>> i-1
4 4
""")) """))
from py.__.test.collect import getfscollector
for x in (o, checkfile): for x in (o, checkfile):
#print "checking that %s returns custom items" % (x,) #print "checking that %s returns custom items" % (x,)
col = getfscollector(x) config = py.test.config._reparse([x])
col = config._getcollector(x)
items = list(col.tryiter(py.test.Item)) items = list(col.tryiter(py.test.Item))
assert len(items) == 1 assert len(items) == 1
assert isinstance(items[0], DoctestText) assert isinstance(items[0], DoctestText)

View File

@ -0,0 +1,64 @@
import py
from py.__.test.representation import Presenter
from py.__.test.terminal.out import getout
from StringIO import StringIO
import sys
def test_repr_source():
source = py.code.Source("""
def f(x):
pass
""").strip()
config = py.test.config._reparse([])
s = StringIO()
out = getout(s)
p = Presenter(out, config)
p.repr_source(source, "|", 0)
lines = s.getvalue().split("\n")
assert len(lines) == 3
assert lines[0].startswith("|")
assert lines[0].find("def f(x)") != -1
assert lines[1].find("pass") != -1
def test_repr_failure_explanation():
""" We check here if indentation is right
"""
def f():
def g():
1/0
try:
g()
except:
e = py.code.ExceptionInfo()
return e
config = py.test.config._reparse([])
s = StringIO()
out = getout(s)
p = Presenter(out, config)
source = py.code.Source(f)
e = f()
p.repr_failure_explanation(e, source)
assert s.getvalue().startswith("> ")
def test_repr_local():
config = py.test.config._reparse(['--showlocals'])
s = StringIO()
out = getout(s)
p = Presenter(out, config)
p.repr_locals(locals())
for key in locals().keys():
assert s.getvalue().find(key) != -1
def test_repr_traceback_long():
py.test.skip("unfinished")
config = py.test.config._reparse([])
s = StringIO()
out = getout(s)
p = Presenter(out, config)
# errr... here we should
# a) prepare an item
# b) prepare excinfo
# c) prepare some traceback info, with few different ideas,
# like recursion detected etc.
# test it...

View File

@ -5,52 +5,27 @@ def setup_module(mod):
mod.datadir = setupdatadir() mod.datadir = setupdatadir()
mod.tmpdir = py.test.ensuretemp(mod.__name__) mod.tmpdir = py.test.ensuretemp(mod.__name__)
class TestDefaultSession: def test_default_session_options():
def test_simple(self): for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'],
config = py.test.config._reparse([datadir/'filetest.py']) ['--tb=long'], ['--fulltrace'], ['--nomagic'],
session = config.getsessionclass()(config, py.std.sys.stdout) ['--traceconfig'], ['-v'], ['-v', '-v']):
session.main() yield runfiletest, opts
l = session.getitemoutcomepairs(py.test.Item.Failed)
assert len(l) == 2
l = session.getitemoutcomepairs(py.test.Item.Passed)
assert not l
def test_simple_verbose(self): def runfiletest(opts):
config = py.test.config._reparse([datadir/'filetest.py', config = py.test.config._reparse(opts + [datadir/'filetest.py'])
'--verbose']) session = config.initsession()
session = config.getsessionclass()(config, py.std.sys.stdout) session.main()
session.main() l = session.getitemoutcomepairs(py.test.Item.Failed)
l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2
assert len(l) == 2 l = session.getitemoutcomepairs(py.test.Item.Passed)
l = session.getitemoutcomepairs(py.test.Item.Passed) assert not l
assert not l
def test_simple_verbose_verbose(self):
config = py.test.config._reparse([datadir/'filetest.py',
'-v', '-v'])
session = config.getsessionclass()(config, py.std.sys.stdout)
session.main()
l = session.getitemoutcomepairs(py.test.Item.Failed)
assert len(l) == 2
l = session.getitemoutcomepairs(py.test.Item.Passed)
assert not l
def test_session_parsing(self):
from py.__.test.terminal.terminal import TerminalSession
from py.__.test.tkinter.reportsession import ReportSession
config = py.test.config._reparse(['--session=terminal'])
assert issubclass(config.getsessionclass(), TerminalSession)
config = py.test.config._reparse(['--session=tkinter'])
assert issubclass(config.getsessionclass(), ReportSession)
config = py.test.config._reparse(['--tkinter'])
assert issubclass(config.getsessionclass(), ReportSession)
class TestKeywordSelection: class TestKeywordSelection:
def test_select_simple(self): def test_select_simple(self):
for keyword in ['test_one', 'est_on']: for keyword in ['test_one', 'est_on']:
config = py.test.config._reparse([datadir/'filetest.py', config = py.test.config._reparse([datadir/'filetest.py',
'-k', keyword]) '-k', keyword])
session = config.getsessionclass()(config, py.std.sys.stdout) session = config._getsessionclass()(config, py.std.sys.stdout)
session.main() session.main()
l = session.getitemoutcomepairs(py.test.Item.Failed) l = session.getitemoutcomepairs(py.test.Item.Failed)
assert len(l) == 1 assert len(l) == 1
@ -79,7 +54,7 @@ class TestKeywordSelection:
'TestClass test_2', 'xxx TestClass test_2',): 'TestClass test_2', 'xxx TestClass test_2',):
f = py.std.StringIO.StringIO() f = py.std.StringIO.StringIO()
config = py.test.config._reparse([o, '-k', keyword]) config = py.test.config._reparse([o, '-k', keyword])
session = config.getsessionclass()(config, f) session = config._getsessionclass()(config, f)
session.main() session.main()
print "keyword", repr(keyword) print "keyword", repr(keyword)
l = session.getitemoutcomepairs(py.test.Item.Passed) l = session.getitemoutcomepairs(py.test.Item.Passed)
@ -318,9 +293,8 @@ class TestTerminalSession:
assert expected_output in out assert expected_output in out
from py.__.test.terminal.remote import getrootdir
class TestRemote: class TestRemote:
def test_rootdir_is_package(self): def XXXtest_rootdir_is_package(self):
d = tmpdir.ensure('rootdirtest1', dir=1) d = tmpdir.ensure('rootdirtest1', dir=1)
d.ensure('__init__.py') d.ensure('__init__.py')
x1 = d.ensure('subdir', '__init__.py') x1 = d.ensure('subdir', '__init__.py')
@ -332,7 +306,7 @@ class TestRemote:
assert getrootdir([x3,x2]) == d assert getrootdir([x3,x2]) == d
assert getrootdir([x2,x3]) == d assert getrootdir([x2,x3]) == d
def test_rootdir_is_not_package(self): def XXXtest_rootdir_is_not_package(self):
one = tmpdir.ensure('rootdirtest1', 'hello') one = tmpdir.ensure('rootdirtest1', 'hello')
rootdir = getrootdir([one]) rootdir = getrootdir([one])
assert rootdir == one.dirpath() assert rootdir == one.dirpath()
@ -348,8 +322,7 @@ class TestRemote:
config = py.test.config._reparse( config = py.test.config._reparse(
['--exec=' + py.std.sys.executable, ['--exec=' + py.std.sys.executable,
o]) o])
assert config.option._remote cls = config._getsessionclass()
cls = config.getsessionclass()
out = [] # out = py.std.Queue.Queue() out = [] # out = py.std.Queue.Queue()
session = cls(config, out.append) session = cls(config, out.append)
session.main() session.main()
@ -368,16 +341,16 @@ class TestRemote:
""")) """))
print py.std.sys.executable print py.std.sys.executable
config = py.test.config._reparse(['--looponfailing', str(o)]) config = py.test.config._reparse(['--looponfailing', str(o)])
assert config.option._remote cls = config._getsessionclass()
cls = config.getsessionclass()
out = py.std.Queue.Queue() out = py.std.Queue.Queue()
session = cls(config, out.put) session = cls(config, out.put)
pool = py._thread.WorkerPool() pool = py._thread.WorkerPool()
reply = pool.dispatch(session.main) reply = pool.dispatch(session.main)
while 1: while 1:
s = out.get() s = out.get(timeout=1.0)
if s.find('1 failed') != -1: if s.find('1 failed') != -1:
break break
print s
else: else:
py.test.fail("did not see test_1 failure") py.test.fail("did not see test_1 failure")
# XXX we would like to have a cleaner way to finish # XXX we would like to have a cleaner way to finish

View File

@ -184,6 +184,7 @@ class ReportBackend:
self.queue.put(item) self.queue.put(item)
def start_tests(self, config = None, args = [], tests = []): def start_tests(self, config = None, args = [], tests = []):
py.test.skip("XXX fix this or remove --tkinter")
if self.running: if self.running:
return return
if config is None: if config is None:
@ -233,7 +234,7 @@ def remote(channel, tests = [], args = []):
if tests: if tests:
cols = getfailureitems(tests) cols = getfailureitems(tests)
else: else:
cols = config.remaining cols = config.args
session = ReportSession(config = config, channel=channel) session = ReportSession(config = config, channel=channel)
session.shouldclose = channel.isclosed session.shouldclose = channel.isclosed
session.main() session.main()

View File

@ -3,7 +3,7 @@ import py
from util import TestReport from util import TestReport
from py.__.test.session import Session from py.__.test.session import Session
class ReportSession(Session): class TkinterSession(Session):
def __init__(self, config = None, channel = None): def __init__(self, config = None, channel = None):
super(ReportSession, self).__init__(config) super(ReportSession, self).__init__(config)
@ -44,5 +44,5 @@ class ReportSession(Session):
def sendreport(self, report): def sendreport(self, report):
self.channel.send(report.to_channel()) self.channel.send(report.to_channel())
TkinterSession = ReportSession ReportSession = TkinterSession

View File

@ -178,9 +178,9 @@ class TestReportBackend:
assert l[0] is None assert l[0] is None
def test_start_tests(self): def test_start_tests(self):
config = py.test.config._reparse([]) config = py.test.config._reparse([datadir/'filetest.py'])
self.backend.start_tests(config = config, self.backend.start_tests(config = config,
args = [str(datadir / 'filetest.py')], args = config.args,
tests = []) tests = [])
while self.backend.running: while self.backend.running:
self.backend.update() self.backend.update()

View File

@ -8,7 +8,7 @@ def test_capture_out_err():
config = py.test.config._reparse([datadir/'filetest.py']) config = py.test.config._reparse([datadir/'filetest.py'])
backend = ReportBackend() backend = ReportBackend()
backend.start_tests(config = config, backend.start_tests(config = config,
args = config.remaining, args = config.args,
tests = []) tests = [])
while backend.running: while backend.running:
backend.update() backend.update()