[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__,
description = "py.test and the py lib",
revision = int('$LastChangedRevision: 37056 $'.split(':')[1][:-1]),
lastchangedate = '$LastChangedDate: 2007-01-20 13:11:01 +0100 (Sat, 20 Jan 2007) $',
revision = int('$LastChangedRevision: 37278 $'.split(':')[1][:-1]),
lastchangedate = '$LastChangedDate: 2007-01-24 17:46:46 +0100 (Wed, 24 Jan 2007) $',
version = version,
url = "http://codespeak.net/py",
download_url = "http://codespeak.net/download/py/%s.tar.gz" %(version,),
@ -32,7 +32,7 @@ initpkg(__name__,
'test.compat.TestCase' : ('./test/compat.py', 'TestCase'),
# 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.cmdline.main' : ('./test/cmdline.py', 'main'),
@ -93,6 +93,9 @@ initpkg(__name__,
'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'),

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()
pkgdir = fs_root.join('pkg')
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"' % (
tempdir, parentdir, pkgdir))
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
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):
"""Reinterpret the failing statement and returns a detailed information
about what operations are performed."""
@ -78,6 +82,10 @@ class TracebackEntry(object):
line = "<could not get sourceline>"
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):
""" Traceback objects encapsulate and offer higher level
access to Traceback entries.

View File

@ -94,7 +94,7 @@ testing
- running "py.test --looponfailing"
- 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
@ -109,15 +109,6 @@ 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
-----------------

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():
config = py.test.config._reparse([failure_demo])
session = config.getsessionclass()(config, py.std.sys.stdout)
session = config.initsession()
session.main()
l = session.getitemoutcomepairs(py.test.Item.Failed)
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:
* `dist_hosts` - a list of ssh addresses (including a specific path if it
should be different than the default: ``$HOME/pytestcache-hostname``)
* **`dist_hosts`**: a required list of ssh addresses (which each may
include a path, default path is: ``$HOME/pytestcache-HOSTNAME``)
* `dist_rsync_roots` - a list of packages to copy to the remote machines.
* `dist_remotepython` - the remote python to run.
* `SessionOptions` - containing some specific tuning options
* `dist_remotepython` - the remote python executable to run.
* `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::
@ -688,17 +691,6 @@ Sample configuration::
Running server is done by ``-w`` command line option or ``--startserver``
(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
-----------------

View File

@ -7,7 +7,6 @@ def setup_module(mod):
def test_doctest_basic():
# XXX get rid of the next line:
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("""
@ -29,7 +28,7 @@ def test_doctest_basic():
end
"""))
config = py.test.config._reparse([xtxt])
session = config.getsessionclass()(config, py.std.sys.stdout)
session = config.initsession()
session.main()
l = session.getitemoutcomepairs(py.test.Item.Failed)
assert len(l) == 0
@ -47,7 +46,7 @@ def test_js_ignore():
.. _`blah`: javascript:some_function()
"""))
config = py.test.config._reparse([xtxt])
session = config.getsessionclass()(config, py.std.sys.stdout)
session = config.initsession()
session.main()
l = session.getitemoutcomepairs(py.test.Item.Failed)
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):
for name in options:
assert name in ('delete',)
class RSync(object):
""" This is an example usage of py.execnet - a sample RSync
protocol, which can perform syncing 1-to-n.
channel = gw.remote_exec("""
import os, stat, shutil, md5
destdir, options = channel.receive()
modifiedfiles = []
Sample usage: you instantiate this class, eventually providing a
callback when rsyncing is done, than add some targets
(gateway + destdir) by running add_target and finally
invoking send() which will send provided source tree remotely.
def remove(path):
assert path.startswith(destdir)
try:
os.unlink(path)
except OSError:
# assume it's a dir
shutil.rmtree(path)
There is limited support for symlinks, which means that symlinks
pointing to the sourcetree will be send "as is" while external
symlinks will be just copied (regardless of existance of such
a path on remote side)
"""
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 receive_directory_structure(path, relcomponents):
#print "receive directory structure", path
try:
st = os.lstat(path)
except OSError:
st = None
msg = channel.receive()
if isinstance(msg, list):
if st and not stat.S_ISDIR(st.st_mode):
os.unlink(path)
st = None
if not st:
os.mkdir(path)
entrynames = {}
for entryname in msg:
receive_directory_structure(os.path.join(path, entryname),
relcomponents + [entryname])
entrynames[entryname] = True
if options.get('delete'):
for othername in os.listdir(path):
if othername not in entrynames:
otherpath = os.path.join(path, othername)
remove(otherpath)
def filter(self, path):
return True
def add_target(self, gateway, destdir, finishedcallback=None):
""" Adds a target for to-be-send data
"""
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):
""" Sends a sourcedir to previously prepared targets
"""
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:
if st and stat.S_ISREG(st.st_mode):
f = file(path, 'rb')
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()
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):
if path.check(dir=1):
subpaths = path.listdir()
print "sending directory structure", path
channel.send([p.basename for p in subpaths])
# 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:
send_directory_structure(p)
elif path.check(file=1):
data = path.read()
checksum = md5.md5(data).digest()
channel.send(checksum)
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,)
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):
channel = gw.remote_exec("""
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()
REMOTE_SOURCE = py.path.local(__file__).dirpath().\
join('rsync_remote.py').open().read() + "\nf()"

View File

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

View File

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

View File

@ -1,6 +1,19 @@
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):
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 += arg
obj = self.new()
obj.strpath = strpath
obj.strpath = os.path.normpath(strpath)
return obj
def __eq__(self, other):

View File

@ -17,6 +17,13 @@ class LocalSetup:
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):
import md5
import sha

View File

@ -8,30 +8,9 @@ def main(args=None):
warn_about_missing_assertion()
if args is None:
args = py.std.sys.argv[1:]
elif isinstance(args, basestring):
args = args.split(" ")
config = py.test.config
config.parse(args)
sessionclass = config.getsessionclass()
# 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)
session = config.initsession()
try:
failures = session.main()
if failures:

View File

@ -29,31 +29,9 @@ import py
def configproperty(name):
def fget(self):
#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)
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):
""" Collector instances are iteratively generated
(through their run() and join() methods)
@ -63,10 +41,10 @@ class Collector(object):
(or None if it is the root collector)
name: basename of this collector object
"""
def __init__(self, name, parent=None):
def __init__(self, name, parent=None):
self.name = name
self.parent = parent
self.option = getattr(parent, 'option', None)
self.config = getattr(parent, 'config', py.test.config)
self.fspath = getattr(parent, 'fspath', None)
Module = configproperty('Module')
@ -187,9 +165,10 @@ class Collector(object):
namelist = namelist.split("/")
cur = self
for name in namelist:
next = cur.join(name)
assert next is not None, (cur, name, namelist)
cur = next
if name:
next = cur.join(name)
assert next is not None, (cur, name, namelist)
cur = next
return cur
def haskeyword(self, keyword):
@ -211,8 +190,10 @@ class Collector(object):
newl.append(x.name)
return ".".join(newl)
# XXX: Copied from session
def skipbykeyword(self, keyword):
""" raise Skipped() exception if the given keyword
matches for this collector.
"""
if not keyword:
return
chain = self.listchain()
@ -342,15 +323,15 @@ class PyCollectorMixin(object):
d[name] = res
return d
def makeitem(self, name, obj, usefilters=True):
def makeitem(self, name, obj, usefilters=True):
if (not usefilters or self.classnamefilter(name)) and \
py.std.inspect.isclass(obj):
return self.Class(name, parent=self)
py.std.inspect.isclass(obj):
return self.Class(name, parent=self)
elif (not usefilters or self.funcnamefilter(name)) and callable(obj):
if obj.func_code.co_flags & 32: # generator function
return self.Generator(name, parent=self)
return self.Generator(name, parent=self)
else:
return self.Function(name, parent=self)
return self.Function(name, parent=self)
def _prepare(self):
if not hasattr(self, '_name2items'):
@ -391,7 +372,7 @@ class Module(FSCollector, PyCollectorMixin):
return res
def startcapture(self):
if not self.option.nocapture:
if not self.config.option.nocapture:
assert not hasattr(self, '_capture')
#self._capture = py.io.OutErrCapture()
# XXX integrate this into py.io / refactor

