[svn r63210] * implement "--dist=each" to distribute each to each available node
* improved node-management and nice showing of results * streamline command line options --HG-- branch : trunk
This commit is contained in:
parent
ad6afe21ff
commit
2c2bfb5513
|
@ -12,11 +12,10 @@ class PylibTestconfigPlugin:
|
||||||
group = parser.addgroup("pylib", "py lib testing options")
|
group = parser.addgroup("pylib", "py lib testing options")
|
||||||
group.addoption('--sshhost',
|
group.addoption('--sshhost',
|
||||||
action="store", dest="sshhost", default=None,
|
action="store", dest="sshhost", default=None,
|
||||||
help=("target to run tests requiring ssh, e.g. "
|
help=("ssh xspec for ssh functional tests. "))
|
||||||
"user@codespeak.net"))
|
|
||||||
group.addoption('--gx',
|
group.addoption('--gx',
|
||||||
action="append", dest="gspecs", default=None,
|
action="append", dest="gspecs", default=None,
|
||||||
help=("add a global test environment, XSpec-syntax. ")),
|
help=("add a global test environment, XSpec-syntax. "))
|
||||||
group.addoption('--runslowtests',
|
group.addoption('--runslowtests',
|
||||||
action="store_true", dest="runslowtests", default=False,
|
action="store_true", dest="runslowtests", default=False,
|
||||||
help=("run slow tests"))
|
help=("run slow tests"))
|
||||||
|
|
|
@ -17,10 +17,10 @@ To send tests to multiple CPUs, type::
|
||||||
Especially for longer running tests or tests requiring
|
Especially for longer running tests or tests requiring
|
||||||
a lot of IO this can lead to considerable speed ups.
|
a lot of IO this can lead to considerable speed ups.
|
||||||
|
|
||||||
Test on a different python interpreter
|
Test on a different python version
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
|
|
||||||
To send tests to a python2.4 process, you may type::
|
To send tests to a python2.4 interpreter process, you may type::
|
||||||
|
|
||||||
py.test --tx popen//python=python2.4
|
py.test --tx popen//python=python2.4
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ with something like this::
|
||||||
|
|
||||||
py.test -d --tx socket=192.168.1.102:8888 --rsyncdir mypkg mypkg
|
py.test -d --tx socket=192.168.1.102:8888 --rsyncdir mypkg mypkg
|
||||||
|
|
||||||
|
|
||||||
no remote installation requirements
|
no remote installation requirements
|
||||||
++++++++++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
|
|
@ -244,6 +244,9 @@ class Gateway(object):
|
||||||
def _rinfo(self, update=False):
|
def _rinfo(self, update=False):
|
||||||
""" return some sys/env information from remote. """
|
""" return some sys/env information from remote. """
|
||||||
if update or not hasattr(self, '_cache_rinfo'):
|
if update or not hasattr(self, '_cache_rinfo'):
|
||||||
|
class RInfo:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.__dict__.update(kwargs)
|
||||||
self._cache_rinfo = RInfo(**self.remote_exec("""
|
self._cache_rinfo = RInfo(**self.remote_exec("""
|
||||||
import sys, os
|
import sys, os
|
||||||
channel.send(dict(
|
channel.send(dict(
|
||||||
|
@ -251,6 +254,7 @@ class Gateway(object):
|
||||||
version_info = sys.version_info,
|
version_info = sys.version_info,
|
||||||
platform = sys.platform,
|
platform = sys.platform,
|
||||||
cwd = os.getcwd(),
|
cwd = os.getcwd(),
|
||||||
|
pid = os.getpid(),
|
||||||
))
|
))
|
||||||
""").receive())
|
""").receive())
|
||||||
return self._cache_rinfo
|
return self._cache_rinfo
|
||||||
|
@ -368,10 +372,6 @@ class Gateway(object):
|
||||||
if self._requestqueue is not None:
|
if self._requestqueue is not None:
|
||||||
self._requestqueue.put(None)
|
self._requestqueue.put(None)
|
||||||
|
|
||||||
class RInfo:
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.__dict__.update(kwargs)
|
|
||||||
|
|
||||||
def getid(gw, cache={}):
|
def getid(gw, cache={}):
|
||||||
name = gw.__class__.__name__
|
name = gw.__class__.__name__
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -3,19 +3,21 @@ import py
|
||||||
XSpec = py.execnet.XSpec
|
XSpec = py.execnet.XSpec
|
||||||
|
|
||||||
class TestXSpec:
|
class TestXSpec:
|
||||||
def test_attributes(self):
|
def test_norm_attributes(self):
|
||||||
spec = XSpec("socket=192.168.102.2:8888//python=c:/this/python2.5//chdir=d:\hello")
|
spec = XSpec("socket=192.168.102.2:8888//python=c:/this/python2.5//chdir=d:\hello")
|
||||||
assert spec.socket == "192.168.102.2:8888"
|
assert spec.socket == "192.168.102.2:8888"
|
||||||
assert spec.python == "c:/this/python2.5"
|
assert spec.python == "c:/this/python2.5"
|
||||||
assert spec.chdir == "d:\hello"
|
assert spec.chdir == "d:\hello"
|
||||||
|
assert spec.nice is None
|
||||||
assert not hasattr(spec, 'xyz')
|
assert not hasattr(spec, 'xyz')
|
||||||
|
|
||||||
py.test.raises(AttributeError, "spec._hello")
|
py.test.raises(AttributeError, "spec._hello")
|
||||||
|
|
||||||
spec = XSpec("socket=192.168.102.2:8888//python=python2.5")
|
spec = XSpec("socket=192.168.102.2:8888//python=python2.5//nice=3")
|
||||||
assert spec.socket == "192.168.102.2:8888"
|
assert spec.socket == "192.168.102.2:8888"
|
||||||
assert spec.python == "python2.5"
|
assert spec.python == "python2.5"
|
||||||
assert spec.chdir is None
|
assert spec.chdir is None
|
||||||
|
assert spec.nice == "3"
|
||||||
|
|
||||||
spec = XSpec("ssh=user@host//chdir=/hello/this//python=/usr/bin/python2.5")
|
spec = XSpec("ssh=user@host//chdir=/hello/this//python=/usr/bin/python2.5")
|
||||||
assert spec.ssh == "user@host"
|
assert spec.ssh == "user@host"
|
||||||
|
@ -53,6 +55,18 @@ class TestMakegateway:
|
||||||
assert rinfo.cwd == py.std.os.getcwd()
|
assert rinfo.cwd == py.std.os.getcwd()
|
||||||
assert rinfo.version_info == py.std.sys.version_info
|
assert rinfo.version_info == py.std.sys.version_info
|
||||||
|
|
||||||
|
def test_popen_nice(self):
|
||||||
|
gw = py.execnet.makegateway("popen//nice=5")
|
||||||
|
remotenice = gw.remote_exec("""
|
||||||
|
import os
|
||||||
|
if hasattr(os, 'nice'):
|
||||||
|
channel.send(os.nice(0))
|
||||||
|
else:
|
||||||
|
channel.send(None)
|
||||||
|
""").receive()
|
||||||
|
if remotenice is not None:
|
||||||
|
assert remotenice == 5
|
||||||
|
|
||||||
def test_popen_explicit(self):
|
def test_popen_explicit(self):
|
||||||
gw = py.execnet.makegateway("popen//python=%s" % py.std.sys.executable)
|
gw = py.execnet.makegateway("popen//python=%s" % py.std.sys.executable)
|
||||||
assert gw.spec.python == py.std.sys.executable
|
assert gw.spec.python == py.std.sys.executable
|
||||||
|
|
|
@ -10,7 +10,7 @@ class XSpec:
|
||||||
* if no "=value" is given, assume a boolean True value
|
* if no "=value" is given, assume a boolean True value
|
||||||
"""
|
"""
|
||||||
# XXX for now we are very restrictive about actually allowed key-values
|
# XXX for now we are very restrictive about actually allowed key-values
|
||||||
popen = ssh = socket = python = chdir = None
|
popen = ssh = socket = python = chdir = nice = None
|
||||||
|
|
||||||
def __init__(self, string):
|
def __init__(self, string):
|
||||||
self._spec = string
|
self._spec = string
|
||||||
|
@ -56,14 +56,18 @@ def makegateway(spec):
|
||||||
gw = py.execnet.SocketGateway(*hostport)
|
gw = py.execnet.SocketGateway(*hostport)
|
||||||
gw.spec = spec
|
gw.spec = spec
|
||||||
# XXX events
|
# XXX events
|
||||||
if spec.chdir:
|
if spec.chdir or spec.nice:
|
||||||
gw.remote_exec("""
|
channel = gw.remote_exec("""
|
||||||
import os
|
import os
|
||||||
path = %r
|
path, nice = channel.receive()
|
||||||
try:
|
if path:
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.mkdir(path)
|
||||||
os.chdir(path)
|
os.chdir(path)
|
||||||
except OSError:
|
if nice and hasattr(os, 'nice'):
|
||||||
os.mkdir(path)
|
os.nice(nice)
|
||||||
os.chdir(path)
|
""")
|
||||||
""" % spec.chdir).waitclose()
|
nice = spec.nice and int(spec.nice) or 0
|
||||||
|
channel.send((spec.chdir, nice))
|
||||||
|
channel.waitclose()
|
||||||
return gw
|
return gw
|
||||||
|
|
|
@ -177,8 +177,11 @@ class DSession(Session):
|
||||||
else:
|
else:
|
||||||
self.bus.notify("collectionstart", event.CollectionStart(next))
|
self.bus.notify("collectionstart", event.CollectionStart(next))
|
||||||
self.queueevent("collectionreport", basic_collect_report(next))
|
self.queueevent("collectionreport", basic_collect_report(next))
|
||||||
self.senditems(senditems)
|
if self.config.option.dist == "each":
|
||||||
#self.senditems_each(senditems)
|
self.senditems_each(senditems)
|
||||||
|
else:
|
||||||
|
# XXX assert self.config.option.dist == "load"
|
||||||
|
self.senditems_load(senditems)
|
||||||
|
|
||||||
def queueevent(self, eventname, *args, **kwargs):
|
def queueevent(self, eventname, *args, **kwargs):
|
||||||
self.queue.put((eventname, args, kwargs))
|
self.queue.put((eventname, args, kwargs))
|
||||||
|
@ -201,7 +204,7 @@ class DSession(Session):
|
||||||
# we have some left, give it to the main loop
|
# we have some left, give it to the main loop
|
||||||
self.queueevent("rescheduleitems", event.RescheduleItems(tosend))
|
self.queueevent("rescheduleitems", event.RescheduleItems(tosend))
|
||||||
|
|
||||||
def senditems(self, tosend):
|
def senditems_load(self, tosend):
|
||||||
if not tosend:
|
if not tosend:
|
||||||
return
|
return
|
||||||
for node, pending in self.node2pending.items():
|
for node, pending in self.node2pending.items():
|
||||||
|
@ -242,6 +245,8 @@ class DSession(Session):
|
||||||
""" setup any neccessary resources ahead of the test run. """
|
""" setup any neccessary resources ahead of the test run. """
|
||||||
self.nodemanager = NodeManager(self.config)
|
self.nodemanager = NodeManager(self.config)
|
||||||
self.nodemanager.setup_nodes(putevent=self.queue.put)
|
self.nodemanager.setup_nodes(putevent=self.queue.put)
|
||||||
|
if self.config.option.dist == "each":
|
||||||
|
self.nodemanager.wait_nodesready(5.0)
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
""" teardown any resources after a test run. """
|
""" teardown any resources after a test run. """
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import py
|
import py
|
||||||
import sys, os
|
import sys, os
|
||||||
from py.__.test.dist.txnode import MasterNode
|
from py.__.test.dist.txnode import TXNode
|
||||||
from py.__.execnet.gwmanage import GatewayManager
|
from py.__.execnet.gwmanage import GatewayManager
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,19 +12,11 @@ class NodeManager(object):
|
||||||
self.roots = self.config.getrsyncdirs()
|
self.roots = self.config.getrsyncdirs()
|
||||||
self.gwmanager = GatewayManager(specs)
|
self.gwmanager = GatewayManager(specs)
|
||||||
self.nodes = []
|
self.nodes = []
|
||||||
|
self._nodesready = py.std.threading.Event()
|
||||||
|
|
||||||
def trace(self, msg):
|
def trace(self, msg):
|
||||||
self.config.bus.notify("trace", "nodemanage", msg)
|
self.config.bus.notify("trace", "nodemanage", msg)
|
||||||
|
|
||||||
def trace_nodestatus(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_each(withchannel=True):
|
|
||||||
self.trace("spec %r, execuable %r, cwd %r, syspath %r" %(
|
|
||||||
ch.gateway.spec, result[0], result[1], result[2]))
|
|
||||||
|
|
||||||
def config_getignores(self):
|
def config_getignores(self):
|
||||||
return self.config.getconftest_pathlist("rsyncignore")
|
return self.config.getconftest_pathlist("rsyncignore")
|
||||||
|
|
||||||
|
@ -55,20 +47,33 @@ class NodeManager(object):
|
||||||
# such that unpickling configs will
|
# such that unpickling configs will
|
||||||
# pick it up as the right topdir
|
# pick it up as the right topdir
|
||||||
# (for other gateways this chdir is irrelevant)
|
# (for other gateways this chdir is irrelevant)
|
||||||
|
self.trace("making gateways")
|
||||||
old = self.config.topdir.chdir()
|
old = self.config.topdir.chdir()
|
||||||
try:
|
try:
|
||||||
self.gwmanager.makegateways()
|
self.gwmanager.makegateways()
|
||||||
finally:
|
finally:
|
||||||
old.chdir()
|
old.chdir()
|
||||||
self.trace_nodestatus()
|
|
||||||
|
|
||||||
|
|
||||||
def setup_nodes(self, putevent):
|
def setup_nodes(self, putevent):
|
||||||
self.rsync_roots()
|
self.rsync_roots()
|
||||||
self.trace_nodestatus()
|
self.trace("setting up nodes")
|
||||||
for gateway in self.gwmanager.gateways:
|
for gateway in self.gwmanager.gateways:
|
||||||
node = MasterNode(gateway, self.config, putevent)
|
node = TXNode(gateway, self.config, putevent, slaveready=self._slaveready)
|
||||||
self.nodes.append(node)
|
gateway.node = node # to keep node alive
|
||||||
|
self.trace("started node %r" % node)
|
||||||
|
|
||||||
|
def _slaveready(self, node):
|
||||||
|
#assert node.gateway == node.gateway
|
||||||
|
#assert node.gateway.node == node
|
||||||
|
self.nodes.append(node)
|
||||||
|
self.trace("%s slave node ready %r" % (node.gateway.id, node))
|
||||||
|
if len(self.nodes) == len(self.gwmanager.gateways):
|
||||||
|
self._nodesready.set()
|
||||||
|
|
||||||
|
def wait_nodesready(self, timeout=None):
|
||||||
|
self._nodesready.wait(timeout)
|
||||||
|
if not self._nodesready.isSet():
|
||||||
|
raise IOError("nodes did not get ready for %r secs" % timeout)
|
||||||
|
|
||||||
def teardown_nodes(self):
|
def teardown_nodes(self):
|
||||||
# XXX do teardown nodes?
|
# XXX do teardown nodes?
|
||||||
|
|
|
@ -35,14 +35,14 @@ class TestDSession:
|
||||||
assert not session.node2pending
|
assert not session.node2pending
|
||||||
session.addnode(node)
|
session.addnode(node)
|
||||||
assert len(session.node2pending) == 1
|
assert len(session.node2pending) == 1
|
||||||
session.senditems([item])
|
session.senditems_load([item])
|
||||||
pending = session.removenode(node)
|
pending = session.removenode(node)
|
||||||
assert pending == [item]
|
assert pending == [item]
|
||||||
assert item not in session.item2nodes
|
assert item not in session.item2nodes
|
||||||
l = session.removenode(node)
|
l = session.removenode(node)
|
||||||
assert not l
|
assert not l
|
||||||
|
|
||||||
def test_send_remove_to_two_nodes(self, testdir):
|
def test_senditems_each_and_receive_with_two_nodes(self, testdir):
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
node1 = MockNode()
|
node1 = MockNode()
|
||||||
node2 = MockNode()
|
node2 = MockNode()
|
||||||
|
@ -60,13 +60,13 @@ class TestDSession:
|
||||||
assert not session.node2pending[node1]
|
assert not session.node2pending[node1]
|
||||||
assert not session.item2nodes
|
assert not session.item2nodes
|
||||||
|
|
||||||
def test_senditems_removeitems(self, testdir):
|
def test_senditems_load_and_receive_one_node(self, testdir):
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
node = MockNode()
|
node = MockNode()
|
||||||
rep = run(item, node)
|
rep = run(item, node)
|
||||||
session = DSession(item.config)
|
session = DSession(item.config)
|
||||||
session.addnode(node)
|
session.addnode(node)
|
||||||
session.senditems([item])
|
session.senditems_load([item])
|
||||||
assert session.node2pending[node] == [item]
|
assert session.node2pending[node] == [item]
|
||||||
assert session.item2nodes[item] == [node]
|
assert session.item2nodes[item] == [node]
|
||||||
session.removeitem(item, node)
|
session.removeitem(item, node)
|
||||||
|
@ -174,7 +174,7 @@ class TestDSession:
|
||||||
session.addnode(node2)
|
session.addnode(node2)
|
||||||
|
|
||||||
# have one test pending for a node that goes down
|
# have one test pending for a node that goes down
|
||||||
session.senditems([item1, item2])
|
session.senditems_load([item1, item2])
|
||||||
node = session.item2nodes[item1] [0]
|
node = session.item2nodes[item1] [0]
|
||||||
session.queueevent("testnodedown", node, None)
|
session.queueevent("testnodedown", node, None)
|
||||||
evrec = EventRecorder(session.bus)
|
evrec = EventRecorder(session.bus)
|
||||||
|
@ -316,7 +316,7 @@ class TestDSession:
|
||||||
|
|
||||||
node = MockNode()
|
node = MockNode()
|
||||||
session.addnode(node)
|
session.addnode(node)
|
||||||
session.senditems([item])
|
session.senditems_load([item])
|
||||||
session.queueevent("itemtestreport", run(item, node))
|
session.queueevent("itemtestreport", run(item, node))
|
||||||
loopstate = session._initloopstate([])
|
loopstate = session._initloopstate([])
|
||||||
session.loop_once(loopstate)
|
session.loop_once(loopstate)
|
||||||
|
@ -340,7 +340,7 @@ class TestDSession:
|
||||||
|
|
||||||
colreport = basic_collect_report(modcol)
|
colreport = basic_collect_report(modcol)
|
||||||
item1, item2 = colreport.result
|
item1, item2 = colreport.result
|
||||||
session.senditems([item1])
|
session.senditems_load([item1])
|
||||||
# node2pending will become empty when the loop sees the report
|
# node2pending will become empty when the loop sees the report
|
||||||
rep = run(item1, node)
|
rep = run(item1, node)
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,13 @@ class TestNodeManager:
|
||||||
assert p.join("dir1").check()
|
assert p.join("dir1").check()
|
||||||
assert p.join("dir1", "file1").check()
|
assert p.join("dir1", "file1").check()
|
||||||
|
|
||||||
|
def test_popen_nodes_are_ready(self, testdir):
|
||||||
|
nodemanager = NodeManager(testdir.parseconfig(
|
||||||
|
"--tx", "3*popen"))
|
||||||
|
|
||||||
|
nodemanager.setup_nodes([].append)
|
||||||
|
nodemanager.wait_nodesready(timeout=2.0)
|
||||||
|
|
||||||
def test_popen_rsync_subdir(self, testdir, source, dest):
|
def test_popen_rsync_subdir(self, testdir, source, dest):
|
||||||
dir1 = source.mkdir("dir1")
|
dir1 = source.mkdir("dir1")
|
||||||
dir2 = dir1.mkdir("dir2")
|
dir2 = dir1.mkdir("dir2")
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
import py
|
import py
|
||||||
from py.__.test.dist.txnode import MasterNode
|
from py.__.test.dist.txnode import TXNode
|
||||||
|
|
||||||
class EventQueue:
|
class EventQueue:
|
||||||
def __init__(self, bus, queue=None):
|
def __init__(self, bus, queue=None):
|
||||||
|
@ -44,7 +44,7 @@ class MySetup:
|
||||||
self.queue = py.std.Queue.Queue()
|
self.queue = py.std.Queue.Queue()
|
||||||
self.xspec = py.execnet.XSpec("popen")
|
self.xspec = py.execnet.XSpec("popen")
|
||||||
self.gateway = py.execnet.makegateway(self.xspec)
|
self.gateway = py.execnet.makegateway(self.xspec)
|
||||||
self.node = MasterNode(self.gateway, self.config, putevent=self.queue.put)
|
self.node = TXNode(self.gateway, self.config, putevent=self.queue.put)
|
||||||
assert not self.node.channel.isclosed()
|
assert not self.node.channel.isclosed()
|
||||||
return self.node
|
return self.node
|
||||||
|
|
||||||
|
|
|
@ -5,16 +5,22 @@ import py
|
||||||
from py.__.test import event
|
from py.__.test import event
|
||||||
from py.__.test.dist.mypickle import PickleChannel
|
from py.__.test.dist.mypickle import PickleChannel
|
||||||
|
|
||||||
class MasterNode(object):
|
|
||||||
""" Install slave code, manage sending test tasks & receiving results """
|
class TXNode(object):
|
||||||
|
""" Represents a Test Execution environment in the controlling process.
|
||||||
|
- sets up a slave node through an execnet gateway
|
||||||
|
- manages sending of test-items and receival of results and events
|
||||||
|
- creates events when the remote side crashes
|
||||||
|
"""
|
||||||
ENDMARK = -1
|
ENDMARK = -1
|
||||||
|
|
||||||
def __init__(self, gateway, config, putevent):
|
def __init__(self, gateway, config, putevent, slaveready=None):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.putevent = putevent
|
self.putevent = putevent
|
||||||
self.gateway = gateway
|
self.gateway = gateway
|
||||||
self.channel = install_slave(gateway, config)
|
self.channel = install_slave(gateway, config)
|
||||||
self.channel.setcallback(self.callback, endmarker=self.ENDMARK)
|
self.channel.setcallback(self.callback, endmarker=self.ENDMARK)
|
||||||
|
self._sendslaveready = slaveready
|
||||||
self._down = False
|
self._down = False
|
||||||
|
|
||||||
def notify(self, eventname, *args, **kwargs):
|
def notify(self, eventname, *args, **kwargs):
|
||||||
|
@ -39,6 +45,8 @@ class MasterNode(object):
|
||||||
return
|
return
|
||||||
eventname, args, kwargs = eventcall
|
eventname, args, kwargs = eventcall
|
||||||
if eventname == "slaveready":
|
if eventname == "slaveready":
|
||||||
|
if self._sendslaveready:
|
||||||
|
self._sendslaveready(self)
|
||||||
self.notify("testnodeready", self)
|
self.notify("testnodeready", self)
|
||||||
elif eventname == "slavefinished":
|
elif eventname == "slavefinished":
|
||||||
self._down = True
|
self._down = True
|
||||||
|
|
|
@ -26,6 +26,7 @@ class TerminalReporter:
|
||||||
file = py.std.sys.stdout
|
file = py.std.sys.stdout
|
||||||
self._tw = py.io.TerminalWriter(file)
|
self._tw = py.io.TerminalWriter(file)
|
||||||
self.currentfspath = None
|
self.currentfspath = None
|
||||||
|
self.gateway2info = {}
|
||||||
|
|
||||||
def write_fspath_result(self, fspath, res):
|
def write_fspath_result(self, fspath, res):
|
||||||
if fspath != self.currentfspath:
|
if fspath != self.currentfspath:
|
||||||
|
@ -96,10 +97,12 @@ class TerminalReporter:
|
||||||
else:
|
else:
|
||||||
d['extra'] = ""
|
d['extra'] = ""
|
||||||
d['cwd'] = rinfo.cwd
|
d['cwd'] = rinfo.cwd
|
||||||
self.write_line("%(id)s %(spec)s -- platform %(platform)s, "
|
infoline = ("%(id)s %(spec)s -- platform %(platform)s, "
|
||||||
"Python %(version)s "
|
"Python %(version)s "
|
||||||
"cwd: %(cwd)s"
|
"cwd: %(cwd)s"
|
||||||
"%(extra)s" % d)
|
"%(extra)s" % d)
|
||||||
|
self.write_line(infoline)
|
||||||
|
self.gateway2info[gateway] = infoline
|
||||||
|
|
||||||
def pyevent_gwmanage_rsyncstart(self, source, gateways):
|
def pyevent_gwmanage_rsyncstart(self, source, gateways):
|
||||||
targets = ", ".join([gw.id for gw in gateways])
|
targets = ", ".join([gw.id for gw in gateways])
|
||||||
|
@ -122,7 +125,6 @@ class TerminalReporter:
|
||||||
|
|
||||||
def pyevent_testnodeready(self, node):
|
def pyevent_testnodeready(self, node):
|
||||||
self.write_line("%s node ready to receive tests" %(node.gateway.id,))
|
self.write_line("%s node ready to receive tests" %(node.gateway.id,))
|
||||||
|
|
||||||
|
|
||||||
def pyevent_testnodedown(self, node, error):
|
def pyevent_testnodedown(self, node, error):
|
||||||
if error:
|
if error:
|
||||||
|
@ -244,7 +246,11 @@ class TerminalReporter:
|
||||||
if 'failed' in self.stats and self.config.option.tbstyle != "no":
|
if 'failed' in self.stats and self.config.option.tbstyle != "no":
|
||||||
self.write_sep("=", "FAILURES")
|
self.write_sep("=", "FAILURES")
|
||||||
for ev in self.stats['failed']:
|
for ev in self.stats['failed']:
|
||||||
self.write_sep("_")
|
self.write_sep("_", "FAILURES")
|
||||||
|
if hasattr(ev, 'node'):
|
||||||
|
self.write_line(self.gateway2info.get(
|
||||||
|
ev.node.gateway, "node %r (platinfo not found? strange)")
|
||||||
|
[:self._tw.fullwidth-1])
|
||||||
ev.toterminal(self._tw)
|
ev.toterminal(self._tw)
|
||||||
|
|
||||||
def summary_stats(self):
|
def summary_stats(self):
|
||||||
|
@ -344,15 +350,6 @@ from py.__.test import event
|
||||||
from py.__.test.runner import basic_run_report
|
from py.__.test.runner import basic_run_report
|
||||||
|
|
||||||
class TestTerminal:
|
class TestTerminal:
|
||||||
@py.test.mark.xfail
|
|
||||||
def test_testnodeready(self, testdir, linecomp):
|
|
||||||
item = testdir.getitem("def test_func(): pass")
|
|
||||||
rep = TerminalReporter(item.config, linecomp.stringio)
|
|
||||||
XXX # rep.pyevent_testnodeready(maketestnodeready())
|
|
||||||
linecomp.assert_contains_lines([
|
|
||||||
"*INPROCESS* %s %s - Python %s" %(sys.platform,
|
|
||||||
sys.executable, repr_pythonversion(sys.version_info))
|
|
||||||
])
|
|
||||||
|
|
||||||
def test_pass_skip_fail(self, testdir, linecomp):
|
def test_pass_skip_fail(self, testdir, linecomp):
|
||||||
modcol = testdir.getmodulecol("""
|
modcol = testdir.getmodulecol("""
|
||||||
|
|
|
@ -400,6 +400,27 @@ class TestDistribution:
|
||||||
])
|
])
|
||||||
assert dest.join(subdir.basename).check(dir=1)
|
assert dest.join(subdir.basename).check(dir=1)
|
||||||
|
|
||||||
|
def test_dist_each(self, testdir):
|
||||||
|
interpreters = []
|
||||||
|
for name in ("python2.4", "python2.5"):
|
||||||
|
interp = py.path.local.sysfind(name)
|
||||||
|
if interp is None:
|
||||||
|
py.test.skip("%s not found" % name)
|
||||||
|
interpreters.append(interp)
|
||||||
|
|
||||||
|
testdir.makepyfile(__init__="", test_one="""
|
||||||
|
import sys
|
||||||
|
def test_hello():
|
||||||
|
print "%s...%s" % sys.version_info[:2]
|
||||||
|
assert 0
|
||||||
|
""")
|
||||||
|
args = ["--dist=each"]
|
||||||
|
args += ["--tx", "popen//python=%s" % interpreters[0]]
|
||||||
|
args += ["--tx", "popen//python=%s" % interpreters[1]]
|
||||||
|
result = testdir.runpytest(*args)
|
||||||
|
result.stdout.fnmatch_lines(["2...4"])
|
||||||
|
result.stdout.fnmatch_lines(["2...5"])
|
||||||
|
|
||||||
|
|
||||||
class TestInteractive:
|
class TestInteractive:
|
||||||
def getspawn(self, tmpdir):
|
def getspawn(self, tmpdir):
|
||||||
|
@ -454,28 +475,6 @@ class TestInteractive:
|
||||||
child.expect("MODIFIED.*test_simple_looponfail_interaction.py", timeout=4.0)
|
child.expect("MODIFIED.*test_simple_looponfail_interaction.py", timeout=4.0)
|
||||||
child.expect("1 passed", timeout=5.0)
|
child.expect("1 passed", timeout=5.0)
|
||||||
child.kill(15)
|
child.kill(15)
|
||||||
|
|
||||||
@py.test.mark.xfail("need new cmdline option")
|
|
||||||
def test_dist_each(self, testdir):
|
|
||||||
for name in ("python2.4", "python2.5"):
|
|
||||||
if not py.path.local.sysfind(name):
|
|
||||||
py.test.skip("%s not found" % name)
|
|
||||||
testdir.makepyfile(__init__="", test_one="""
|
|
||||||
import sys
|
|
||||||
def test_hello():
|
|
||||||
print sys.version_info[:2]
|
|
||||||
assert 0
|
|
||||||
""")
|
|
||||||
result = testdir.runpytest("--dist-each",
|
|
||||||
"--tx=popen//python2.4",
|
|
||||||
"--tx=popen//python2.5",
|
|
||||||
)
|
|
||||||
assert result.ret == 1
|
|
||||||
result.stdout.fnmatch_lines([
|
|
||||||
"*popen-python2.5*FAIL*",
|
|
||||||
"*popen-python2.4*FAIL*",
|
|
||||||
"*2 failed*"
|
|
||||||
])
|
|
||||||
|
|
||||||
class TestKeyboardInterrupt:
|
class TestKeyboardInterrupt:
|
||||||
def test_raised_in_testfunction(self, testdir):
|
def test_raised_in_testfunction(self, testdir):
|
||||||
|
|
Loading…
Reference in New Issue