[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:
fijal 2007-02-10 17:50:47 +01:00
parent 275673ba21
commit 1739ba4f55
7 changed files with 58 additions and 114 deletions

View File

@ -20,12 +20,19 @@ class RunExecutor(object):
self.config = config
assert self.config
def run(self):
self.item.run()
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)

View File

@ -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):

View File

@ -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):

View File

@ -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)
return ex.execute()
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()

View File

@ -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
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

View File

@ -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()

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, 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)")