View File

@ -26,32 +26,65 @@ class CmdOptions(object):
class Config(object):
""" central hub for dealing with configuration/initialization data. """
Option = optparse.Option
conftest = Conftest()
def __init__(self):
self.option = CmdOptions()
self._parser = optparse.OptionParser(
usage="usage: %prog [options] [query] [filenames of tests]")
self._parsed = False
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._parsed, (
assert not self._initialized, (
"can only parse cmdline args once per Config object")
self._parsed = True
self._initialized = True
self.conftest.setinitial(args)
self.conftest.lget('adddefaultoptions')()
self.conftest.rget('adddefaultoptions')()
args = [str(x) for x in args]
self._origargs = args
cmdlineoption, remaining = self._parser.parse_args(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 remaining:
remaining.append(py.std.os.getcwd())
self.remaining = remaining
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.
@ -70,79 +103,186 @@ class Config(object):
if path is None, lookup the value in the initial
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
and looked up in initial config modules.
"""
sessionname = self.option.session + 'Session'
try:
return self.conftest.rget(sessionname)
except KeyError:
pass
sessionimportpaths = self.conftest.lget('sessionimportpaths')
importpath = sessionimportpaths[sessionname]
mod = __import__(importpath, None, None, ['__doc__'])
return getattr(mod, sessionname)
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
oldconfig = py.test.config
global config_per_process
oldconfig = py.test.config
try:
config = py.test.config = Config()
config.parse(args)
return config
config_per_process = py.test.config = Config()
config_per_process.parse(args)
return config_per_process
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
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
#
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. """
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 not option.nocapture:
print "--usepdb currently implies --nocapture"
#print "--pdb implies --nocapture"
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:
print "Cannot point browser when not starting server"
#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

@ -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
return clist[:]
def lget(self, name, path=None):
modules = self.getconftestmodules(path)
return self._get(name, modules)
# XXX no real use case, may probably go
#def lget(self, name, path=None):
# modules = self.getconftestmodules(path)
# return self._get(name, modules)
def rget(self, name, path=None):
modules = self.getconftestmodules(path)

View File

@ -10,15 +10,25 @@ Instance = py.test.collect.Instance
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
sessionimportpaths = {
'RSession': 'py.__.test.rsession.rsession',
'LSession': 'py.__.test.rsession.rsession',
'TerminalSession': 'py.__.test.terminal.terminal',
'TkinterSession': 'py.__.test.tkinter.reportsession',
}
def adddefaultoptions():
py.test.config.addoptions('general options',
Option('-v', '--verbose',
@ -56,35 +66,41 @@ def adddefaultoptions():
Option('', '--traceconfig',
action="store_true", dest="traceconfig", default=False,
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',
action="store", dest="apigen",
help="generate api documentation while testing (requires"
"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',
action="store", dest="session", default=None,
help="use given sessionclass, default is terminal."),
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."),
help="lookup given sessioname in conftest.py files and use it."),
)

View File

@ -31,7 +31,7 @@ class SetupState(object):
class Item(py.test.collect.Collector):
def startcapture(self):
if not self.option.nocapture:
if not self.config.option.nocapture:
# XXX refactor integrate capturing
#self._capture = py.io.OutErrCapture()
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):
count = 0
def __init__(self, fun, args=None, kwargs=None):
def __init__(self, fun, args=None, kwargs=None, config=None):
if args is None:
args = []
if kwargs is None:
kwargs = {}
self.fun = fun
self.config = config
assert self.config
self.args = args
self.kwargs = kwargs
@ -35,8 +37,7 @@ class FileBox(object):
self.PYTESTSTDOUT = tempdir.join('stdout')
self.PYTESTSTDERR = tempdir.join('stderr')
from py.__.test.rsession.rsession import remote_options
nice_level = remote_options.nice_level
nice_level = self.config.getvalue('dist_nicelevel')
pid = os.fork()
if pid:
if not continuation:
@ -48,10 +49,12 @@ class FileBox(object):
outcome = self.children(nice_level)
except:
excinfo = py.code.ExceptionInfo()
print "Internal box error"
x = open("/tmp/traceback", "w")
print >>x, "Internal box error"
for i in excinfo.traceback:
print str(i)[2:-1]
print excinfo
print >>x, str(i)[2:-1]
print >>x, excinfo
x.close()
os._exit(1)
os.close(1)
os.close(2)
@ -83,8 +86,8 @@ class FileBox(object):
retvalf.close()
os._exit(0)
def parent(self, pid):
pid, exitstat = os.waitpid(pid, 0)
def parent(self, pid, waiter=os.waitpid):
pid, exitstat = waiter(pid, 0)
self.signal = exitstat & 0x7f
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
def __init__(self, item, usepdb=False, reporter=None):
def __init__(self, item, usepdb=False, reporter=None, config=None):
self.item = item
self.usepdb = usepdb
self.reporter = reporter
self.config = config
assert self.config
def execute(self):
try:
@ -36,7 +38,8 @@ class RunExecutor(object):
if self.usepdb:
if self.reporter is not None:
self.reporter(report.ImmediateFailure(self.item,
ReprOutcome(outcome.make_repr())))
ReprOutcome(outcome.make_repr
(self.config.option.tbstyle))))
import pdb
pdb.post_mortem(excinfo._excinfo[2])
# XXX hmm, we probably will not like to continue from that
@ -54,8 +57,8 @@ class BoxExecutor(RunExecutor):
def execute(self):
def fun():
outcome = RunExecutor.execute(self)
return outcome.make_repr()
b = Box(fun)
return outcome.make_repr(self.config.option.tbstyle)
b = Box(fun, config=self.config)
pid = b.run()
assert pid
if b.retval is not None:
@ -76,13 +79,13 @@ class AsyncExecutor(RunExecutor):
def execute(self):
def fun():
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)
def cont():
parent(pid)
def cont(waiter=os.waitpid):
parent(pid, waiter=waiter)
if b.retval is not None:
passed, setupfailure, excinfo, skipped,\
critical, _, _, _ = b.retval

View File

