[svn r62978] * introduce --hosts and --rsyncdirs optiosn
* re-sort option groups, disable some options for now * add docstrings to execnet gatewaymanage * streamline tests a bit * unify debugging and tracing --HG-- branch : trunk
This commit is contained in:
parent
2aea0a73e2
commit
b5a1f95856
|
@ -22,7 +22,6 @@ from py.__.test import event
|
|||
|
||||
|
||||
class GatewaySpec(object):
|
||||
type = "ssh"
|
||||
def __init__(self, spec, defaultjoinpath="pyexecnetcache"):
|
||||
if spec == "popen" or spec.startswith("popen:"):
|
||||
self.address = "popen"
|
||||
|
@ -42,6 +41,7 @@ class GatewaySpec(object):
|
|||
parts = spec.split(":", 1)
|
||||
self.address = parts.pop(0)
|
||||
self.joinpath = parts and parts.pop(0) or ""
|
||||
self.type = "ssh"
|
||||
if not self.joinpath and not self.inplacelocal():
|
||||
self.joinpath = defaultjoinpath
|
||||
|
||||
|
@ -60,8 +60,15 @@ class GatewaySpec(object):
|
|||
elif self.type == "ssh":
|
||||
gw = py.execnet.SshGateway(self.address, remotepython=python)
|
||||
if self.joinpath:
|
||||
channel = gw.remote_exec("import os ; os.chdir(channel.receive())")
|
||||
channel.send(self.joinpath)
|
||||
channel = gw.remote_exec("""
|
||||
import os
|
||||
path = %r
|
||||
try:
|
||||
os.chdir(path)
|
||||
except OSError:
|
||||
os.mkdir(path)
|
||||
os.chdir(path)
|
||||
""" % self.joinpath)
|
||||
if waitclose:
|
||||
channel.waitclose()
|
||||
else:
|
||||
|
@ -74,13 +81,16 @@ class MultiChannel:
|
|||
def __init__(self, channels):
|
||||
self._channels = channels
|
||||
|
||||
def receive(self):
|
||||
values = []
|
||||
def receive_items(self):
|
||||
items = []
|
||||
for ch in self._channels:
|
||||
values.append(ch.receive())
|
||||
return values
|
||||
items.append((ch, ch.receive()))
|
||||
return items
|
||||
|
||||
def wait(self):
|
||||
def receive(self):
|
||||
return [x[1] for x in self.receive_items()]
|
||||
|
||||
def waitclose(self):
|
||||
for ch in self._channels:
|
||||
ch.waitclose()
|
||||
|
||||
|
@ -91,8 +101,7 @@ class GatewayManager:
|
|||
self.spec2gateway[GatewaySpec(spec)] = None
|
||||
|
||||
def trace(self, msg):
|
||||
py._com.pyplugins.notify("trace_gatewaymanage", msg)
|
||||
#print "trace", msg
|
||||
py._com.pyplugins.notify("trace", "gatewaymanage", msg)
|
||||
|
||||
def makegateways(self):
|
||||
for spec, value in self.spec2gateway.items():
|
||||
|
@ -101,6 +110,9 @@ class GatewayManager:
|
|||
self.spec2gateway[spec] = spec.makegateway()
|
||||
|
||||
def multi_exec(self, source, inplacelocal=True):
|
||||
""" remote execute code on all gateways.
|
||||
@param inplacelocal=False: don't send code to inplacelocal hosts.
|
||||
"""
|
||||
source = py.code.Source(source)
|
||||
channels = []
|
||||
for spec, gw in self.spec2gateway.items():
|
||||
|
@ -109,10 +121,15 @@ class GatewayManager:
|
|||
return MultiChannel(channels)
|
||||
|
||||
def multi_chdir(self, basename, inplacelocal=True):
|
||||
""" perform a remote chdir to the given path, may be relative.
|
||||
@param inplacelocal=False: don't send code to inplacelocal hosts.
|
||||
"""
|
||||
self.multi_exec("import os ; os.chdir(%r)" % basename,
|
||||
inplacelocal=inplacelocal).wait()
|
||||
inplacelocal=inplacelocal).waitclose()
|
||||
|
||||
def rsync(self, source, notify=None, verbose=False, ignores=None):
|
||||
""" perform rsync to all remote hosts.
|
||||
"""
|
||||
rsync = HostRSync(source, verbose=verbose, ignores=ignores)
|
||||
added = False
|
||||
for spec, gateway in self.spec2gateway.items():
|
||||
|
|
|
@ -155,7 +155,11 @@ class TestGatewayManagerPopen:
|
|||
testdir.tmpdir.chdir()
|
||||
hellopath = testdir.tmpdir.mkdir("hello")
|
||||
hm.makegateways()
|
||||
l = hm.multi_exec("import os ; channel.send(os.getcwd())").receive()
|
||||
l = [x[1] for x in hm.multi_exec(
|
||||
"import os ; channel.send(os.getcwd())"
|
||||
).receive_items()
|
||||
]
|
||||
paths = [x[1] for x in l]
|
||||
assert l == [str(hellopath)] * 2
|
||||
py.test.raises(Exception, 'hm.multi_chdir("world", inplacelocal=False)')
|
||||
worldpath = hellopath.mkdir("world")
|
||||
|
@ -188,22 +192,25 @@ class TestGatewayManagerPopen:
|
|||
|
||||
from py.__.execnet.gwmanage import MultiChannel
|
||||
class TestMultiChannel:
|
||||
def test_multichannel_receive(self):
|
||||
def test_multichannel_receive_items(self):
|
||||
class pseudochannel:
|
||||
def receive(self):
|
||||
return 12
|
||||
multichannel = MultiChannel([pseudochannel(), pseudochannel()])
|
||||
l = multichannel.receive()
|
||||
assert len(l) == 2
|
||||
assert l == [12, 12]
|
||||
|
||||
def test_multichannel_wait(self):
|
||||
pc1 = pseudochannel()
|
||||
pc2 = pseudochannel()
|
||||
multichannel = MultiChannel([pc1, pc2])
|
||||
l = multichannel.receive_items()
|
||||
assert len(l) == 2
|
||||
assert l == [(pc1, 12), (pc2, 12)]
|
||||
|
||||
def test_multichannel_waitclose(self):
|
||||
l = []
|
||||
class pseudochannel:
|
||||
def waitclose(self):
|
||||
l.append(0)
|
||||
multichannel = MultiChannel([pseudochannel(), pseudochannel()])
|
||||
multichannel.wait()
|
||||
multichannel.waitclose()
|
||||
assert len(l) == 2
|
||||
|
||||
|
||||
|
|
|
@ -39,9 +39,17 @@ class Config(object):
|
|||
assert isinstance(pytestplugins, py.test._PytestPlugins)
|
||||
self.bus = pytestplugins.pyplugins
|
||||
self.pytestplugins = pytestplugins
|
||||
self._conftest = Conftest(onimport=self.pytestplugins.consider_conftest)
|
||||
self._conftest = Conftest(onimport=self._onimportconftest)
|
||||
self._setupstate = SetupState()
|
||||
|
||||
def _onimportconftest(self, conftestmodule):
|
||||
self.trace("loaded conftestmodule %r" %(conftestmodule,))
|
||||
self.pytestplugins.consider_conftest(conftestmodule)
|
||||
|
||||
def trace(self, msg):
|
||||
if getattr(self.option, 'traceconfig', None):
|
||||
self.bus.notify("trace", "config", msg)
|
||||
|
||||
def _processopt(self, opt):
|
||||
if hasattr(opt, 'default') and opt.dest:
|
||||
val = os.environ.get("PYTEST_OPTION_" + opt.dest.upper(), None)
|
||||
|
@ -119,21 +127,18 @@ class Config(object):
|
|||
col = Dir(pkgpath, config=self)
|
||||
return col._getfsnode(path)
|
||||
|
||||
def getvalue_pathlist(self, name, path=None):
|
||||
def getconftest_pathlist(self, name, path=None):
|
||||
""" return a matching value, which needs to be sequence
|
||||
of filenames that will be returned as a list of Path
|
||||
objects (they can be relative to the location
|
||||
where they were found).
|
||||
"""
|
||||
try:
|
||||
return getattr(self.option, name)
|
||||
except AttributeError:
|
||||
try:
|
||||
mod, relroots = self._conftest.rget_with_confmod(name, path)
|
||||
except KeyError:
|
||||
return None
|
||||
modpath = py.path.local(mod.__file__).dirpath()
|
||||
return [modpath.join(x, abs=True) for x in relroots]
|
||||
mod, relroots = self._conftest.rget_with_confmod(name, path)
|
||||
except KeyError:
|
||||
return None
|
||||
modpath = py.path.local(mod.__file__).dirpath()
|
||||
return [modpath.join(x, abs=True) for x in relroots]
|
||||
|
||||
def addoptions(self, groupname, *specs):
|
||||
""" add a named group of options to the current testing session.
|
||||
|
|
|
@ -44,9 +44,9 @@ class DSession(Session):
|
|||
def fixoptions(self):
|
||||
""" check, fix and determine conflicting options. """
|
||||
option = self.config.option
|
||||
if option.runbrowser and not option.startserver:
|
||||
#print "--runbrowser implies --startserver"
|
||||
option.startserver = True
|
||||
#if option.runbrowser and not option.startserver:
|
||||
# #print "--runbrowser implies --startserver"
|
||||
# option.startserver = True
|
||||
if self.config.getvalue("dist_boxed") and option.dist:
|
||||
option.boxed = True
|
||||
# conflicting options
|
||||
|
|
|
@ -8,19 +8,34 @@ def getconfighosts(config):
|
|||
if config.option.numprocesses:
|
||||
hosts = ['localhost'] * config.option.numprocesses
|
||||
else:
|
||||
hosts = config.getvalue("dist_hosts")
|
||||
assert hosts is not None
|
||||
hosts = config.option.hosts
|
||||
if not hosts:
|
||||
hosts = config.getvalue("hosts")
|
||||
else:
|
||||
hosts = hosts.split(",")
|
||||
assert hosts is not None
|
||||
return hosts
|
||||
|
||||
def getconfigroots(config):
|
||||
roots = config.option.rsyncdirs
|
||||
if roots:
|
||||
roots = [py.path.local(x) for x in roots.split(',')]
|
||||
else:
|
||||
roots = []
|
||||
conftestroots = config.getconftest_pathlist("rsyncdirs")
|
||||
if conftestroots:
|
||||
roots.extend(conftestroots)
|
||||
for root in roots:
|
||||
if not root.check():
|
||||
raise ValueError("rsyncdir doesn't exist: %r" %(root,))
|
||||
return roots
|
||||
|
||||
class HostManager(object):
|
||||
def __init__(self, config, hosts=None):
|
||||
self.config = config
|
||||
roots = self.config.getvalue_pathlist("rsyncroots")
|
||||
if not roots:
|
||||
roots = self.config.getvalue_pathlist("dist_rsync_roots")
|
||||
self.roots = roots
|
||||
if hosts is None:
|
||||
hosts = getconfighosts(self.config)
|
||||
self.roots = getconfigroots(config)
|
||||
self.gwmanager = GatewayManager(hosts)
|
||||
|
||||
def makegateways(self):
|
||||
|
@ -29,6 +44,19 @@ class HostManager(object):
|
|||
self.gwmanager.makegateways()
|
||||
finally:
|
||||
old.chdir()
|
||||
self.trace_hoststatus()
|
||||
|
||||
def trace_hoststatus(self):
|
||||
if self.config.option.debug:
|
||||
for ch, result in self.gwmanager.multi_exec("""
|
||||
import sys, os
|
||||
channel.send((sys.executable, os.getcwd(), sys.path))
|
||||
""").receive_items():
|
||||
self.trace("spec %r, execuable %r, cwd %r, syspath %r" %(
|
||||
ch.gateway.spec, result[0], result[1], result[2]))
|
||||
|
||||
def config_getignores(self):
|
||||
return self.config.getconftest_pathlist("rsyncignore")
|
||||
|
||||
def rsync_roots(self):
|
||||
""" make sure that all remote gateways
|
||||
|
@ -42,20 +70,23 @@ class HostManager(object):
|
|||
# (for other gateways this chdir is irrelevant)
|
||||
self.makegateways()
|
||||
options = {
|
||||
'ignores': self.config.getvalue_pathlist("dist_rsync_ignore"),
|
||||
'verbose': self.config.option.verbose
|
||||
'ignores': self.config_getignores(),
|
||||
'verbose': 1, # self.config.option.verbose
|
||||
}
|
||||
if self.roots:
|
||||
# send each rsync root
|
||||
for root in self.roots:
|
||||
self.gwmanager.rsync(root, **options)
|
||||
else:
|
||||
# we transfer our topdir as the root
|
||||
# but need to be careful regarding
|
||||
# we transfer our topdir as the root
|
||||
self.gwmanager.rsync(self.config.topdir, **options)
|
||||
# and cd into it
|
||||
self.gwmanager.multi_chdir(self.config.topdir.basename, inplacelocal=False)
|
||||
self.config.bus.notify("rsyncfinished", event.RsyncFinished())
|
||||
|
||||
def trace(self, msg):
|
||||
self.config.bus.notify("trace", "testhostmanage", msg)
|
||||
|
||||
def setup_hosts(self, putevent):
|
||||
self.rsync_roots()
|
||||
nice = self.config.getvalue("dist_nicelevel")
|
||||
|
@ -64,8 +95,10 @@ class HostManager(object):
|
|||
import os
|
||||
if hasattr(os, 'nice'):
|
||||
os.nice(%r)
|
||||
""" % nice).wait()
|
||||
|
||||
""" % nice).waitclose()
|
||||
|
||||
self.trace_hoststatus()
|
||||
|
||||
for host, gateway in self.gwmanager.spec2gateway.items():
|
||||
host.node = MasterNode(host,
|
||||
gateway,
|
||||
|
|
|
@ -39,7 +39,6 @@ class TestAsyncFunctional:
|
|||
])
|
||||
|
||||
def test_dist_some_tests(self, testdir):
|
||||
testdir.makepyfile(conftest="dist_hosts=['localhost']\n")
|
||||
p1 = testdir.makepyfile(test_one="""
|
||||
def test_1():
|
||||
pass
|
||||
|
@ -49,7 +48,7 @@ class TestAsyncFunctional:
|
|||
def test_fail():
|
||||
assert 0
|
||||
""")
|
||||
config = testdir.parseconfig('-d', p1)
|
||||
config = testdir.parseconfig('-d', p1, '--hosts=popen')
|
||||
dsession = DSession(config)
|
||||
eq = EventQueue(config.bus)
|
||||
dsession.main([config.getfsnode(p1)])
|
||||
|
@ -61,7 +60,7 @@ class TestAsyncFunctional:
|
|||
assert ev.failed
|
||||
# see that the host is really down
|
||||
ev, = eq.geteventargs("hostdown")
|
||||
assert ev.host.address == "localhost"
|
||||
assert ev.host.address == "popen"
|
||||
ev, = eq.geteventargs("testrunfinish")
|
||||
|
||||
def test_distribution_rsync_roots_example(self, testdir):
|
||||
|
@ -70,8 +69,8 @@ class TestAsyncFunctional:
|
|||
subdir = "sub_example_dist"
|
||||
sourcedir = self.tmpdir.mkdir("source")
|
||||
sourcedir.ensure(subdir, "conftest.py").write(py.code.Source("""
|
||||
dist_hosts = ["localhost:%s"]
|
||||
dist_rsync_roots = ["%s", "../py"]
|
||||
hosts = ["popen:%s"]
|
||||
rsyncdirs = ["%s", "../py"]
|
||||
""" % (destdir, tmpdir.join(subdir), )))
|
||||
tmpdir.ensure(subdir, "__init__.py")
|
||||
tmpdir.ensure(subdir, "test_one.py").write(py.code.Source("""
|
||||
|
@ -102,7 +101,6 @@ class TestAsyncFunctional:
|
|||
if not hasattr(os, 'nice'):
|
||||
py.test.skip("no os.nice() available")
|
||||
testdir.makepyfile(conftest="""
|
||||
dist_hosts=['localhost']
|
||||
dist_nicelevel = 10
|
||||
""")
|
||||
p1 = testdir.makepyfile("""
|
||||
|
@ -110,7 +108,7 @@ class TestAsyncFunctional:
|
|||
import os
|
||||
assert os.nice(0) == 10
|
||||
""")
|
||||
evrec = testdir.inline_run('-d', p1)
|
||||
evrec = testdir.inline_run('-d', p1, '--hosts=popen')
|
||||
ev = evrec.getreport('test_nice')
|
||||
assert ev.passed
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"""
|
||||
|
||||
import py
|
||||
from py.__.test.dsession.hostmanage import HostManager, getconfighosts
|
||||
from py.__.test.dsession.hostmanage import HostManager, getconfighosts, getconfigroots
|
||||
from py.__.execnet.gwmanage import GatewaySpec as Host
|
||||
|
||||
from py.__.test import event
|
||||
|
@ -15,12 +15,14 @@ def pytest_pyfuncarg_dest(pyfuncitem):
|
|||
return dest
|
||||
|
||||
class TestHostManager:
|
||||
def gethostmanager(self, source, dist_hosts, dist_rsync_roots=None):
|
||||
l = ["dist_hosts = %r" % dist_hosts]
|
||||
if dist_rsync_roots:
|
||||
l.append("dist_rsync_roots = %r" % dist_rsync_roots)
|
||||
source.join("conftest.py").write("\n".join(l))
|
||||
config = py.test.config._reparse([source])
|
||||
def gethostmanager(self, source, hosts, rsyncdirs=None):
|
||||
def opt(optname, l):
|
||||
return '%s=%s' % (optname, ",".join(map(str, l)))
|
||||
args = [opt('--hosts', hosts)]
|
||||
if rsyncdirs:
|
||||
args.append(opt('--rsyncdir', [source.join(x, abs=True) for x in rsyncdirs]))
|
||||
args.append(source)
|
||||
config = py.test.config._reparse(args)
|
||||
assert config.topdir == source
|
||||
hm = HostManager(config)
|
||||
assert hm.gwmanager.spec2gateway
|
||||
|
@ -34,7 +36,7 @@ class TestHostManager:
|
|||
def test_hostmanager_rsync_roots_no_roots(self, source, dest):
|
||||
source.ensure("dir1", "file1").write("hello")
|
||||
config = py.test.config._reparse([source])
|
||||
hm = HostManager(config, hosts=["localhost:%s" % dest])
|
||||
hm = HostManager(config, hosts=["popen:%s" % dest])
|
||||
assert hm.config.topdir == source == config.topdir
|
||||
hm.rsync_roots()
|
||||
p, = hm.gwmanager.multi_exec("import os ; channel.send(os.getcwd())").receive()
|
||||
|
@ -48,8 +50,8 @@ class TestHostManager:
|
|||
dir2 = source.ensure("dir1", "dir2", dir=1)
|
||||
dir2.ensure("hello")
|
||||
hm = self.gethostmanager(source,
|
||||
dist_hosts = ["localhost:%s" % dest],
|
||||
dist_rsync_roots = ['dir1']
|
||||
hosts = ["popen:%s" % dest],
|
||||
rsyncdirs = ['dir1']
|
||||
)
|
||||
assert hm.config.topdir == source
|
||||
hm.rsync_roots()
|
||||
|
@ -61,8 +63,8 @@ class TestHostManager:
|
|||
dir2 = source.ensure("dir1", "dir2", dir=1)
|
||||
dir2.ensure("hello")
|
||||
hm = self.gethostmanager(source,
|
||||
dist_hosts = ["localhost:%s" % dest],
|
||||
dist_rsync_roots = [str(source)]
|
||||
hosts = ["popen:%s" % dest],
|
||||
rsyncdirs = [str(source)]
|
||||
)
|
||||
assert hm.config.topdir == source
|
||||
hm.rsync_roots()
|
||||
|
@ -77,37 +79,37 @@ class TestHostManager:
|
|||
dir2.ensure("hello")
|
||||
source.ensure("bogusdir", "file")
|
||||
source.join("conftest.py").write(py.code.Source("""
|
||||
dist_rsync_roots = ['dir1/dir2']
|
||||
rsyncdirs = ['dir1/dir2']
|
||||
"""))
|
||||
session = py.test.config._reparse([source]).initsession()
|
||||
hm = HostManager(session.config,
|
||||
hosts=["localhost:" + str(dest)])
|
||||
hosts=["popen:" + str(dest)])
|
||||
hm.rsync_roots()
|
||||
assert dest.join("dir2").check()
|
||||
assert not dest.join("dir1").check()
|
||||
assert not dest.join("bogus").check()
|
||||
|
||||
def test_hostmanager_rsync_ignore(self, source, dest):
|
||||
def test_hostmanager_rsyncignore(self, source, dest):
|
||||
dir2 = source.ensure("dir1", "dir2", dir=1)
|
||||
dir5 = source.ensure("dir5", "dir6", "bogus")
|
||||
dirf = source.ensure("dir5", "file")
|
||||
dir2.ensure("hello")
|
||||
source.join("conftest.py").write(py.code.Source("""
|
||||
dist_rsync_ignore = ['dir1/dir2', 'dir5/dir6']
|
||||
dist_rsync_roots = ['dir1', 'dir5']
|
||||
rsyncdirs = ['dir1', 'dir5']
|
||||
rsyncignore = ['dir1/dir2', 'dir5/dir6']
|
||||
"""))
|
||||
session = py.test.config._reparse([source]).initsession()
|
||||
hm = HostManager(session.config,
|
||||
hosts=["localhost:" + str(dest)])
|
||||
hosts=["popen:" + str(dest)])
|
||||
hm.rsync_roots()
|
||||
assert dest.join("dir1").check()
|
||||
assert not dest.join("dir1", "dir2").check()
|
||||
assert dest.join("dir5","file").check()
|
||||
assert not dest.join("dir6").check()
|
||||
|
||||
def test_hostmanage_optimise_localhost(self, source, dest):
|
||||
hosts = ["localhost"] * 3
|
||||
source.join("conftest.py").write("dist_rsync_roots = ['a']")
|
||||
def test_hostmanage_optimise_popen(self, source, dest):
|
||||
hosts = ["popen"] * 3
|
||||
source.join("conftest.py").write("rsyncdirs = ['a']")
|
||||
source.ensure('a', dir=1)
|
||||
config = py.test.config._reparse([source])
|
||||
hm = HostManager(config, hosts=hosts)
|
||||
|
@ -116,31 +118,35 @@ class TestHostManager:
|
|||
assert gwspec.inplacelocal()
|
||||
assert not gwspec.joinpath
|
||||
|
||||
def test_hostmanage_setup_hosts(self, source):
|
||||
hosts = ["localhost"] * 3
|
||||
source.join("conftest.py").write("dist_rsync_roots = ['a']")
|
||||
def test_hostmanage_setup_hosts_DEBUG(self, source, EventRecorder):
|
||||
hosts = ["popen"] * 2
|
||||
source.join("conftest.py").write("rsyncdirs = ['a']")
|
||||
source.ensure('a', dir=1)
|
||||
config = py.test.config._reparse([source])
|
||||
config = py.test.config._reparse([source, '--debug'])
|
||||
assert config.option.debug
|
||||
hm = HostManager(config, hosts=hosts)
|
||||
queue = py.std.Queue.Queue()
|
||||
hm.setup_hosts(putevent=queue.put)
|
||||
evrec = EventRecorder(config.bus, debug=True)
|
||||
hm.setup_hosts(putevent=[].append)
|
||||
for host in hm.gwmanager.spec2gateway:
|
||||
eventcall = queue.get(timeout=2.0)
|
||||
name, args, kwargs = eventcall
|
||||
assert name == "hostup"
|
||||
for host in hm.gwmanager.spec2gateway:
|
||||
host.node.shutdown()
|
||||
for host in hm.gwmanager.spec2gateway:
|
||||
eventcall = queue.get(timeout=2.0)
|
||||
name, args, kwargs = eventcall
|
||||
print name, args, kwargs
|
||||
assert name == "hostdown"
|
||||
l = evrec.getnamed("trace")
|
||||
print evrec.events
|
||||
assert l
|
||||
hm.teardown_hosts()
|
||||
|
||||
def XXXtest_ssh_rsync_samehost_twice(self):
|
||||
#XXX we have no easy way to have a temp directory remotely!
|
||||
def test_hostmanage_simple_ssh_test(self, testdir):
|
||||
rp = testdir.mkdir('xyz123')
|
||||
rp.ensure("__init__.py")
|
||||
p = testdir.makepyfile("def test_123(): import xyz123")
|
||||
result = testdir.runpytest(p, '-d', "--hosts=popen", '--rsyncdirs=' + str(rp))
|
||||
assert result.ret == 0
|
||||
assert result.stdout.str().find("1 passed") != -1
|
||||
|
||||
@py.test.mark.xfail("implement double-rsync test")
|
||||
def test_ssh_rsync_samehost_twice(self):
|
||||
option = py.test.config.option
|
||||
if option.sshhost is None:
|
||||
py.test.skip("no known ssh target, use -S to set one")
|
||||
|
||||
host1 = Host("%s" % (option.sshhost, ))
|
||||
host2 = Host("%s" % (option.sshhost, ))
|
||||
hm = HostManager(config, hosts=[host1, host2])
|
||||
|
@ -150,8 +156,35 @@ class TestHostManager:
|
|||
assert 0
|
||||
|
||||
|
||||
def test_getconfighosts():
|
||||
def test_getconfighosts_numprocesses():
|
||||
config = py.test.config._reparse(['-n3'])
|
||||
hosts = getconfighosts(config)
|
||||
assert len(hosts) == 3
|
||||
|
||||
def test_getconfighosts_disthosts():
|
||||
config = py.test.config._reparse(['--hosts=a,b,c'])
|
||||
hosts = getconfighosts(config)
|
||||
assert len(hosts) == 3
|
||||
assert hosts == ['a', 'b', 'c']
|
||||
|
||||
def test_getconfigroots(testdir):
|
||||
config = testdir.parseconfig('--rsyncdirs=' + str(testdir.tmpdir))
|
||||
roots = getconfigroots(config)
|
||||
assert len(roots) == 1
|
||||
assert roots == [testdir.tmpdir]
|
||||
|
||||
def test_getconfigroots_with_conftest(testdir):
|
||||
testdir.chdir()
|
||||
p = py.path.local()
|
||||
for bn in 'x y z'.split():
|
||||
p.mkdir(bn)
|
||||
testdir.makeconftest("""
|
||||
rsyncdirs= 'x',
|
||||
""")
|
||||
config = testdir.parseconfig(testdir.tmpdir, '--rsyncdirs=y,z')
|
||||
roots = getconfigroots(config)
|
||||
assert len(roots) == 3
|
||||
assert py.path.local('y') in roots
|
||||
assert py.path.local('z') in roots
|
||||
assert testdir.tmpdir.join('x') in roots
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ class MySetup:
|
|||
config = py.test.config._reparse([])
|
||||
self.config = config
|
||||
self.queue = py.std.Queue.Queue()
|
||||
self.host = GatewaySpec("localhost")
|
||||
self.host = GatewaySpec("popen")
|
||||
self.gateway = self.host.makegateway()
|
||||
self.node = MasterNode(self.host, self.gateway, self.config, putevent=self.queue.put)
|
||||
assert not self.node.channel.isclosed()
|
||||
|
|
|
@ -36,9 +36,6 @@ class DefaultPlugin:
|
|||
group._addoption('-x', '--exitfirst',
|
||||
action="store_true", dest="exitfirst", default=False,
|
||||
help="exit instantly on first error or failed test."),
|
||||
group._addoption('-s', '--nocapture',
|
||||
action="store_true", dest="nocapture", default=False,
|
||||
help="disable catching of sys.stdout/stderr output."),
|
||||
group._addoption('-k',
|
||||
action="store", dest="keyword", default='',
|
||||
help="only run test items matching the given "
|
||||
|
@ -47,57 +44,70 @@ class DefaultPlugin:
|
|||
"to run all subsequent tests. ")
|
||||
group._addoption('-l', '--showlocals',
|
||||
action="store_true", dest="showlocals", default=False,
|
||||
help="show locals in tracebacks (disabled by default)."),
|
||||
help="show locals in tracebacks (disabled by default).")
|
||||
group._addoption('--showskipsummary',
|
||||
action="store_true", dest="showskipsummary", default=False,
|
||||
help="always show summary of skipped tests"),
|
||||
group._addoption('', '--pdb',
|
||||
help="always show summary of skipped tests")
|
||||
group._addoption('--pdb',
|
||||
action="store_true", dest="usepdb", default=False,
|
||||
help="start pdb (the Python debugger) on errors."),
|
||||
group._addoption('', '--tb',
|
||||
help="start pdb (the Python debugger) on errors.")
|
||||
group._addoption('--tb',
|
||||
action="store", dest="tbstyle", default='long',
|
||||
type="choice", choices=['long', 'short', 'no'],
|
||||
help="traceback verboseness (long/short/no)."),
|
||||
group._addoption('', '--fulltrace',
|
||||
help="traceback verboseness (long/short/no).")
|
||||
group._addoption('--fulltrace',
|
||||
action="store_true", dest="fulltrace", default=False,
|
||||
help="don't cut any tracebacks (default is to cut)."),
|
||||
group._addoption('', '--nomagic',
|
||||
action="store_true", dest="nomagic", default=False,
|
||||
help="refrain from using magic as much as possible."),
|
||||
group._addoption('', '--traceconfig',
|
||||
action="store_true", dest="traceconfig", default=False,
|
||||
help="trace considerations of conftest.py files."),
|
||||
group._addoption('-f', '--looponfailing',
|
||||
action="store_true", dest="looponfailing", default=False,
|
||||
help="loop on failing test set."),
|
||||
group._addoption('', '--exec',
|
||||
action="store", dest="executable", default=None,
|
||||
help="python executable to run the tests with."),
|
||||
group._addoption('-n', '--numprocesses', dest="numprocesses", default=0,
|
||||
action="store", type="int",
|
||||
help="number of local test processes."),
|
||||
group._addoption('', '--debug',
|
||||
action="store_true", dest="debug", default=False,
|
||||
help="turn on debugging information."),
|
||||
|
||||
group = parser.addgroup("experimental", "experimental options")
|
||||
group._addoption('-d', '--dist',
|
||||
action="store_true", dest="dist", default=False,
|
||||
help="ad-hoc distribute tests across machines (requires conftest settings)"),
|
||||
group._addoption('-w', '--startserver',
|
||||
action="store_true", dest="startserver", default=False,
|
||||
help="starts local web server for displaying test progress.",
|
||||
),
|
||||
group._addoption('-r', '--runbrowser',
|
||||
action="store_true", dest="runbrowser", default=False,
|
||||
help="run browser (implies --startserver)."
|
||||
),
|
||||
help="don't cut any tracebacks (default is to cut).")
|
||||
group._addoption('-s', '--nocapture',
|
||||
action="store_true", dest="nocapture", default=False,
|
||||
help="disable catching of sys.stdout/stderr output."),
|
||||
group._addoption('--boxed',
|
||||
action="store_true", dest="boxed", default=False,
|
||||
help="box each test run in a separate process"),
|
||||
group._addoption('--rest',
|
||||
action='store_true', dest="restreport", default=False,
|
||||
help="restructured text output reporting."),
|
||||
group._addoption('-f', '--looponfailing',
|
||||
action="store_true", dest="looponfailing", default=False,
|
||||
help="loop on failing test set.")
|
||||
|
||||
group = parser.addgroup("test process debugging")
|
||||
group.addoption('--collectonly',
|
||||
action="store_true", dest="collectonly",
|
||||
help="only collect tests, don't execute them."),
|
||||
group.addoption('--traceconfig',
|
||||
action="store_true", dest="traceconfig", default=False,
|
||||
help="trace considerations of conftest.py files."),
|
||||
group._addoption('--nomagic',
|
||||
action="store_true", dest="nomagic", default=False,
|
||||
help="don't use assert reinterpretation and python traceback cutting. ")
|
||||
group.addoption('--debug',
|
||||
action="store_true", dest="debug", default=False,
|
||||
help="generate and show debugging information.")
|
||||
|
||||
group = parser.addgroup("xplatform", "distributed/cross platform testing")
|
||||
group._addoption('-d', '--dist',
|
||||
action="store_true", dest="dist", default=False,
|
||||
help="ad-hoc distribute tests across machines (requires conftest settings)")
|
||||
group._addoption('-n', '--numprocesses', dest="numprocesses", default=0, metavar="num",
|
||||
action="store", type="int",
|
||||
help="number of local test processes. conflicts with --dist.")
|
||||
group.addoption('--rsyncdirs', dest="rsyncdirs", default=None, metavar="dir1,dir2,...",
|
||||
help="comma-separated list of directories to rsync. All those roots will be rsynced "
|
||||
"into a corresponding subdir on the remote sides. ")
|
||||
group.addoption('--hosts', dest="hosts", default=None, metavar="host1,host2,...",
|
||||
help="comma-separated list of host specs to send tests to.")
|
||||
group._addoption('--exec',
|
||||
action="store", dest="executable", default=None,
|
||||
help="python executable to run the tests with.")
|
||||
#group._addoption('-w', '--startserver',
|
||||
# action="store_true", dest="startserver", default=False,
|
||||
# help="starts local web server for displaying test progress.",
|
||||
# ),
|
||||
#group._addoption('-r', '--runbrowser',
|
||||
# action="store_true", dest="runbrowser", default=False,
|
||||
# help="run browser (implies --startserver)."
|
||||
# ),
|
||||
#group._addoption('--rest',
|
||||
# action='store_true', dest="restreport", default=False,
|
||||
# help="restructured text output reporting."),
|
||||
|
||||
def pytest_configure(self, config):
|
||||
self.setsession(config)
|
||||
|
|
|
@ -155,6 +155,9 @@ class PytestPluginHooks:
|
|||
def pyevent(self, eventname, *args, **kwargs):
|
||||
""" called for each testing event. """
|
||||
|
||||
def pyevent_trace(self, category, msg):
|
||||
""" called for tracing events events. """
|
||||
|
||||
def pyevent_internalerror(self, event):
|
||||
""" called for internal errors. """
|
||||
|
||||
|
|
|
@ -248,10 +248,8 @@ class EventRecorder(object):
|
|||
if name == "plugin_registered" and args == (self,):
|
||||
return
|
||||
if self.debug:
|
||||
print "[event] %s: %s **%s" %(name, ", ".join(map(str, args)), kwargs,)
|
||||
if len(args) == 1:
|
||||
event, = args
|
||||
self.events.append((name, event))
|
||||
print "[event: %s]: %s **%s" %(name, ", ".join(map(str, args)), kwargs,)
|
||||
self.events.append((name,) + tuple(args))
|
||||
|
||||
def get(self, cls):
|
||||
l = []
|
||||
|
|
|
@ -3,11 +3,6 @@ import sys
|
|||
|
||||
class TerminalPlugin(object):
|
||||
""" Report a test run to a terminal. """
|
||||
def pytest_addoption(self, parser):
|
||||
parser.addoption('--collectonly',
|
||||
action="store_true", dest="collectonly",
|
||||
help="only collect tests, don't execute them."),
|
||||
|
||||
def pytest_configure(self, config):
|
||||
if config.option.collectonly:
|
||||
self.reporter = CollectonlyReporter(config)
|
||||
|
@ -115,6 +110,11 @@ class TerminalReporter:
|
|||
if error:
|
||||
self.write_line("HostDown %s: %s" %(host, error))
|
||||
|
||||
def pyevent_trace(self, category, msg):
|
||||
if self.config.option.debug or \
|
||||
self.config.option.traceconfig and category.find("config") != -1:
|
||||
self.write_line("[%s] %s" %(category, msg))
|
||||
|
||||
def pyevent_itemstart(self, event):
|
||||
if self.config.option.verbose:
|
||||
info = event.item.repr_metainfo()
|
||||
|
@ -167,14 +167,15 @@ class TerminalReporter:
|
|||
rev = py.__pkg__.getrev()
|
||||
self.write_line("using py lib: %s <rev %s>" % (
|
||||
py.path.local(py.__file__).dirpath(), rev))
|
||||
plugins = []
|
||||
for x in self.config.pytestplugins._plugins:
|
||||
if isinstance(x, str) and x.startswith("pytest_"):
|
||||
plugins.append(x[7:])
|
||||
else:
|
||||
plugins.append(str(x)) # XXX display conftest plugins more nicely
|
||||
plugins = ", ".join(plugins)
|
||||
self.write_line("active plugins: %s" %(plugins,))
|
||||
if self.config.option.traceconfig:
|
||||
plugins = []
|
||||
for x in self.config.pytestplugins._plugins:
|
||||
if isinstance(x, str) and x.startswith("pytest_"):
|
||||
plugins.append(x[7:])
|
||||
else:
|
||||
plugins.append(str(x)) # XXX display conftest plugins more nicely
|
||||
plugins = ", ".join(plugins)
|
||||
self.write_line("active plugins: %s" %(plugins,))
|
||||
for i, testarg in py.builtin.enumerate(self.config.args):
|
||||
self.write_line("test object %d: %s" %(i+1, testarg))
|
||||
|
||||
|
|
|
@ -29,9 +29,9 @@ class Session(object):
|
|||
def fixoptions(self):
|
||||
""" check, fix and determine conflicting options. """
|
||||
option = self.config.option
|
||||
if option.runbrowser and not option.startserver:
|
||||
#print "--runbrowser implies --startserver"
|
||||
option.startserver = 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."
|
||||
|
|
|
@ -241,13 +241,11 @@ class TestPyTest:
|
|||
def test_skip():
|
||||
py.test.skip("hello")
|
||||
""",
|
||||
conftest="""
|
||||
dist_hosts = ['localhost'] * 3
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(p1, '-d')
|
||||
#result = testdir.runpytest(p1, '-d')
|
||||
result = testdir.runpytest(p1, '-d', '--hosts=popen,popen,popen')
|
||||
result.stdout.fnmatch_lines([
|
||||
"HOSTUP: localhost*Python*",
|
||||
"HOSTUP: popen*Python*",
|
||||
#"HOSTUP: localhost*Python*",
|
||||
#"HOSTUP: localhost*Python*",
|
||||
"*2 failed, 1 passed, 1 skipped*",
|
||||
|
@ -273,17 +271,14 @@ class TestPyTest:
|
|||
import os
|
||||
time.sleep(0.5)
|
||||
os.kill(os.getpid(), 15)
|
||||
""",
|
||||
conftest="""
|
||||
dist_hosts = ['localhost'] * 3
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(p1, '-d')
|
||||
result = testdir.runpytest(p1, '-d', '--hosts=popen,popen,popen')
|
||||
result.stdout.fnmatch_lines([
|
||||
"*localhost*Python*",
|
||||
"*localhost*Python*",
|
||||
"*localhost*Python*",
|
||||
"HostDown*localhost*TERMINATED*",
|
||||
"*popen*Python*",
|
||||
"*popen*Python*",
|
||||
"*popen*Python*",
|
||||
"HostDown*TERMINATED*",
|
||||
"*3 failed, 1 passed, 1 skipped*"
|
||||
])
|
||||
assert result.ret == 1
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import py
|
||||
|
||||
|
||||
class TestConfigCmdlineParsing:
|
||||
def test_config_cmdline_options(self, testdir):
|
||||
testdir.makepyfile(conftest="""
|
||||
|
@ -118,22 +119,18 @@ class TestConfigAPI:
|
|||
config = py.test.config._reparse([str(o)])
|
||||
assert config.getvalue('x') == 1
|
||||
|
||||
def test_getvalue_pathlist(self, tmpdir):
|
||||
def test_getconftest_pathlist(self, tmpdir):
|
||||
somepath = tmpdir.join("x", "y", "z")
|
||||
p = tmpdir.join("conftest.py")
|
||||
p.write("pathlist = ['.', %r]" % str(somepath))
|
||||
config = py.test.config._reparse([p])
|
||||
assert config.getvalue_pathlist('notexist') is None
|
||||
pl = config.getvalue_pathlist('pathlist')
|
||||
assert config.getconftest_pathlist('notexist') is None
|
||||
pl = config.getconftest_pathlist('pathlist')
|
||||
print pl
|
||||
assert len(pl) == 2
|
||||
assert pl[0] == tmpdir
|
||||
assert pl[1] == somepath
|
||||
|
||||
config.option.mypathlist = [py.path.local()]
|
||||
pl = config.getvalue_pathlist('mypathlist')
|
||||
assert pl == [py.path.local()]
|
||||
|
||||
def test_setsessionclass_and_initsession(self, testdir):
|
||||
from py.__.test.config import Config
|
||||
config = Config()
|
||||
|
|
|
@ -100,7 +100,7 @@ class TestBootstrapping:
|
|||
|
||||
def test_config_sets_conftesthandle_onimport(self, testdir):
|
||||
config = testdir.parseconfig([])
|
||||
assert config._conftest._onimport == config.pytestplugins.consider_conftest
|
||||
assert config._conftest._onimport == config._onimportconftest
|
||||
|
||||
def test_consider_conftest_deps(self, testdir):
|
||||
mod = testdir.makepyfile("pytest_plugins='xyz'").pyimport()
|
||||
|
|
Loading…
Reference in New Issue