[svn r38397] Kill PidInfo and make boxing optional for distributed testing.
This should make dist testing on windows possible. --HG-- branch : trunk
This commit is contained in:
parent
275673ba21
commit
1739ba4f55
|
@ -20,12 +20,19 @@ class RunExecutor(object):
|
|||
self.config = config
|
||||
assert self.config
|
||||
|
||||
def run(self):
|
||||
def run(self, capture=True):
|
||||
if capture:
|
||||
try:
|
||||
self.item.startcapture()
|
||||
self.item.run()
|
||||
finally:
|
||||
self.item.finishcapture()
|
||||
else:
|
||||
self.item.run()
|
||||
|
||||
def execute(self):
|
||||
def execute(self, capture=True):
|
||||
try:
|
||||
self.run()
|
||||
self.run(capture)
|
||||
outcome = Outcome()
|
||||
except Skipped, e:
|
||||
outcome = Outcome(skipped=str(e))
|
||||
|
@ -49,8 +56,7 @@ class RunExecutor(object):
|
|||
# XXX hmm, we probably will not like to continue from that
|
||||
# point
|
||||
raise SystemExit()
|
||||
outcome.stdout = ""
|
||||
outcome.stderr = ""
|
||||
outcome.stdout, outcome.stderr = self.item._getouterr()
|
||||
return outcome
|
||||
|
||||
class ApigenExecutor(RunExecutor):
|
||||
|
@ -68,7 +74,7 @@ class ApigenExecutor(RunExecutor):
|
|||
finally:
|
||||
self.tracer.end_tracing()
|
||||
|
||||
def run(self):
|
||||
def run(self, capture):
|
||||
""" We want to trace *only* function objects here. Unsure
|
||||
what to do with custom collectors at all
|
||||
"""
|
||||
|
@ -83,7 +89,7 @@ class BoxExecutor(RunExecutor):
|
|||
|
||||
def execute(self):
|
||||
def fun():
|
||||
outcome = RunExecutor.execute(self)
|
||||
outcome = RunExecutor.execute(self, False)
|
||||
return outcome.make_repr(self.config.option.tbstyle)
|
||||
b = Box(fun, config=self.config)
|
||||
pid = b.run()
|
||||
|
@ -105,7 +111,7 @@ class AsyncExecutor(RunExecutor):
|
|||
|
||||
def execute(self):
|
||||
def fun():
|
||||
outcome = RunExecutor.execute(self)
|
||||
outcome = RunExecutor.execute(self, False)
|
||||
return outcome.make_repr(self.config.option.tbstyle)
|
||||
|
||||
b = Box(fun, config=self.config)
|
||||
|
|
|
@ -8,29 +8,14 @@ from py.__.test.rsession.executor import BoxExecutor, RunExecutor,\
|
|||
from py.__.test.rsession import repevent
|
||||
from py.__.test.rsession.outcome import ReprOutcome
|
||||
|
||||
# XXX copied from session.py
|
||||
def startcapture(session):
|
||||
if not session.config.option.nocapture:
|
||||
session._capture = py.io.StdCapture()
|
||||
|
||||
def finishcapture(session):
|
||||
if hasattr(session, '_capture'):
|
||||
capture = session._capture
|
||||
del session._capture
|
||||
return capture.reset()
|
||||
return "", ""
|
||||
|
||||
def box_runner(item, session, reporter):
|
||||
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, config=session.config)
|
||||
outcome = r.execute()
|
||||
outcome = ReprOutcome(outcome.make_repr(session.config.option.tbstyle))
|
||||
outcome.stdout, outcome.stderr = finishcapture(session)
|
||||
return outcome
|
||||
|
||||
def benchmark_runner(item, session, reporter):
|
||||
|
|
|
@ -18,6 +18,8 @@ class Outcome(object):
|
|||
self.excinfo = excinfo
|
||||
self.is_critical = is_critical
|
||||
self.signal = 0
|
||||
self.stdout = "" # XXX temporary
|
||||
self.stderr = ""
|
||||
assert bool(self.passed) + bool(excinfo) + bool(skipped) == 1
|
||||
|
||||
def make_excinfo_repr(self, tbstyle):
|
||||
|
@ -54,7 +56,7 @@ class Outcome(object):
|
|||
def make_repr(self, tbstyle="long"):
|
||||
return (self.passed, self.setupfailure,
|
||||
self.make_excinfo_repr(tbstyle),
|
||||
self.skipped, self.is_critical, 0, "", "")
|
||||
self.skipped, self.is_critical, 0, self.stdout, self.stderr)
|
||||
|
||||
class TracebackEntryRepr(object):
|
||||
def __init__(self, tbentry):
|
||||
|
|
|
@ -9,59 +9,16 @@ from py.__.test.outcome import Skipped
|
|||
import thread
|
||||
import os
|
||||
|
||||
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, config, pidinfo, executor=AsyncExecutor):
|
||||
def __init__(self, config, executor):
|
||||
#self.rootcollector = rootcollector
|
||||
self.config = config
|
||||
self.executor = executor
|
||||
self.pidinfo = pidinfo
|
||||
|
||||
def execute(self, itemspec):
|
||||
item = self.config._getcollector(itemspec)
|
||||
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()
|
||||
return cont(self.pidinfo.waitandclear)
|
||||
|
||||
def run(self, itemspec):
|
||||
#outcome = self.execute(itemspec)
|
||||
|
@ -72,7 +29,7 @@ class SlaveNode(object):
|
|||
else:
|
||||
return outcome.make_repr(self.config.option.tbstyle)
|
||||
|
||||
def slave_main(receive, send, path, config, pidinfo):
|
||||
def slave_main(receive, send, path, config):
|
||||
import os
|
||||
assert os.path.exists(path)
|
||||
path = os.path.abspath(path)
|
||||
|
@ -82,7 +39,11 @@ def slave_main(receive, send, path, config, pidinfo):
|
|||
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(config, pidinfo)
|
||||
if config.option.boxed:
|
||||
executor = BoxExecutor
|
||||
else:
|
||||
executor = RunExecutor
|
||||
node = nodes[item[0]] = SlaveNode(config, executor)
|
||||
return node
|
||||
while 1:
|
||||
nextitem = receive()
|
||||
|
@ -120,15 +81,6 @@ def setup_slave(host, config):
|
|||
return channel
|
||||
|
||||
def setup():
|
||||
def callback_gen(channel, queue, info):
|
||||
def callback(item):
|
||||
if item == 42: # magic call-cleanup
|
||||
# XXX should kill a pid here
|
||||
info.kill()
|
||||
channel.close()
|
||||
sys.exit(0)
|
||||
queue.put(item)
|
||||
return callback
|
||||
# our current dir is the topdir
|
||||
import os, sys
|
||||
basedir = channel.receive()
|
||||
|
@ -139,13 +91,13 @@ def setup():
|
|||
config = py.test.config
|
||||
assert not config._initialized
|
||||
config.initdirect(basedir, config_repr)
|
||||
if hasattr(os, 'nice'):
|
||||
nice_level = config.getvalue('dist_nicelevel')
|
||||
os.nice(nice_level)
|
||||
if not config.option.nomagic:
|
||||
py.magic.invoke(assertion=1)
|
||||
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)
|
||||
from py.__.test.rsession.slave import slave_main
|
||||
slave_main(channel.receive, channel.send, basedir, config)
|
||||
if not config.option.nomagic:
|
||||
py.magic.revoke(assertion=1)
|
||||
channel.close()
|
||||
|
|
|
@ -11,46 +11,60 @@ def setup_module(mod):
|
|||
if py.std.sys.platform == "win32":
|
||||
py.test.skip("skipping executor tests (some require os.fork)")
|
||||
|
||||
class ItemTestPassing(py.test.Item):
|
||||
class Item(py.test.Item):
|
||||
def __init__(self, name, config):
|
||||
super(Item, self).__init__(name)
|
||||
self._config = config
|
||||
|
||||
class ItemTestPassing(Item):
|
||||
def run(self):
|
||||
return None
|
||||
|
||||
class ItemTestFailing(py.test.Item):
|
||||
class ItemTestFailing(Item):
|
||||
def run(self):
|
||||
assert 0 == 1
|
||||
|
||||
class ItemTestSkipping(py.test.Item):
|
||||
class ItemTestSkipping(Item):
|
||||
def run(self):
|
||||
py.test.skip("hello")
|
||||
|
||||
class ItemTestPrinting(Item):
|
||||
def run(self):
|
||||
print "hello"
|
||||
|
||||
class TestExecutor(BasicRsessionTest):
|
||||
def test_run_executor(self):
|
||||
ex = RunExecutor(ItemTestPassing("pass"), config=self.config)
|
||||
ex = RunExecutor(ItemTestPassing("pass", self.config), config=self.config)
|
||||
outcome = ex.execute()
|
||||
assert outcome.passed
|
||||
|
||||
ex = RunExecutor(ItemTestFailing("fail"), config=self.config)
|
||||
ex = RunExecutor(ItemTestFailing("fail", self.config), config=self.config)
|
||||
outcome = ex.execute()
|
||||
assert not outcome.passed
|
||||
|
||||
ex = RunExecutor(ItemTestSkipping("skip"), config=self.config)
|
||||
ex = RunExecutor(ItemTestSkipping("skip", self.config), config=self.config)
|
||||
outcome = ex.execute()
|
||||
assert outcome.skipped
|
||||
assert not outcome.passed
|
||||
assert not outcome.excinfo
|
||||
|
||||
def test_run_executor_capture(self):
|
||||
ex = RunExecutor(ItemTestPrinting("print", self.config), config=self.config)
|
||||
outcome = ex.execute()
|
||||
assert outcome.stdout == "hello\n"
|
||||
|
||||
def test_box_executor(self):
|
||||
ex = BoxExecutor(ItemTestPassing("pass"), config=self.config)
|
||||
ex = BoxExecutor(ItemTestPassing("pass", self.config), config=self.config)
|
||||
outcome_repr = ex.execute()
|
||||
outcome = ReprOutcome(outcome_repr)
|
||||
assert outcome.passed
|
||||
|
||||
ex = BoxExecutor(ItemTestFailing("fail"), config=self.config)
|
||||
ex = BoxExecutor(ItemTestFailing("fail", self.config), config=self.config)
|
||||
outcome_repr = ex.execute()
|
||||
outcome = ReprOutcome(outcome_repr)
|
||||
assert not outcome.passed
|
||||
|
||||
ex = BoxExecutor(ItemTestSkipping("skip"), config=self.config)
|
||||
ex = BoxExecutor(ItemTestSkipping("skip", self.config), config=self.config)
|
||||
outcome_repr = ex.execute()
|
||||
outcome = ReprOutcome(outcome_repr)
|
||||
assert outcome.skipped
|
||||
|
|
|
@ -137,6 +137,7 @@ class TestSlave:
|
|||
return self.config.get_collector_trail(item)
|
||||
|
||||
def test_slave_setup(self):
|
||||
py.test.skip("Doesn't work anymore")
|
||||
pkgname = self.pkgpath.basename
|
||||
host = HostInfo("localhost:%s" %(self.tmpdir,))
|
||||
host.initgateway()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
""" Testing the slave side node code (in a local way). """
|
||||
from py.__.test.rsession.slave import SlaveNode, slave_main, setup, PidInfo
|
||||
from py.__.test.rsession.slave import SlaveNode, slave_main, setup
|
||||
from py.__.test.rsession.outcome import ReprOutcome
|
||||
import py, sys
|
||||
from py.__.test.rsession.testing.basetest import BasicRsessionTest
|
||||
|
@ -17,8 +17,7 @@ from py.__.test.rsession.executor import RunExecutor
|
|||
|
||||
class TestSlave(BasicRsessionTest):
|
||||
def gettestnode(self):
|
||||
pidinfo = PidInfo()
|
||||
node = SlaveNode(self.config, pidinfo, executor=RunExecutor)
|
||||
node = SlaveNode(self.config, executor=RunExecutor)
|
||||
return node
|
||||
|
||||
def test_slave_run_passing(self):
|
||||
|
@ -74,18 +73,3 @@ class TestSlave(BasicRsessionTest):
|
|||
node = self.gettestnode()
|
||||
node.run(self.rootcol._getitembynames("py doc log.txt".split()).
|
||||
_get_collector_trail())
|
||||
|
||||
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)")
|
||||
|
|
Loading…
Reference in New Issue