@ -5,7 +5,6 @@ import thread, threading
from py.__.test.rsession.master import \
setup_slave, MasterNode
from py.__.test.rsession import report
from py.__.test.rsession.rsync import RSync
class HostInfo(object):
""" Class trying to store all necessary attributes
@ -39,11 +38,11 @@ class HostInfo(object):
def __ne__(self, other):
return not self == other
class HostRSync(RSync):
class HostRSync(py.execnet.RSync):
""" An rsync wrapper which filters out *~, .svn/ and *.pyc
"""
def __init__(self, rsync_roots):
RSync.__init__(self, delete=True)
py.execnet.RSync.__init__(self, delete=True)
self.rsync_roots = rsync_roots
def filter(self, path):
@ -60,104 +59,143 @@ class HostRSync(RSync):
class DummyGateway(object):
pass
def prepare_gateway(sshosts, optimise_localhost,
remote_python, pkgdir, real_create=True):
hosts = []
for host in sshosts:
if host.hostname != 'localhost' or not optimise_localhost:
if real_create:
# for tests we want to use somtehing different
if host.hostname == 'localhost' and optimise_localhost is False:
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
else:
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
class HostOptions(object):
""" Dummy container for host options, not to keep that
as different function parameters, mostly related to
tests only
"""
def __init__(self, rsync_roots=None, remote_python="python",
optimise_localhost=True, do_sync=True,
create_gateways=True):
self.rsync_roots = rsync_roots
self.remote_python = remote_python
self.optimise_localhost = optimise_localhost
self.do_sync = do_sync
self.create_gateways = create_gateways
def init_hosts(reporter, sshhosts, pkgdir, rsync_roots=None,
remote_python=None, \
remote_options={}, optimise_localhost=True,\
do_sync=True, done_dict=None):
if done_dict is None:
done_dict = {}
assert pkgdir.join("__init__.py").check(), (
class HostManager(object):
def __init__(self, sshhosts, config, pkgdir, options=HostOptions()):
self.sshhosts = sshhosts
self.pkgdir = pkgdir
self.config = config
self.options = options
if not options.create_gateways:
self.prepare_gateways = self.prepare_dummy_gateways
assert pkgdir.join("__init__.py").check(), (
"%s probably wrong" %(pkgdir,))
exc_info = [None]
hosts = prepare_gateway(sshhosts, optimise_localhost,
remote_python, pkgdir, real_create=do_sync)
# rsyncing
rsynced = {}
def prepare_dummy_gateways(self):
for host in self.sshhosts:
gw = DummyGateway()
host.gw = gw
gw.host = host
return self.sshhosts
if do_sync:
rsync = HostRSync(rsync_roots)
for host in hosts:
#for num, host, gw, remoterootpath in hosts:
remoterootpath = host.relpath
if (host.hostname, remoterootpath) in rsynced or\
(host.hostname == 'localhost' and optimise_localhost):
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())
def prepare_ssh_gateway(self, host):
if self.options.remote_python is None:
gw = py.execnet.SshGateway(host.hostname)
else:
gw = py.execnet.SshGateway(host.hostname,
remotepython=self.options.remote_python)
return gw
# hosts ready
return setup_nodes(hosts, pkgdir, remote_options, reporter, done_dict)
def prepare_popen_rsync_gateway(self, host):
""" 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):
nodes = []
for host in hosts:
ch = setup_slave(host.gw, os.path.join(host.relpath,\
pkgdir.basename), remote_options)
nodes.append(MasterNode(ch, reporter, done_dict))
return nodes
def prepare_popen_gateway(self, host):
if self.options.remote_python is None:
gw = py.execnet.PopenGateway()
else:
gw = py.execnet.PopenGateway(python=self.options.remote_python)
host.relpath = str(self.pkgdir.dirpath())
return gw
def teardown_hosts(reporter, channels, nodes, waiter=lambda : time.sleep(.1),
exitfirst=False):
for channel in channels:
channel.send(None)
from py.__.test.rsession.rsession import session_options
def prepare_gateways(self):
for host in self.sshhosts:
if host.hostname == 'localhost':
if not self.options.optimise_localhost:
gw = self.prepare_popen_rsync_gateway(host)
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
while not clean:
clean = True
for node in nodes:
if node.pending:
clean = False
waiter()
def need_rsync(self, rsynced, hostname, remoterootpath):
if (hostname, remoterootpath) in rsynced:
return False
if hostname == 'localhost' and self.options.optimise_localhost:
return False
return True
def init_hosts(self, reporter, done_dict={}):
if done_dict is None:
done_dict = {}
hosts = self.prepare_gateways()
# rsyncing
rsynced = {}
for channel in channels:
try:
report.wrapcall(reporter, channel.waitclose, int(session_options.waittime))
except KeyboardInterrupt, SystemExit:
raise
except:
pass
channel.gateway.exit()
if self.options.do_sync:
rsync = HostRSync(self.options.rsync_roots)
for host in hosts:
if not self.need_rsync(rsynced, host.hostname, host.relpath):
reporter(report.HostReady(host))
continue
rsynced[(host.hostname, host.relpath)] = True
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 "", ""
def box_runner(item, session, reporter):
r = BoxExecutor(item)
r = BoxExecutor(item, config=session.config)
return ReprOutcome(r.execute())
def plain_runner(item, session, reporter):
# box executor is doing stdout/err catching for us, let's do it here
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 = ReprOutcome(outcome.make_repr())
outcome = ReprOutcome(outcome.make_repr(session.config.option.tbstyle))
outcome.stdout, outcome.stderr = finishcapture(session)
return outcome
RunnerPolicy = {
'plain_runner':plain_runner,
'box_runner':box_runner
}
def benchmark_runner(item, session, reporter):
raise NotImplementedError()

View File

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

View File

@ -20,51 +20,82 @@ class Outcome(object):
self.signal = 0
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:
return None
excinfo = self.excinfo
tb_info = [self.traceback_entry_repr(x) for x in excinfo.traceback]
return (excinfo.type.__name__, str(excinfo.value), tb_info)
tb_info = [self.traceback_entry_repr(x, tbstyle)
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
relline = lineno - tb_entry.frame.code.firstlineno
path = str(tb_entry.path)
try:
from py.__.test.rsession.rsession import remote_options
if remote_options.tbstyle == 'long':
source = str(tb_entry.getsource())
else:
source = str(tb_entry.getsource()).split("\n")[relline]
except:
source = "<could not get source>"
return (relline, lineno, source, path)
#try:
if tb_style == 'long':
source = str(tb_entry.getsource())
else:
source = str(tb_entry.getsource()).split("\n")[relline]
name = tb_entry.frame.code.raw.co_name
# XXX: Bare except. What can getsource() raise anyway?
# SyntaxError, AttributeError, IndentationError for sure, check it
#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,
self.make_excinfo_repr(),
self.make_excinfo_repr(tbstyle),
self.skipped, self.is_critical, 0, "", "")
class TracebackEntryRepr(object):
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.path = py.path.local(self.path)
self.lineno = int(lineno)
self.locals = {}
def __repr__(self):
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):
def __init__(self, excinfo):
self.typename, self.value, tb = excinfo
self.traceback = [TracebackEntryRepr(x) for x in tb]
self.typename, self.value, tb_i = excinfo
tb, rec_index = tb_i
self.traceback = TracebackRepr([TracebackEntryRepr(x) for x in tb])
self.traceback.recursion_index = rec_index
def __repr__(self):
l = ["%s=%s" %(x, getattr(self, x))
for x in "typename value traceback".split()]
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):
def __init__(self, repr_tuple):
(self.passed, self.setupfailure, excinfo, self.skipped,

View File

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

View File

@ -2,7 +2,7 @@
""" reporter - different reporter for different purposes ;-)
Still lacks:
1. Hanging nodes are not good done
1. Hanging nodes are not done well
"""
import py
@ -10,6 +10,10 @@ import py
from py.__.test.terminal.out import getout
from py.__.test.rsession import report
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):
def __init__(self, config, hosts, pkgdir=py.path.local(py.__file__)):
@ -19,6 +23,7 @@ class AbstractReporter(object):
self.failed_tests_outcome = []
self.skipped_tests_outcome = []
self.out = getout(py.std.sys.stdout)
self.presenter = Presenter(self.out, config)
self.failed = 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])
@ -46,6 +51,7 @@ class AbstractReporter(object):
def report_SendItem(self, item):
address = item.host.hostname
assert isinstance(item.host.hostname, str)
if self.config.option.verbose:
print "Sending %s to %s" % (item.item,
address)
@ -108,6 +114,7 @@ class AbstractReporter(object):
self.out.sep('_', "%s on %s" %
(" ".join(event.item.listnames()), host))
if event.outcome.signal:
self.presenter.repr_item_info(event.item)
self.repr_signal(event.item, event.outcome)
else:
self.repr_failure(event.item, event.outcome)
@ -119,7 +126,7 @@ class AbstractReporter(object):
def gethost(self, event):
return event.host.hostname
def repr_failure(self, item, outcome):
def repr_failure(self, item, outcome):
excinfo = outcome.excinfo
traceback = excinfo.traceback
#if item and not self.config.option.fulltrace:
@ -127,18 +134,11 @@ class AbstractReporter(object):
if not traceback:
self.out.line("empty traceback from item %r" % (item,))
return
#handler = getattr(self, 'repr_failure_tb%s' % self.config.option.tbstyle)
self.repr_traceback(item, excinfo, traceback)
if outcome.stdout:
self.out.sep('-', " Captured process stdout: ")
self.out.write(outcome.stdout)
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)
handler = getattr(self.presenter, 'repr_failure_tb%s' % self.config.option.tbstyle)
handler(item, excinfo, traceback, lambda: self.repr_out_err(outcome))
def repr_out_err(self, outcome):
if outcome.stdout:
self.out.sep('-', " Captured process stdout: ")
self.out.write(outcome.stdout)
@ -146,25 +146,11 @@ class AbstractReporter(object):
self.out.sep('-', " Captured process stderr: ")
self.out.write(outcome.stderr)
def repr_traceback(self, item, excinfo, traceback):
if self.config.option.tbstyle == 'long':
for index, entry in py.builtin.enumerate(traceback):
self.out.sep('-')
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_signal(self, item, outcome):
signal = outcome.signal
self.out.line("Received signal: %d" % outcome.signal)
self.repr_out_err(outcome)
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):
texts = {}
for event in self.skipped_tests_outcome:
@ -225,19 +211,30 @@ class AbstractReporter(object):
def report_ReceivedItemOutcome(self, event):
host = event.host
if event.outcome.passed:
status = "PASSED "
self.passed[host] += 1
sys.stdout.write("%10s: PASSED " % host.hostname[:10])
elif event.outcome.skipped:
status = "SKIPPED"
self.skipped_tests_outcome.append(event)
self.skipped[host] += 1
sys.stdout.write("%10s: SKIPPED " % host.hostname[:10])
else:
status = "FAILED "
self.failed[host] += 1
self.failed_tests_outcome.append(event)
# we'll take care of them later
itempath = " ".join(event.item.listnames()[1:])
print "%10s: %s %s" %(host.hostname[:10], status, itempath)
sys.stdout.write("%10s: " % host.hostname[:10])
ansi_print("FAILED", esc=(31,1), newline=False, file=sys.stdout)
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):
self.nodes = event.nodes

