test_ok1/py/test/terminal/remote.py

142 lines
5.0 KiB
Python

from __future__ import generators
import py
from py.__.test.session import Session
from py.__.test.terminal.out import getout
from py.__.test.outcome import Failed, Passed, Skipped
def checkpyfilechange(rootdir, statcache={}):
""" wait until project files are changed. """
def fil(p):
return p.ext in ('.py', '.c', '.h')
#fil = lambda x: x.check(fnmatch='*.py')
def rec(p):
return p.check(dotfile=0)
changed = False
for path in rootdir.visit(fil, rec):
oldstat = statcache.get(path, None)
try:
statcache[path] = curstat = path.stat()
except py.error.ENOENT:
if oldstat:
del statcache[path]
print "# WARN: race condition on", path
else:
if oldstat:
if oldstat.mtime != curstat.mtime or \
oldstat.size != curstat.size:
changed = True
print "# MODIFIED", path
else:
changed = True
return changed
def getfailureitems(failures):
l = []
for rootpath, names in failures:
root = py.path.local(rootpath)
if root.check(dir=1):
current = py.test.collect.Directory(root).Directory(root)
elif root.check(file=1):
current = py.test.collect.Module(root).Module(root)
# root is fspath of names[0] -> pop names[0]
# slicing works with empty lists
names = names[1:]
while names:
name = names.pop(0)
try:
current = current.join(name)
except NameError:
print "WARNING: could not find %s on %r" %(name, current)
break
else:
l.append(current)
return l
class RemoteTerminalSession(Session):
def __init__(self, config, file=None):
super(RemoteTerminalSession, self).__init__(config=config)
self._setexecutable()
if file is None:
file = py.std.sys.stdout
self._file = file
self.out = getout(file)
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 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(4.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 _initslavegateway(self):
print "* opening PopenGateway: ", self.executable
topdir = self.config.topdir
return py.execnet.PopenGateway(self.executable), topdir
def run_remote_session(self, failures):
gw, topdir = self._initslavegateway()
channel = gw.remote_exec("""
from py.__.test.terminal.remote import slaverun_TerminalSession
slaverun_TerminalSession(channel)
""", stdout=self.out, stderr=self.out)
try:
print "MASTER: initiated slave terminal session ->"
repr = self.config._makerepr(conftestnames=[])
channel.send((str(topdir), repr, failures))
print "MASTER: send start info, topdir=%s" % (topdir,)
try:
return channel.receive()
except channel.RemoteError, e:
print "*" * 70
print "ERROR while waiting for proper slave startup"
print "*" * 70
print e
return []
finally:
gw.exit()
def slaverun_TerminalSession(channel):
""" we run this on the other side. """
print "SLAVE: initializing ..."
topdir, repr, failures = channel.receive()
print "SLAVE: received configuration, using topdir:", topdir
config = py.test.config
config._initdirect(topdir, repr, failures)
config.option.session = None
config.option.looponfailing = False
config.option.usepdb = False
config.option.executable = None
if failures:
config.option.verbose = True
session = config.initsession()
session.shouldclose = channel.isclosed
print "SLAVE: starting session.main()"
session.main()
failures = session.getitemoutcomepairs(Failed)
failures = [config.get_collector_trail(item) for item,_ in failures]
channel.send(failures)