View File

@ -31,18 +31,23 @@ class RestReporter(AbstractReporter):
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 = item.channel.gateway.host.hostname
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.hostname[:10],
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.hostname[:10],)))
self.add_rest(LiteralBlock('%10s: READY' % (item.host.hostname[:10],)))
def report_TestStarted(self, event):
txt = "Running tests on hosts: %s" % ", ".join(event.hosts)
@ -91,7 +96,7 @@ class RestReporter(AbstractReporter):
self.out.write(self.rest.render_links())
def report_ReceivedItemOutcome(self, event):
host = event.channel.gateway.host
host = self.gethost(event)
if event.outcome.passed:
status = [Strong("PASSED")]
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.master import \
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,\
box_runner, RunnerPolicy
box_runner
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):
"""
An abstract session executes collectors/items through a runner.
@ -114,17 +52,29 @@ class AbstractSession(object):
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
start_server()
httpd = start_server(server_address = ('', port))
port = httpd.server_port
if self.config.option.runbrowser:
import webbrowser
webbrowser.open("http://localhost:8000")
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
@ -150,6 +100,8 @@ class AbstractSession(object):
reporterror = staticmethod(reporterror)
def kill_server(self, startserverflag):
""" Kill web server
"""
if startserverflag:
from py.__.test.rsession.web import kill_server
kill_server()
@ -172,6 +124,8 @@ class AbstractSession(object):
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)
@ -186,9 +140,8 @@ class RSession(AbstractSession):
"""
def main(self, reporter=None):
""" 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()
reporter, startserverflag = self.init_reporter(reporter,
sshhosts, RemoteReporter)
@ -198,22 +151,42 @@ class RSession(AbstractSession):
pkgdir = self.getpkgdir(args[0])
done_dict = {}
nodes = init_hosts(reporter, sshhosts, pkgdir,
rsync_roots, remotepython, remote_options=remote_options.d,
optimise_localhost=self.optimise_localhost, done_dict=done_dict)
reporter(report.RsyncFinished())
hostopts = HostOptions(rsync_roots=rsync_roots,
remote_python=remotepython,
optimise_localhost=self.optimise_localhost)
hostmanager = HostManager(sshhosts, self.config, pkgdir, hostopts)
try:
self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict)
finally:
teardown_hosts(reporter, [node.channel for node in nodes], nodes,
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
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:
@ -221,10 +194,7 @@ class RSession(AbstractSession):
sshhosts = [HostInfo(i) for i in
self.config.getvalue("disthosts")]
parse_directories(sshhosts)
try:
remotepython = self.config.getvalue("dist_remotepython")
except:
remotepython = None
remotepython = self.config.getvalue("dist_remotepython")
return sshhosts, remotepython, rsync_roots
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):
# 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
if not self.config.option.nomagic:
py.magic.invoke(assertion=1)
session_options.bind_config(self.config)
reporter, startserverflag = self.init_reporter(reporter,
sshhosts, LocalReporter, args[0])
reporter, checkfun = self.wrap_reporter(reporter)
@ -321,4 +290,8 @@ class LSession(AbstractSession):
self.tracer = Tracer(self.docstorage)
return apigen_runner
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
from py.__.test.rsession.executor import RunExecutor, BoxExecutor, AsyncExecutor
from py.__.test.rsession.outcome import Outcome
import thread
import os
class Info:
pid = None
class PidInfo(object):
""" 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):
def __init__(self, rootcollector, executor=AsyncExecutor):
def __init__(self, rootcollector, config, pidinfo, executor=AsyncExecutor):
self.rootcollector = rootcollector
self.config = config
self.executor = executor
self.pidinfo = pidinfo
def execute(self, itemspec):
item = self.rootcollector.getitembynames(itemspec)
#if isinstance(item, py.test.Function):
# ex = Executor(item.obj, setup=item.setup)
#else:
ex = self.executor(item)
ex = self.executor(item, config=self.config)
if self.executor is AsyncExecutor:
cont, pid = ex.execute()
self.pidinfo.set_pid(pid)
else:
# for tests only
return ex.execute()
Info.pid = pid
return cont()
return cont(self.pidinfo.waitandclear)
def run(self, itemspec):
#outcome = self.execute(itemspec)
@ -35,9 +72,9 @@ class SlaveNode(object):
if self.executor.wraps:
return outcome
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
assert os.path.exists(path)
path = os.path.abspath(path)
@ -47,7 +84,7 @@ def slave_main(receive, send, path, info = None):
if node is not None:
return node
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
while 1:
nextitem = receive()
@ -62,8 +99,7 @@ def slave_main(receive, send, path, info = None):
excinfo = py.code.ExceptionInfo()
send(Outcome(excinfo=excinfo, is_critical=True).make_repr())
else:
from py.__.test.rsession.rsession import remote_options
if not res[0] and not res[3] and remote_options.exitfirst:
if not res[0] and not res[3] and config.option.exitfirst:
# we're finished, but need to eat what we can
send(res)
break
@ -71,43 +107,42 @@ def slave_main(receive, send, path, info = None):
while nextitem is not None:
nextitem = receive()
def setup():
def callback_gen(queue):
from py.__.test.rsession.slave import Info
def callback_gen(channel, queue, info):
def callback(item):
if item == 42: # magic call-cleanup
# XXX should kill a pid here
if Info.pid:
os.kill(Info.pid, 15)
info.kill()
channel.close()
sys.exit(0)
queue.put(item)
return callback
import os, sys
from Queue import Queue
pkgdir = channel.receive() # path is ready
options = channel.receive() # options stuff, should be dictionary
config_repr = channel.receive()
basedir = os.path.dirname(pkgdir)
pkgname = os.path.basename(pkgdir)
# setup defaults...
sys.path.insert(0, basedir)
import py
options['we_are_remote'] = True
from py.__.test.rsession.rsession import RemoteOptions
from py.__.test.rsession.slave import Info
Info.pid = 0
from py.__.test.rsession import rsession
rsession.remote_options = RemoteOptions(options)
# XXX the following assumes that py lib is there, a bit
# much of an assumtion
if not rsession.remote_options.nomagic:
config = py.test.config
if config._initialized:
config = config._reparse([basedir])
config.merge_repr(config_repr)
else:
config.initdirect(basedir, config_repr)
#config.conftest.lget('adddefaultoptions')()
if not config.option.nomagic:
py.magic.invoke(assertion=1)
mod = __import__(pkgname)
assert py.path.local(mod.__file__).dirpath() == py.path.local(pkgdir)
from py.__.test.rsession.slave import slave_main
queue = Queue()
channel.setcallback(callback_gen(queue))
slave_main(queue.get, channel.send, basedir)
if not rsession.remote_options.nomagic:
from py.__.test.rsession.slave import slave_main, PidInfo
queue = py.std.Queue.Queue()
pidinfo = PidInfo()
channel.setcallback(callback_gen(channel, queue, pidinfo))
slave_main(queue.get, channel.send, basedir, config, pidinfo)
if not config.option.nomagic:
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.testing import example2
from py.__.test.rsession.conftest import option
def setup_module(mod):
from py.__.test.rsession.rsession import remote_options
remote_options['nice_level'] = 0
mod.rootdir = py.path.local(py.__file__).dirpath().dirpath()
mod.config = py.test.config._reparse([mod.rootdir])
def test_basic_boxing():
# XXX: because we do not have option transfer
## if not hasattr(option, 'nocapture') or not option.nocapture:
## 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()
assert b.stdoutrepr == "some out\n"
assert b.stderrrepr == "some err\n"
@ -28,7 +27,7 @@ def test_basic_boxing():
assert b.retval == 1
def test_boxing_on_fds():
b = Box(example2.boxf2)
b = Box(example2.boxf2, config=config)
b.run()
assert b.stdoutrepr == "someout"
assert b.stderrrepr == "someerr"
@ -37,13 +36,13 @@ def test_boxing_on_fds():
assert b.retval == 2
def test_boxing_signal():
b = Box(example2.boxseg)
b = Box(example2.boxseg, config=config)
b.run()
assert b.signal == 11
assert b.retval is None
def test_boxing_huge_data():
b = Box(example2.boxhuge)
b = Box(example2.boxhuge, config=config)
b.run()
assert b.stdoutrepr
assert b.exitstat == 0
@ -53,7 +52,7 @@ def test_boxing_huge_data():
def test_box_seq():
# we run many boxes with huge data, just one after another
for i in xrange(100):
b = Box(example2.boxhuge)
b = Box(example2.boxhuge, config=config)
b.run()
assert b.stdoutrepr
assert b.exitstat == 0
@ -62,13 +61,13 @@ def test_box_seq():
def test_box_in_a_box():
def boxfun():
b = Box(example2.boxf2)
b = Box(example2.boxf2, config=config)
b.run()
print b.stdoutrepr
print >>sys.stderr, b.stderrrepr
return b.retval
b = Box(boxfun)
b = Box(boxfun, config=config)
b.run()
assert b.stdoutrepr == "someout\n"
assert b.stderrrepr == "someerr\n"
@ -77,8 +76,6 @@ def test_box_in_a_box():
assert b.retval == 2
def test_box_killer():
if option.skip_kill_test:
py.test.skip('skipping kill test')
class A:
pass
info = A()
@ -87,7 +84,7 @@ def test_box_killer():
def box_fun():
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)
os.kill(pid, 15)
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):
mod.rootdir = py.path.local(py.__file__).dirpath().dirpath()
from py.__.test.rsession.rsession import remote_options
remote_options['nice_level'] = 0
mod.config = py.test.config._reparse([mod.rootdir])
def XXXtest_executor_passing_function():
ex = Executor(example1.f1)
@ -61,32 +60,32 @@ class ItemTestSkipping(py.test.Item):
py.test.skip("hello")
def test_run_executor():
ex = RunExecutor(ItemTestPassing("pass"))
ex = RunExecutor(ItemTestPassing("pass"), config=config)
outcome = ex.execute()
assert outcome.passed
ex = RunExecutor(ItemTestFailing("fail"))
ex = RunExecutor(ItemTestFailing("fail"), config=config)
outcome = ex.execute()
assert not outcome.passed
ex = RunExecutor(ItemTestSkipping("skip"))
ex = RunExecutor(ItemTestSkipping("skip"), config=config)
outcome = ex.execute()
assert outcome.skipped
assert not outcome.passed
assert not outcome.excinfo
def test_box_executor():
ex = BoxExecutor(ItemTestPassing("pass"))
ex = BoxExecutor(ItemTestPassing("pass"), config=config)
outcome_repr = ex.execute()
outcome = ReprOutcome(outcome_repr)
assert outcome.passed
ex = BoxExecutor(ItemTestFailing("fail"))
ex = BoxExecutor(ItemTestFailing("fail"), config=config)
outcome_repr = ex.execute()
outcome = ReprOutcome(outcome_repr)
assert not outcome.passed
ex = BoxExecutor(ItemTestSkipping("skip"))
ex = BoxExecutor(ItemTestSkipping("skip"), config=config)
outcome_repr = ex.execute()
outcome = ReprOutcome(outcome_repr)
assert outcome.skipped
@ -96,7 +95,7 @@ def test_box_executor():
def test_box_executor_stdout():
rootcol = py.test.collect.Directory(rootdir)
item = rootcol.getitembynames(funcprint_spec)
ex = BoxExecutor(item)
ex = BoxExecutor(item, config=config)
outcome_repr = ex.execute()
outcome = ReprOutcome(outcome_repr)
assert outcome.passed
@ -105,7 +104,7 @@ def test_box_executor_stdout():
def test_box_executor_stdout_error():
rootcol = py.test.collect.Directory(rootdir)
item = rootcol.getitembynames(funcprintfail_spec)
ex = BoxExecutor(item)
ex = BoxExecutor(item, config=config)
outcome_repr = ex.execute()
outcome = ReprOutcome(outcome_repr)
assert not outcome.passed
@ -114,7 +113,7 @@ def test_box_executor_stdout_error():
def test_cont_executor():
rootcol = py.test.collect.Directory(rootdir)
item = rootcol.getitembynames(funcprintfail_spec)
ex = AsyncExecutor(item)
ex = AsyncExecutor(item, config=config)
cont, pid = ex.execute()
assert pid
outcome_repr = cont()

View File

@ -5,7 +5,7 @@
import py
from py.__.test.rsession.rsession import LSession
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):
mod.tmp = py.test.ensuretemp("lsession_module")
@ -82,22 +82,26 @@ class TestLSession(object):
l = []
def some_fun(*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:
lsession.main(reporter=allevents.append, runner=plain_runner)
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
post_mortem = pdb.post_mortem
pdb.post_mortem = some_fun
args = [str(tmpdir.join(subdir)), '--pdb']
config = py.test.config._reparse(args)
lsession = LSession(config)
allevents = []
try:
lsession.main(reporter=allevents.append, runner=plain_runner)
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):
if not hasattr(py.std.os, 'fork'):
@ -262,3 +266,31 @@ class TestLSession(object):
assert testevents[0].outcome.passed
assert testevents[0].outcome.stderr == ""
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.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.rsession import session_options, remote_options
from py.__.test.rsession.hostmanage import HostInfo
def setup_module(mod):
# bind an empty config
config = py.test.config._reparse([])
config.option.max_tasks_per_node = 10
session_options.bind_config(config)
#assert not remote_options.exitfirst
config._overwrite('dist_taskspernode', 10)
mod.pkgdir = py.path.local(py.__file__).dirpath()
class DummyGateway(object):
@ -92,11 +89,12 @@ def test_dispatch_loop():
def test_slave_setup():
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)
output = ReprOutcome(channel.receive())
assert output.passed
channel.send(None)
channel.send(42)
channel.waitclose(10)
gw.exit()
@ -114,7 +112,8 @@ def test_slave_running():
gw = py.execnet.PopenGateway()
gw.host = HostInfo("localhost")
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, {})
return mn
@ -127,6 +126,34 @@ def test_slave_running():
shouldstop = lambda : False
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():
d = {}
gen = randomgen({1:True, 2:True, 3:True}, d)

View File

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

View File

@ -166,11 +166,12 @@ class AbstractTestReporter(object):
r.report(report.HostReady(hosts[2]))
out, err = cap.reset()
assert not err
expected = """================= Test started, hosts: host1, host2, host3 ==================
host1: READY (still 2 to go)
expected1 = "Test started, hosts: host1, host2, host3"
expected2 = """host1: READY (still 2 to go)
host2: READY (still 1 to go)
host3: READY"""
assert out.find(expected) != -1
assert out.find(expected1) != -1
assert out.find(expected2) != -1
class TestLocalReporter(AbstractTestReporter):
reporter = LocalReporter
@ -181,8 +182,9 @@ class TestLocalReporter(AbstractTestReporter):
def test_module(self):
#py.test.skip("XXX rewrite test to not rely on exact formatting")
assert self._test_module().endswith("test_slave.py[9] FsF."),\
self._test_module()
output = self._test_module()
assert output.find("test_slave") != -1
assert output.endswith("FsF."), output
def test_full_module(self):
#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):
#py.test.skip("XXX rewrite test to not rely on exact formatting")
val = self.report_received_item_outcome()
expected = """ localhost: FAILED 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: PASSED 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: FAILED py.test.rsession.testing.test_slave.py funcpass
localhost: PASSED py.test.rsession.testing.test_slave.py funcpass
"""
assert val.find(expected) != -1
def test_module(self):
val = self._test_module()
print val
expected = """ localhost: FAILED 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: PASSED 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: FAILED py.test.rsession.testing.test_slave.py funcpass
localhost: PASSED py.test.rsession.testing.test_slave.py funcpass
"""
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.__.test.rsession.hostmanage import HostInfo
py.test.skip("This tests are not really testing, needs rewrite")
class RestTestReporter(RestReporter):
def __init__(self, *args, **kwargs):
if args:

View File

@ -5,12 +5,12 @@
import py
from py.__.test.rsession import report
from py.__.test.rsession.rsession import RSession, parse_directories,\
session_options, remote_options, parse_directories
from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts,\
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, funcoption_spec
funcoptioncustom_spec
def setup_module(mod):
mod.pkgdir = py.path.local(py.__file__).dirpath()
@ -18,7 +18,8 @@ def setup_module(mod):
def test_setup_non_existing_hosts():
setup_events = []
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)
#assert setup_events
@ -105,7 +106,7 @@ class TestRSessionRemote:
tmpdir = py.test.ensuretemp("example_distribution")
tmpdir.ensure(subdir, "conftest.py").write(py.code.Source("""
disthosts = [%r]
distrsync_roots = ["%s"]
distrsync_roots = ["%s", "py"]
""" % ('localhost', subdir)))
tmpdir.ensure(subdir, "__init__.py")
tmpdir.ensure(subdir, "test_one.py").write(py.code.Source("""
@ -119,7 +120,11 @@ class TestRSessionRemote:
pass
def test_5():
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))]
config = py.test.config._reparse(args)
rsession = RSession(config, optimise_localhost=False)
@ -131,21 +136,21 @@ class TestRSessionRemote:
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) == 5
assert len(passevents) == 2
assert len(testevents) == 6
assert len(passevents) == 3
assert len(failevents) == 3
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 failevents[0].outcome.excinfo.typename == 'AssertionError'
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 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 tb[0].path.find("executor") != -1
assert str(tb[0].path).find("executor") != -1
assert tb[0].source.find("execute") != -1
def test_setup_teardown_ssh(self):
@ -155,10 +160,10 @@ class TestRSessionRemote:
teardown_events = []
config = py.test.config._reparse([])
session_options.bind_config(config)
nodes = init_hosts(setup_events.append, hosts, pkgdir,
rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d)
teardown_hosts(teardown_events.append,
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
@ -182,9 +187,9 @@ class TestRSessionRemote:
allevents = []
config = py.test.config._reparse([])
session_options.bind_config(config)
nodes = init_hosts(allevents.append, hosts, pkgdir,
rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d)
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
@ -202,7 +207,7 @@ class TestRSessionRemote:
node.send(itemskip)
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
if isinstance(i, report.ReceivedItemOutcome)]
@ -224,31 +229,33 @@ class TestRSessionRemote:
hosts = [HostInfo('localhost')]
parse_directories(hosts)
config = py.test.config._reparse([])
session_options.bind_config(config)
d = remote_options.d.copy()
d['custom'] = 'custom'
nodes = init_hosts(allevents.append, hosts, pkgdir,
rsync_roots=["py"], remote_options=d,
optimise_localhost=False)
rootcol = py.test.collect.Directory(pkgdir.dirpath())
itempass = rootcol.getitembynames(funcoption_spec)
itempassaswell = rootcol.getitembynames(funcoptioncustom_spec)
for node in nodes:
node.send(itempass)
node.send(itempassaswell)
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) == 0
assert len(events) == len(passed)
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
@ -258,14 +265,16 @@ class TestRSessionRemote:
parse_directories(hosts)
tmpdir = py.test.ensuretemp("nice")
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():
import os
assert os.nice(0) == 10
""")
config = py.test.config._reparse([tmpdir])
config.option.nice_level = 10
rsession = RSession(config)
allevents = []
rsession.main(reporter=allevents.append)
@ -298,7 +307,10 @@ class TestInithosts(object):
hostnames = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home']
hosts = [HostInfo(i) for i in hostnames]
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)]
assert len(events) == 4
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). """
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
import py, sys
@ -11,10 +11,7 @@ if sys.platform == 'win32':
py.test.skip("rsession is unsupported on Windows.")
def setup_module(module):
from py.__.test.rsession.rsession import session_options
module.rootdir = py.path.local(py.__file__).dirpath().dirpath()
config = py.test.config._reparse([])
session_options.bind_config(config)
# ----------------------------------------------------------------------
# inlined testing functions used below
@ -34,13 +31,8 @@ def funcprintfail():
print "samfing elz"
asddsa
def funcoption():
from py.__.test.rsession.rsession import remote_options
assert remote_options.we_are_remote
def funcoptioncustom():
from py.__.test.rsession.rsession import remote_options
assert remote_options.custom == "custom"
assert py.test.config.getvalue("custom")
def funchang():
import time
@ -52,7 +44,6 @@ funcfail_spec = (BASE + "funcfail").split("/")
funcskip_spec = (BASE + "funcskip").split("/")
funcprint_spec = (BASE + "funcprint").split("/")
funcprintfail_spec = (BASE + "funcprintfail").split("/")
funcoption_spec = (BASE + "funcoption").split("/")
funcoptioncustom_spec = (BASE + "funcoptioncustom").split("/")
funchang_spec = (BASE + "funchang").split("/")
mod_spec = BASE[:-1].split("/")
@ -63,7 +54,9 @@ from py.__.test.rsession.executor import RunExecutor
def gettestnode():
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
def test_slave_run_passing():
@ -116,7 +109,9 @@ def test_slave_main_simple():
funcpass_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
res_repr = [ReprOutcome(r) for r in res]
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())
def test_slave_setup_fails_on_import_error():
from py.__.test.rsession.slave import setup
tmp = py.test.ensuretemp("slavesetup")
config = py.test.config._reparse([tmp])
class C:
def __init__(self):
self.count = 0
@ -136,12 +131,15 @@ def test_slave_setup_fails_on_import_error():
if self.count == 0:
retval = str(tmp)
elif self.count == 1:
from py.__.test.rsession.rsession import remote_options
retval = remote_options.d
retval = config.make_repr(conftestnames=['dist_nicelevel'])
else:
raise NotImplementedError("more data")
self.count += 1
return retval
def close(self):
pass
try:
exec py.code.Source(setup, "setup()").compile() in {
'channel': C()}
@ -153,16 +151,14 @@ def test_slave_setup_fails_on_import_error():
def test_slave_setup_exit():
tmp = py.test.ensuretemp("slaveexit")
tmp.ensure("__init__.py")
from py.__.test.rsession.slave import setup
from Queue import Queue
q = Queue()
q = py.std.Queue.Queue()
config = py.test.config._reparse([tmp])
class C:
res = []
def __init__(self):
from py.__.test.rsession.rsession import remote_options
self.q = [str(tmp),
remote_options.d,
config.make_repr(conftestnames=['dist_nicelevel']),
funchang_spec,
42,
funcpass_spec]
@ -178,6 +174,9 @@ def test_slave_setup_exit():
callback(self.q.pop())
f()
#thread.start_new_thread(f, ())
def close(self):
pass
send = res.append
try:
@ -188,8 +187,8 @@ def test_slave_setup_exit():
py.test.fail("Did not exit")
def test_slave_setup_fails_on_missing_pkg():
from py.__.test.rsession.slave import setup
tmp = py.test.ensuretemp("slavesetup2")
config = py.test.config._reparse([tmp])
x = tmp.ensure("sometestpackage", "__init__.py")
class C:
def __init__(self):
@ -199,8 +198,7 @@ def test_slave_setup_fails_on_missing_pkg():
if self.count == 0:
retval = str(x.dirpath())
elif self.count == 1:
from py.__.test.rsession.rsession import remote_options
retval = remote_options.d
retval = config.make_repr(conftestnames=['dist_nicelevel'])
else:
raise NotImplementedError("more data")
self.count += 1
@ -223,3 +221,19 @@ def test_slave_setup_fails_on_missing_pkg():
else:
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):
config = py.test.config._reparse([])
from py.__.test.rsession.rsession import session_options
session_options.bind_config(config)
session_options.import_pypy = True
config._overwrite('_dist_import_pypy', True)
from py.__.test.rsession.web import TestHandler as _TestHandler
from py.__.test.rsession.web import MultiQueue
mod._TestHandler = _TestHandler

View File

@ -23,9 +23,7 @@ def setup_module(mod):
dom.window = dom.Window(html)
dom.document = dom.window.document
config = py.test.config._reparse([])
from py.__.test.rsession.rsession import session_options
session_options.bind_config(config)
session_options.import_pypy = True
config._overwrite('_dist_import_pypy', True)
from py.__.test.rsession import webjs
from py.__.test.rsession.web import exported_methods
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 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 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"]
"show_host", "hide_host", "hide_messagebox", "opt_scroll"]
try:
try:
if not session_options.import_pypy:
if not py.test.config.getvalue('_dist_import_pypy'):
raise ImportError
except AttributeError:
pass
@ -416,10 +416,10 @@ def start_server(server_address = ('', 8000), handler=TestHandler, start_new=Tru
if start_new:
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
else:
print "Server started, listening on %s" % (server_address,)
print "Server started, listening on port %d" % (httpd.server_port,)
httpd.serve_forever()
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>
<td><a href="#aftermessage">End of Traceback</a></td>
</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>
</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()
class Options(object):
""" Store global options
"""
def __init__(self):
self.scroll = True
opts = Options()
def comeback(msglist):
if len(msglist) == 0:
return
@ -62,11 +70,29 @@ def hide_info():
info = dom.document.getElementById("info")
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
def opt_scroll():
if opts.scroll:
opts.scroll = False
else:
opts.scroll = True
def scroll_down_if_needed(mbox):
if dom.window.scrollMaxY - dom.window.scrollY < SCROLL_LINES:
mbox.parentNode.scrollIntoView()
if not opts.scroll:
return
#if dom.window.scrollMaxY - dom.window.scrollY < SCROLL_LINES:
mbox.parentNode.scrollIntoView()
def hide_messagebox():
mbox = dom.document.getElementById("messagebox")
@ -175,6 +201,7 @@ def process(msg):
add_received_item_outcome(msg, module_part)
elif msg['type'] == 'TestFinished':
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.getElementById("Tests").childNodes[0].nodeValue = \
"Tests [%s]" % text
@ -202,6 +229,10 @@ def process(msg):
module_part.appendChild(tr)
elif msg['type'] == 'RsyncFinished':
glob.rsync_done = True
elif msg['type'] == 'InterruptedExecution':
show_interrupt()
elif msg['type'] == 'CrashedExecution':
show_crash()
if glob.data_empty:
mbox = dom.document.getElementById('messagebox')
scroll_down_if_needed(mbox)
@ -262,6 +293,8 @@ def hide_host():
glob.host = ""
def update_rsync():
if glob.finished:
return
elem = dom.document.getElementById("Tests")
if glob.rsync_done is True:
elem.childNodes[0].nodeValue = "Tests"
@ -294,11 +327,23 @@ def host_init(host_dict):
for key in host_dict.keys():
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):
glob.sessid = id
exported_methods.show_all_statuses(id, comeback)
def main():
glob.finished = False
exported_methods.show_hosts(host_init)
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):
""" main loop for running tests. """
colitems = self._map2colitems(self.config.remaining)
colitems = self.config.getcolitems()
try:
self.header(colitems)
try:
for colitem in colitems:
colitem.option = self.config.option
self.runtraced(colitem)
except KeyboardInterrupt:
raise
@ -53,9 +52,6 @@ class Session(object):
self.footer(colitems)
except Exit, ex:
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):
if self.shouldclose():
@ -91,7 +87,7 @@ class Session(object):
if self.config.option.collectonly and isinstance(colitem, py.test.Item):
return
if isinstance(colitem, py.test.Item):
self.skipbykeyword(colitem)
colitem.skipbykeyword(self.config.option.keyword)
res = colitem.run()
if res is None:
return py.test.Item.Passed()
@ -110,39 +106,6 @@ class Session(object):
finish()
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):
""" for immediate program exits without tracebacks and reporter/summary. """
def __init__(self, msg="unknown reason", item=None):

View File

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

View File

@ -29,46 +29,6 @@ def checkpyfilechange(rootdir, statcache={}):
changed = True
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):
l = []
for rootpath, names in failures:
@ -91,61 +51,77 @@ def getfailureitems(failures):
l.append(current)
return l
def failure_master(executable, out, args, failures):
gw = py.execnet.PopenGateway(executable)
channel = gw.remote_exec("""
from py.__.test.terminal.remote import failure_slave
failure_slave(channel)
""", stdout=out, stderr=out)
channel.send((args, failures))
return waitfinish(channel)
class RemoteTerminalSession(object):
def __init__(self, config, file=None):
self.config = config
self._setexecutable()
if file is None:
file = py.std.sys.stdout
self._file = file
self.out = getout(file)
def generalize(p1, p2):
general = p1
for x, y in zip(p1.parts(), p2.parts()):
if x != y:
break
general = x
return general
def _setexecutable(self):
name = self.config.option.executable
if name is None:
executable = py.std.sys.executable
else:
executable = py.path.local.sysfind(name)
assert executable is not None, executable
self.executable = executable
def getrootdir(args):
tops = []
for arg in args:
p = py.path.local(arg)
tops.append(p.pypkgpath() or p)
p = reduce(generalize, tops)
if p.check(file=1):
p = p.dirpath()
return p
def main(self):
rootdir = self.config.topdir
wasfailing = False
failures = []
while 1:
if self.config.option.looponfailing and (failures or not wasfailing):
while not checkpyfilechange(rootdir):
py.std.time.sleep(0.4)
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):
""" testing process and output happens at a remote place. """
assert file
if hasattr(file, 'write'):
def out(data):
file.write(data)
file.flush()
else:
out = file
failures = []
args = list(args)
rootdir = getrootdir(config.remaining)
#print "rootdir", rootdir
wasfailing = False
while 1:
if config.option.looponfailing and (failures or not wasfailing):
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 run_remote_session(self, failures):
print "* opening PopenGateway: ", self.executable
gw = py.execnet.PopenGateway(self.executable)
channel = gw.remote_exec("""
from py.__.test.terminal.remote import slaverun_TerminalSession
slaverun_TerminalSession(channel)
""", stdout=self.out, stderr=self.out)
print "MASTER: triggered slave terminal session ->"
repr = self.config.make_repr(conftestnames=[])
channel.send((str(self.config.topdir), repr, failures))
print "MASTER: send start info"
try:
return channel.receive()
except channel.RemoteError, e:
print e
return []
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
from py.__.test.terminal.out import getout
import py.__.code.safe_repr
from py.__.test.representation import Presenter
def getrelpath(source, dest):
base = source.common(dest)
@ -25,15 +26,8 @@ class TerminalSession(Session):
file = py.std.sys.stdout
self._file = file
self.out = getout(file)
self._started = {}
self._opencollectors = []
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()
self.presenter = Presenter(self.out, config)
# ---------------------
# PROGRESS information
@ -128,10 +122,10 @@ class TerminalSession(Session):
for name in 'looponfailing', 'exitfirst', 'nomagic':
if getattr(option, name):
modes.append(name)
if option._fromremote:
modes.insert(0, 'child process')
else:
modes.insert(0, 'inprocess')
#if self._isremoteoption._fromremote:
# modes.insert(0, 'child process')
#else:
# modes.insert(0, 'inprocess')
mode = "/".join(modes)
self.out.line("testing-mode: %s" % mode)
self.out.line("executable: %s (%s)" %
@ -146,7 +140,7 @@ class TerminalSession(Session):
self.out.line("test target: %s" %(x.fspath,))
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__))
#for i, x in py.builtin.enumerate(py.test.config.configpaths):
@ -279,173 +273,8 @@ class TerminalSession(Session):
if not traceback:
self.out.line("empty traceback from item %r" % (item,))
return
handler = getattr(self, 'repr_failure_tb%s' % self.config.option.tbstyle)
handler(item, excinfo, traceback)
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)
handler = getattr(self.presenter, 'repr_failure_tb%s' % self.config.option.tbstyle)
handler(item, excinfo, traceback, lambda : self.repr_out_err(item))
def repr_out_err(self, colitem):
for parent in colitem.listchain():
@ -453,23 +282,6 @@ class TerminalSession(Session):
if obj:
self.out.sep("- ", "%s: recorded std%s" % (parent.name, name))
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():
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)
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():
col1 = py.test.collect.Directory(datadir.dirpath())
col2 = col1.join(datadir.basename)
@ -203,10 +191,10 @@ def test_custom_python_collection_from_conftest():
assert 23 == 23
""")
from py.__.test.collect import getfscollector
for x in (o, checkfile, checkfile.dirpath()):
config = py.test.config._reparse([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 items[1].__class__.__name__ == 'MyFunction'
@ -215,7 +203,7 @@ def test_custom_python_collection_from_conftest():
try:
config = py.test.config._reparse([])
out = py.std.cStringIO.StringIO()
session = config.getsessionclass()(config, out)
session = config._getsessionclass()(config, out)
session.main()
l = session.getitemoutcomepairs(py.test.Item.Passed)
assert len(l) == 2
@ -225,7 +213,7 @@ def test_custom_python_collection_from_conftest():
# test that running the file directly works
config = py.test.config._reparse([str(checkfile)])
out = py.std.cStringIO.StringIO()
session = config.getsessionclass()(config, out)
session = config._getsessionclass()(config, out)
session.main()
l = session.getitemoutcomepairs(py.test.Item.Passed)
assert len(l) == 2
@ -250,10 +238,10 @@ def test_custom_NONpython_collection_from_conftest():
""")
checkfile = o.ensure('somedir', 'moredir', 'check_something.txt')
from py.__.test.collect import getfscollector
for x in (o, checkfile, checkfile.dirpath()):
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 items[1].__class__.__name__ == 'MyFunction'
@ -262,7 +250,7 @@ def test_custom_NONpython_collection_from_conftest():
try:
config = py.test.config._reparse([])
out = py.std.cStringIO.StringIO()
session = config.getsessionclass()(config, out)
session = config._getsessionclass()(config, out)
session.main()
l = session.getitemoutcomepairs(py.test.Item.Passed)
assert len(l) == 1
@ -272,12 +260,14 @@ def test_custom_NONpython_collection_from_conftest():
# test that running the file directly works
config = py.test.config._reparse([str(checkfile)])
out = py.std.cStringIO.StringIO()
session = config.getsessionclass()(config, out)
session = config._getsessionclass()(config, out)
session.main()
l = session.getitemoutcomepairs(py.test.Item.Passed)
assert len(l) == 1
def test_order_of_execution_generator_same_codeline():
if py.test.config.is_boxed():
py.test.skip("Does not work with boxing")
test_list = []
expected_list = range(6)
@ -295,6 +285,8 @@ def test_order_of_execution_generator_same_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 = []
expected_list = range(3)

View File

@ -1,6 +1,8 @@
from __future__ import generators
import py
from py.__.test.config import gettopdir
def test_tmpdir():
d1 = py.test.ensuretemp('hello')
d2 = py.test.ensuretemp('hello')
@ -72,3 +74,266 @@ def test_siblingconftest_fails_maybe():
print py.process.cmdexec("py.test")
finally:
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/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):
conftest = Conftest()
snap0 = len(conftest._path2confmods)
@ -25,40 +30,40 @@ class TestConftestValueAccessGlobal:
def test_default_Module_setting_is_visible_always(self):
for path in self.basedir.parts():
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
def test_default_has_lower_prio(self):
conftest = Conftest(self.basedir.join("adir"))
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):
conftest = Conftest(self.basedir)
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):
conftest = Conftest(self.basedir)
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.lget("a", self.basedir.join('adir', 'b')) == 1
assert conftest.lget("b", self.basedir.join('adir', 'b')) == 2
assert py.test.raises(KeyError,
'conftest.lget("b", self.basedir.join("a"))'
)
#assert conftest.lget("a", self.basedir.join('adir', 'b')) == 1
#assert conftest.lget("b", self.basedir.join('adir', 'b')) == 2
#assert py.test.raises(KeyError,
# 'conftest.lget("b", self.basedir.join("a"))'
#)
def test_value_access_with_init_one_conftest(self):
conftest = Conftest(self.basedir.join('adir'))
assert conftest.rget("a") == 1
assert conftest.lget("a") == 1
#assert conftest.lget("a") == 1
def test_value_access_with_init_two_conftests(self):
conftest = Conftest(self.basedir.join("adir", "b"))
conftest.rget("a") == 1.5
conftest.lget("a") == 1
conftest.lget("b") == 1
#conftest.lget("a") == 1
#conftest.lget("b") == 1
class TestConftestValueAccessInPackage(TestConftestValueAccessGlobal):
def setup_class(cls):

View File

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

View File

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

View File

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

View File

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

View File

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